c/c++面试题(6)运算符重载详解

1.操作符函数:

在特定条件下,编译器有能力把一个由操作数和操作符共同组成的表达式,解释为对

一个全局或成员函数的调用,该全局或成员函数被称为操作符函数.该全局或成员函数

被称为操作符函数.通过定义操作符函数,可以实现针对自定义类型的运算法则,并使之

与内置类型一样参与各种表达式运算.

2.首先我们先介绍下左值和右值,因为我们在运用运算符的时候要尽量和内置类型的一致性.

左值:有名的可以直接取地址的我们称之为左值,左值的特性是可以修改的.

右值:右值主要是一些临时变量,匿名变量,字符串字面值常量,字符常量;

表达式有的是左值有的是右值,例如+-*/运算返回的是右值,而赋值运算,复合赋值运算

返回的是左值.前++返回的是左值,后++返回的是右值.

类型转换(无论是显示的还是隐式的)都伴随着临时变量的产生.函数的返回值当返回的

不是引用的时候也是一个右值,但是一个引用的时候返回的是一个左值.

下面给出一个经典的全面左值和右值的示例:

#include <iostream>
int g = 0;
using namespace std;
int foo(void)
{
    return g;
}

int& bar(void)
{
    return g;
}

void func(int& i)
{
    cout << "func1" << endl;
}
void func(const int& i)
{
    cout << "func2" << endl;
}

int main(void)
{
    int i;  //左值,有名字可以取地址.
    int* p = &i;
    i = 10; //可修改
    //p = &10;这里的10是个右值,上面10赋值给i的时候产生的一个临时变量,是右值.所以10++也是错误的.

    // foo() = 10; 这里返回值也是临时变量是右值,是只读的属性,不能修改;++foo();也是错误的.

    bar() = 10; //这里返回的是一个左值引用,所以它是可以赋值的
    cout << g << endl;
    int a = 10,b = 20,c;
    c = a + b;//a + b是右值.
    //a + b = 10;是非法的,因为表达式的值也是用一个临时变量来存储的.是右值.
    //无论是显示的还是隐式的类型转换,都会产生临时变量.
    (a = b) = 10; //赋值表达式返回的是左值.
    ++a = 1000;   //前++表达式返回的是左值.
    //a++ = 2000;  这里是不行的,后++运算符的表达式的值是右值.
    //a++++;这也是不行的,对右值做++是不行的.
    ++++a;//这个是可以的.++a的返回值还是a本身.
    cout << a << endl; //1000
    char ch = 0;
    i = ch; //这里是可以的.
    //int& r = ch;它会先把ch转换成一个临时变量,然后把这个临时;变量赋值给r,而引用是不能引用一个临时变量的.
    //非常引用只能引用一个左值,不能是右值.
    const int& r = ch;  //常左引用又叫万能引用,它可以引用左值和右值.
    func(ch);//这里它会调用参数是常属性的函数,因为它要类型转换,类型转换产生临时变量.
    //所谓类型转换,无论是显示还是隐式的.都不是改变了原来的变量的类型,只能产生一个临时变量.
    return 0;
}

3.操作符重载的一般规则

a.C++不允许用户自己定义新的运算符,只能对已经存在的操作符进行重载.

b.C++大部分的运算符都可以重载,但是有一部分运算符是不能重载的主要有下面几种

.成员访问运算符; ::作用域解析运算符;.* 成员指针访问运算符;sizeof运算符;三目运算符;

.成员访问运算符和.*成员指针访问运算符不能重载的原因是为了保证成员访问的功能不能被改变;

::和sizeof运算符操作的对象是类型而不是一般的变量和表达式,不具备重载的特征.

c.重载不能改变操作的对象操作数的个数;

d.重载不能改变运算符的优先级;

e.重载函数的参数不能有默认的缺省参数值,因为它会改变运算符的操作数和前面的规则矛盾;

f.重载的参数不能全部都是C++的基本类型,因为这样会改变原有的用于标准的运算符的性质.

g.应当尽量使自定义的重载操作符和系统用于标准类型的运算符具有相似的功能;

h.运算符重载可以是类的成员函数,还可以是类的友元函数,还可以是普通的全局函数;

4.运算类双目操作符:+ - * /等   a.左右操作数均可以左值或右值;

b.表达式的值为右值.

c.成员函数形式:

class LEFT

{

const RESULT operator#(const RIGHT& right)const {}

};

全局函数的形式:

const RESULT operator#(const LEFT& left,const RIGHT& right){...}

