1. 运算符重载
掌握运算符最重要的是了解每个运算符重载时它的返回值类型、参数类型以及是否需要参数、需要几个参数。
概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
注意:
-
对于内置的数据类型,编译器知道如何进行运算
-
对于内置的数据类型的表达式的运算符是不可能改变的
-
不要滥用运算符重载
加法运算符、减法运算符较简单,此处并不列出!
2. 左移运算符重载
作用:可以输出自定义数据类型
注意:只能利用全局函数重载左移运算符(不会利用成员函数重载左移运算符,因为无法实现cout
在左侧)
示例:
template<class T>
ostream& operator<<(ostream& cout,T& t){
//代码段
return cout;
}
值得注意的是左移运算符的重载是要使用全局函数的,因为要使用到输出流ostream
,而且该函数需要有返回值且返回值类型也为输出流ostream
,这样才能实现链式编程。
也可以将该函数作为友元来方便访问类中私有属性。
重载左移运算符配合友元可以实现输出自定义数据类型。
3. 递增运算符重载
递增运算符分为前置递增和后置递增
-
前置递增:
++i
,输出时先自增,后输出值 -
后置递增:
i++
,输出时先输出值,后自增
注意:在递增前可以选择先重载左移运算符方便输出
- 在前置递增重载时,
MyInteger& operator++()
,函数的返回值为自己定义的类类型的引用,原因是:前置递增在自增后,返回*this
,如果返回值类型不是引用,那么在链式编程中得到的并不是原来的对象,而是其它对象。且该函数不需要参数。 - 在后置递增重载时,
MyInteger operator++(int)
,函数的返回值为值类型,并不是引用,这是因为在函数体中,我们首先定义了一个临时变量记录*this
,而自增后我们返回的是这个临时变量,因为后置递增是先输出值,后自增,即返回的是自增前的*this
。如果返回的是引用,得到的是临时变量,导致错误。其中参数中int
是占位参数,可以用于区分前置和后置递增。 - 注意以下代码中,左移运算符重载函数中第二个参数类型为值类型,这是为了适应两种自增。
#include<iostream>
#include<string>
using namespace std;
class MyInteger {
friend ostream& operator<<(ostream& cout, MyInteger myint);
public:
MyInteger();
//重载前置递增
MyInteger& operator++() { //注意这里返回的是引用
this->myInt++;
return *this;
}
//重载后置递增
MyInteger operator++(int) { //int代表占位参数,可以用于区分前置和后置递增,注意这里返回的是值
MyInteger temp = *this;
this->myInt++;
return temp;
}
private:
int myInt;
};
MyInteger::MyInteger() {
this->myInt = 0;
}
ostream& operator<<(ostream& cout, MyInteger myint) {
cout << "myint:" << myint.myInt << endl;
return cout;
}
int main()
{
MyInteger myint;
cout << ++myint << endl;
cout << myint++ << endl;
}
综上:推荐编程中多使用前置递增
4. 赋值运算符重载
c++编译器至少给一个类添加4个函数
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝
- 赋值运算符
operator=
,对属性进行值拷贝
参考深拷贝与浅拷贝的内容,赋值运算也会出现堆区内存重复释放导致程序崩溃,解决方法是重新申请一块堆上的空间
注意:以下代码中,在进行赋值运算符重载时,重载函数的返回值是类类型的引用,是为了实现链式编程,举个例子就是:a = b = c
。在函数体内部,先判断成员变量是否为空指针,如果非空,则进行delete
操作,再重新开辟堆空间并赋值,最后返回对象本身。
class Person {
public:
Person(int a) {
this->m_A = new int(a);
}
Person& operator=(Person& p) {
if (this->m_A != NULL) {
delete this->m_A;
this->m_A = NULL;
}
this->m_A = new int(*p.m_A);
return *this;
}
int* m_A;
};
5. 关系运算符重载
作用:可以让两个自定义类型对象进行对比操作(==、<、>、<=、>=、!=…)
6. 函数调用运算符重载
- 函数调用运算符
()
也可以重载 - 由于重载后使用的方式非常像函数的调用,因此称为仿函数
- 仿函数没有固定写法,非常灵活
class myPrint {
public:
myPrint(string text) {
this->text = text;
}
void operator()(string str) {
cout << str << endl;
}
string text;
};
匿名函数对象
在下面代码中,MyAdd()(100, 100)
会创建一个匿名函数对象,当前行代码执行完毕即释放该对象
示例:
class MyAdd {
public:
int operator()(int num1, int num2) {
return num1 + num2;
}
};
int main()
{
MyAdd myadd;
int ret = myadd(10,10);
cout << "ret = " << ret << endl;
cout << MyAdd()(100, 100) << endl; //匿名函数对象,当前行执行完立即释放对象
}