目录
一、运算符重载
1. 运算符重载是什么?
2. 为什么要运算符重载?
3. 怎么进行运算符重载?
a. 运算符重载函数的格式
b. 参数的个数和要重载的运算符操作数相同
c. 运算符重载函数必须有一个类类型参数
4. 注意事项
二、 赋值运算符重载
1. 默认赋值运算符重载函数
2. 赋值运算符只能在类中重载
3. 在类中显示重载赋值运算符
4. 两边必须是已经存在的对象才会调用赋值运算符重载函数
一、运算符重载
1. 运算符重载是什么?
运算符重载是一种在编程中扩展或改变已有运算符功能的技术。简单来说,运算符重载就是对已有的运算符重新进行定义,让运算符能够处理特定类型的对象。
2. 为什么要运算符重载?
如果想要让两个对象比较大小该怎么办?有没有办法直接使用运算符>, <来比较两个对象的大小?
class A { public: A(int a) { num = a; } int max(A b) { return num > b.num; } int operator> (A b) { return num > b.num; } private: int num; }; int main() { A a(10); A b(20); //如果不使用运算符重载,只能编写函数来比较两个对象的大小 cout << a.max(b) << endl; //使用运算符重载 cout << (a > b) << endl; return 0; }
运算符重载的目的是为了让自定义类型能够使用运算符。
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,其目的就是让自定义类型可以像内置类型一样可以直接使用运算符进行操作。
3. 怎么进行运算符重载?
a. 运算符重载函数的格式
运算符重载函数的函数名是operator加要重载的运算符,有返回值和参数,参数的个数和要重载的运算符操作数相同,且必须有一个类类型参数。
格式:返回值类型 operator操作符 (参数列表) 运算符重载函数函数可以写在类内,也可以在类外: class A { public: A(int a) { num = a; } // 类内的运算符重载函数 // 参数的个数和要重载的运算符操作数相同(类的成员函数参数隐藏了一个this指针。) int operator> (A b) { return num > b.num; } private: int num; }; class B { public: B(int a) { num = a; } private: int num; }; // 类外的运算符重载函数 // 参数的个数和要重载的运算符操作数相同 int operator> (A a, B b) { return 1; } int operator> (A a, int b) { return 0; }
b. 参数的个数和要重载的运算符操作数相同
要注意的是类的成员函数参数隐藏了一个this指针,所以在类内重载运算符时,参数个数要减一。
在类内重载>时参数太少:
在类内重载>时参数太多:
c. 运算符重载函数必须有一个类类型参数
类外的运算符重载函数必须显示的传入一个类类型参数。
类内的运算符重载函数,是类的成员函数,所以编译器会传入一个this指针,这相当于已经有了一个类类型参数,所以可以不写类类型参数。
4. 注意事项
- operator后面不能连接非运算符:比如operator@
- 重载操作符必须有一个类类型参数
- 运算符重载函数作为类成员函数时,它的第一个参数为隐藏的this指针,所以对于只需要两个参数的运算符,我们在类中重载时传一个参数即可。
- 逗号运算符 ::(域作用限定符) sizeof运算符 ?:(三目运算符) 成员函数运算符(.和->) 以上5个运算符不能重载。这个经常在笔试选择题中出现。
- 无法在类外重载=
二、 赋值运算符重载
1. 默认赋值运算符重载函数
赋值运算符重载函数的特殊在于:只能在类中定义,用户在类中没有显式实现时,编译器会生成一个默认赋值运算符重载函数,以值的方式逐字节拷贝。注意:默认赋值运算符重载函数里内置类型成员变量是直接赋值的,而自定义类型成员变量会调用对应类的赋值运算符重载函数完成赋值:
默认赋值运算符重载函数会调用自定义类型成员变量对应类的赋值运算符重载函数完成赋值:
// operator=(=的重载)是类的默认成员函数。即使你不写,编译器也会自动生成的,实现一个对象对另一个对象的浅拷贝。 // 自定义类型成员变量会调用对应类的赋值运算符重载函数完成赋值。 class A { public: A& operator= (const A& a) { cout << "A的赋值运算符重载函数被调用" << endl; num = a.num; return *this; } private: int num; }; class B{ private: A a; }; int main() { B b, c; c = b; return 0; }
2. 赋值运算符只能在类中重载
原因:赋值运算符如果不在类中显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
3. 在类中显示重载赋值运算符
一个标准的赋值运算符重载函数应该做到以下几点:
- 参数类型:const T&,传递引用可以提高传参效率
- 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值。
- 有条件判断,能检测是否自己给自己赋值
- 返回 *this :为了支持连续赋值。
一个标准的 =重载: class A { public: // 参数类型:const T&,传递引用可以提高传参效率。 // 返回值使用传引用返回,这样可以减少使用拷贝构造函数。 A& operator= (const A& a) { // 有条件判断,能检测是否自己给自己赋值. if(this != &a) { num = a.num; } // this是对象的指针,加*解引用返回对象本身,这样能支持连续赋值。 return *this; } private: int num; };
4. 两边必须是已经存在的对象才会调用赋值运算符重载函数
=两边是两个已经存在的对象才是赋值,才会调用赋值运算符。
在以下在例子中的虽然是用=,但仍是调用拷贝构造: class A { public: A() {} A(const A& a) { cout << "A的拷贝构造函数被调用" << endl; } A& operator= (const A& a) { cout << "A的赋值运算符重载函数被调用" << endl; num = a.num; return *this; } private: int num; }; int main() { A b; A c = b; return 0; }
对象b存在,对象c不存在:
对象b,c都存在才是赋值,才会调用赋值运算符重载函数:
------------------------END-------------------------
才疏学浅,谬误难免,欢迎各位批评指正。