#include <iostream>
using namespace std;
class Complex
{
public:
    Complex(int r = 0,int i = 0):m_r(r),m_i(i){}
    //运算符+-/*表达式的结果是右值,不能是引用.
    /*参数用常引用的好处:
      即避免了拷贝构造的开销,它又能接收常右操作数
      且可以保证右操作数不被修改.
      第三个const是可以支持常属性的左操作数.
      因为非常对象依然可以调用常函数.
      第一const为了追求语义上的一致性,使其返回值是一个
      临时变量.不能被赋值.
    */
    /*从做到右的三个const依次表示:
      1.第一个const:返回常对象,禁止对加号表达式进行赋值.
      2.第二个const:支持常右操作数,并且可以避免其被修改.
      3.第三个const:支持常左操作数.
     */
    const Complex operator+(const Complex& c) const
    {
        return Complex(m_r + c.m_r,m_i + c.m_i);
    }
    const Complex operator*(const Complex& c) const
    {
        return Complex(m_r*c.m_r,m_i*c.m_i);
    }
    const Complex operator/(const Complex& c) const
    {
        return Complex(m_r/c.m_r,m_i/c.m_i);
    }
    void print(void) const
    {
        cout << m_r << ‘+‘ << m_i << "i" << endl;
    }
    /*用全局函数来实现(友元)
    const Complex operator-(const Complex& c) const
    {
        return Complex(m_r - c.m_r,m_i - c.m_i);
    }
    */
private:
    int m_r;
    int m_i;
    friend const Complex operator-(const Complex&,const Complex&);
};
const Complex operator-(const Complex& left,
        const Complex& right)//这里没有const了,因为
    //const是修饰this指针的,而全局函数是没有this指针的;
{
    return Complex(left.m_r-right.m_r,left.m_i - right.m_i);
}
int main(void)
{
    Complex c1(1,2),c2(3,4);
    Complex c3 = c1 + c2;
    //编译器翻译成Complex c3 = c1.operator+(c2);
    c3.print();
    Complex c4 = c1 + c2 + c3;
    //Complex c4 = c1.operator+(c2).operator+(c3);
    c4.print();
    //(c1 + c2) = c3;如果加号运算符没有第一个const这里编译会通过.
    c4 = c3 - c1;
    //c4 = ::operator-(c3,c1);也可以处理成这样
    c4.print();
    c4 = c1*c2;
    c4.print();
    c4 = c2/c1;
    c4.print();
    return 0;
}

5.赋值类操作运算符:=,+=,-=,*=,/=,^=,&=,|=等

a.左操作数必须是左值,右操作数可以是左值或者右值.

b.表达式的值为左值,返回自引用.是操作数本身,而非副本.

成员函数形式:

class LEFT

{

LEFT& operator#(const RIGHT& right){...}

}

全局函数形式:

LEFT& operator#(LEFT& left,const RIGHT& right){...}

#include <iostream>
using namespace std;
class Complex
{
public:
    Complex(int r = 0,int i = 0):m_r(r),m_i(i){}
    void print(void)const
    {
        cout << m_r << ‘+‘ << m_i << ‘i‘ << endl;
    }
    //这里不能是const函数,因为左操作数要改变的.
    Complex& operator+=(const Complex& c) //这里不能带const
    {
        m_r += c.m_r;
        m_i += c.m_i;
        return *this;
    }
    friend Complex& operator-=(Complex& left,const Complex& right)//友元函数没有this指针,也就没有常函数之说.
    {
        left.m_r -= right.m_r;
        left.m_i -= right.m_i;
        return left;
    }
    Complex& operator*=(const Complex& c)
    {
        m_r *= c.m_r;
        m_i *= c.m_i;
        return *this;
    }
    Complex& operator/=(const Complex& c)
    {
        m_r /= c.m_r;
        m_i /= c.m_i;
    }
private:
    int m_r;
    int m_i;
};
int main(void)
{
    Complex c1(1,2),c2(3,4),c3(5,6);
    (c1 += c2) = c3;
    //c1.operator+=(c2).operator=(c3);
    c1.print();
    c3 -= c1;
    c3.print();
    //::operator-=(c3,c1);
    c3 *= c1;
    c3.print();
    c3 /= c1;
    c3.print();
    return 0;
}

6.运算类单目操作符:-,~,!等

a.操作数为左值或右值.

b.表达式的值为右值.

const RESULT operator#(void)const{...}

const RESULT operator#(const OPERAND& operand){...}

#include <iostream>
using namespace std;
class Integer
{
public:
    Integer(int i = 0):m_i(i){}
    void print(void) const
    {
        cout << m_i << endl;
    }
    const Integer operator-(void) const
    {
        return Integer(-m_i);
    }
    friend const Integer operator~(const Integer& i)
    {
        return Integer(i.m_i * i.m_i);
    }
    const Integer operator!(void) const
    {
        return m_i?Integer(0):Integer(1);
    }
private:
    int m_i;
};
int main(void)
{
    Integer i(100);
    Integer j = -i;
    //Integer j = i.operator-();
    j.print();
    j = ~i;
    //j = ::operator~(i)求其平方.用~完成一个平方的效果.
    j.print();
    j = !i;   //取反,表示真假之间的转换.
    j.print();
    return 0;
}

7.前++前--后++后--运算符:

a.前自增减单目操作符:操作数为左值,表达式的值为左值,且为操作数本身(而非副本)

成员函数形式:

OPERAND& operator#(void){...}

全局函数形式:

OPERAND& operator#(OPERAND& operand){...}

b.后自增减单目操作符:操作数为左值,表达式的值为右值,且为自增减以前的值.

成员函数形式:

const OPERAND operator#(int){...}

全局函数形式:

const OPERAND operator#(OPERAND& operand,int){...}

8.输出操作符:<<

a.左操作数为左值形式的输出流(ostream)对象里面的成员,右操作数为左值或右值.

b.表达式的值为左值,且为左操作数本身(而非副本)

c.左操作数的类型为ostream,若以成员函数重载该操作符,就应该将其定义为ostream类的

成员,该类为标准库提供,无法添加新的成员,因此只能以全局函数的形式重载该操作符

ostream& operator<<(ostream& os,const RIGHT& right){...}

输入操作符:>>

a.左操作数为左值形式的输入流(istream)对象,右操作数为左值.

b.表达式的值为左值,且为左操作数本身(而非副本)

c.左操作数的类型为istream,若以成员函数形式重载该操作符,就应该将其定义为istream类的成员,

该类为标准库提供,无法添加新的成员,因此只能以全局函数的形式重载该操作符

istream& operator>>(istream& is,RIGHT& right){...}

#include <iostream>
using namespace std;
class Complex
{
public:
    Complex(int r = 0,int i = 0):m_r(r),m_i(i){}
    friend ostream& operator<<(ostream& os,const Complex& c)
    {
        return os << c.m_r << ‘+‘ << c.m_i << ‘i‘;
    }
    friend istream& operator>>(istream& is,Complex& c)
    {
        return is >> c.m_r >> c.m_i;
    }
private:
    int m_r;
    int m_i;
};
int main(void)
{
    Complex c1(12,23),c2(2,4);
    cout << c1 << ", " << c2 << endl;
    cout << "Please input :" << endl;
    cin >> c1 >> c2;
    cout << c1 << ", " << c2 << endl;
    return 0;
}

9.下标运算符:[]

a.常用于在容器类型中以下标方式获取数据元素.

b.非常容器元素为左值,常容器的元素为右值.一个是非const成员,一个是const成员.并且必须定义为成员函数.

#include <iostream>
using namespace std;
class Array
{
public:
    Array(size_t size):m_data(new int[size]),m_size(size){}
    ~Array(void)
    {
        if(m_data != NULL)
            delete[] m_data;
        m_data = NULL;
    }
    int& operator[] (size_t i)
    {
        return *(m_data + i);
    }
    //常版本
    const int& operator[] (size_t i) const
    {
        return const_cast<Array&>(*this)[i];
    }
private:
    int*   m_data;
    size_t m_size;
};
int main(void)
{
    Array a(10);
    a[0] = 13; //a.operator[](0) = 13;
    a[2] = 26;
    a[9] = 58;
    cout << a[0] << ‘ ‘ << a[2] << ‘ ‘ << a[9] << endl;
    const Array& r = a;
    cout << r[0] << ‘ ‘ << r[2] << ‘ ‘ << endl;
    return 0;
}

10.小括号函数操作符()

a.如果一个类重载了函数操作符,那么该类的对象就可以被做函数来调用,其参数和返回值就是函数

操作符函数的参数和返回值.

b.参数的个数,类型以及返回值的类型,没有限制

c.唯一可以带有缺省参数的操作符函数

#include <iostream>
using namespace std;
class Square
{
public:
    double operator()(double x) const
    {
        return x * x;
    }
    int operator()(int a,int b,int c = 9) const
    {
        return a + b - c;
    }
};
int main(void)
{
    Square square;
    cout << square(13.) << endl;
    //cout << square.operator()(13.) << endl;
    cout << square(10,40,20) << endl;
    cout << square(10,40) << endl;
    return 0;
}

11.解引用和间接成员访问操作符:* 以及 ->

a.如果一个类重载了解引用和间接成员访问操作符,那么该类的对象就可以被当作指针来使用.

c/c++面试题(6)运算符重载详解

时间: 2024-11-05 15:55:10

c/c++面试题(6)运算符重载详解的相关文章

运算符重载详解

1.运算符重载定义: C++中预定义的运算符的操作对象只能是基本数据类型.但实际上,对于许多用户自定义类型(例如类),也需要类似的运算操作.这时就必须在C++中重新定义这些运算符,赋予已有运算符新的功能,使它能够用于特定类型执行特定的操作.运算符重载的实质是函数重载,它提供了C++的可扩展性,也是C++最吸引人的特性之一. 运算符重载是通过创建运算符函数实现的,运算符函数定义了重载的运算符将要进行的操作.运算符函数的定义与其他函数的定义类似,惟一的区别是运算符函数的函数名是由关键字operato

Python 3 之 运算符重载详解

基础知识 实际上,"运算符重载"只是意味着在类方法中拦截内置的操作--当类的实例出现在内置操作中,Python自动调用你的方法,并且你的方法的返回值变成了相应操作的结果.以下是对重载的关键概念的复习: 运算符重载让类拦截常规的Python运算. 类可重载所有Python表达式运算符 类可以重载打印.函数调用.属性点号运算等内置运算 重载使类实例的行为像内置类型. 重载是通过特殊名称的类方法来实现的. 换句话说,当类中提供了某个特殊名称的方法,在该类的实例出现在它们相关的表达式时,Pyt

C++运算符重载详解

1.什么是运算符重载 运算符重载是一种函数重载. 运算符函数的格式:operatorop(argument-list)例如,operator+()重载+运算符.其中的op,必须是有效的C++运算符,如[email protected]()会报错,因为C++中没有@运算符. 2.重载运算符的使用 如下例所示: class Test { public: Test operator+(Test &test); } 调用运算符函数的方式有两种:Test t1;Test t2;1)普通函数调用Test t

C++对bool operator &lt; (const p &amp;a)const的运算符重载详解

struct node { //定义一个结构体node(节点) int x; int y; int len; //node中有3个成员变量x,y,len bool operator <(const node &a)const {//重载<操作符.可以对两个node使用<操作符进行比较 return len<a.len; } }; 括号中的const表示参数a对象不会被修改,最后的const表明调用函数对象不会被修改! 重载运算符的介绍 C++中预定义的运算符的操作对象只能是

三元运算符用法详解

三元运算符用法详解: 此运算符是一种非常常用的元素符号,如果使用得当可以省却不少代码.三元运算符也可以称作为条件运算符,可以说是if...else语句的一种简化形式,下面就结合实例简单介绍一下如何实现次运算符,先看一下运算符的语法结构: 表达式一?表达式二:表达式三 三元运算符之所以称作三元,就是因为具有三个操作数.当表达式一的返回值为true的时候,则返回表达式二的值,否则返回表达式三的值.下面看一段代码实例: <!DOCTYPE HTML> <html> <head>

MySQL笔记之运算符使用详解

运算符包括四类,分别是:算数运算符.比较运算符.逻辑运算符和位运算符 Mysql可以通过运算符来对表中数据进行运算,比如通过出生日期求年龄等 运算符包括四类,分别是:算数运算符.比较运算符.逻辑运算符和位运算符 算数运算符加.减.乘运算 代码如下: mysql> select a,a+5,a*2 from t1; +------+------+------+ | a    | a+5  | a*2  | +------+------+------+ |   24 |   29 |   48 |

面试题:JavaIO流分类详解与常用流用法实例

Java流概念: Java把所有的有序数据都抽象成流模型,简化了输入输出,理解了流模型就理解了Java IO.可以把流想象成水流,里面的水滴有序的朝某一方向流动.水滴就是数据,且代表着最小的数据流动单位,在字节流中,水滴就是一字节(byte),在字符流中,水滴就是一字符(char). Java流的分类方法大致分为以下几种: 1.按流向划分,分为输入流.输出流 请注意,这里的流向是以程序的运行时内存为参照的. 输入流类名中包含关键字InputStream或Reader,输出流类名中包含关键字Out

C++ 运算符重载的注意点

运算符重载包括:双目运算符重载.单目运算符重载.流运算符重载.转换构造函数.类型转换函数 重载运算符的函数一般格式如下: 函数类型 operator 运算符名称 (形参表列) { // 对运算符的重载处理 } 1. 双目运算符重载 双目:有2个操作数的运算符 重载函数可以为类成员函数或者是类的友元函数: - 当是成员函数时,有一个隐藏参数(当前类),因此只有一个显示参数:如 Complex operator+(const Complex &b); Complex operator+(const

C++中重载函数详解

函数的重载详解 什么时函数重载: 函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数.重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处. 1.是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数“个数” 或 “类型” 或 “顺序”)必须不同,常用来处理实现功能类似数据类型不同的问题(这也是C++与C语言的最重要区别) 1 int Ad