运算符重载
目录
一、加号运算符重载
分类:
①成员函数重载+号
②全局函数重载+号
二、左移运算符重载
作用:以输出自定义数据类型
三、递增运算符重载
前置递增
后置递增
总结:前置递增返回引用,后置递增返回值
四、赋值运算符重载
前提:C++编译器至少给1个类添加4个函数
五、关系运算符重载
作用:重载关系运算符,使2个自定义对象进行对比
六、函数调用运算符重载函数调用运算符()也可以重载
一、加号运算符重载
“+”号一般代表2个数字之间相加,是内置数据类型
而加号运算符重载代表2个自定义数据类型之间相加
分类:
①成员函数重载+号=首先创建代码,对象n1,n2,n3。尝试用n1+n2=n3;
class number
{
public:int m_a;int m_b;
};
void test01()
{number n1;n1.m_a = 10;n1.m_b = 10;number n2;n2.m_a = 5;n2.m_b = 6;number n3;n3 = n1 + n2;
}
接下来,我们在类中创建加号运算符重载函数operator+
class number
{
public:number operator+(number& n) // 加号运算符重载函数{number tmp;tmp.m_a = this->m_a + n.m_a;tmp.m_b = this->m_b + n.m_b;return tmp;}int m_a;int m_b;
};
实际上number n3 = p1.operator+(p2);;等价于 number n3 = n1 + n2;
成功相加
②全局函数重载+号
同样的,只不过是在类外实现,而且参数是2个对象
number operator+(number& n1, number& n2)
{number tmp;tmp.m_a = n1.m_a + n2.m_a;tmp.m_b = n1.m_b + n2.m_b;return tmp;
}
实际上number n3 = operator+(p1,p2);等价于 number n3 = n1 + n2;
同时,运算符重载也可以发生函数重载
而且,内置数据类型不可以修改
二、左移运算符重载
作用:以输出自定义数据类型
首先创建一个person类与测试函数test01,类中有属性m_a/m_b;
class person
{
public:int m_a;int m_b;
};void test01()
{person p;p.m_a = 10;p.m_b = 5;cout << p.m_a << endl;
}
如果只是想 输出m_a或m_b,可以直接输出
不过,如果想直接输出p,则不行
接下来,就可以使用operator函数重载左移运算符<<
不过首先,如果我们想要使用成员函数重载<<运算符,这么写
这样的话,实际调用效果是这样:
p.operator(p)
而如果参数改成cout:
实际调用效果是这样:
p.operator(cout) == p<<cout ,out在右侧
并非我们要的 cout<<p
因此,我们要使用全局函数重载
void operator<<(ostream &cout, person &p)
{cout << p.m_a << p.m_b;
}
这样实际调用:operator << (cout,p) == cout << p;
成功输出
不过,如果我们想换行
因为<<返回的是void,不可再输出其他内容。
因此,我们要使它仍然返回cout,修改operator函数
ostream& operator<<(ostream& cout, person& p)
{cout << p.m_a << p.m_b;return cout;
}
这样就可以实现换行
甚至还可以随便输出其他内容
而此时,如果我们把属性设为私有,函数将无法访问
这样就可以使用上节课学到的友元来解决
class person
{friend ostream& operator<<(ostream& cout, person& p);friend void test01();
private:int m_a;int m_b;
};
三、递增运算符重载
分为2种:前置递增与后置递增
而且是对我们自定义数据类型进行递增,并非内置数据类型
前置递增
首先创建myInteger类,并创建初始化函数myInteger
这样,在cout时就需要自定义<<左移运算符
class myInteger
{
public:myInteger(){m_a = 5;}int m_a;
};
ostream& operator<<(ostream& cout, myInteger myint) // 自定义<<左移运算符重载
{cout << myint.m_a << myint.m_b;return cout;
}
void test01()
{myInteger myint;cout << ++myint << endl;
}
此时提示没有对应的++运算符
接下来实现前置++
myInteger& operator++(){m_a++; // 先自增return *this; // 再返回自身}
令 数据自增后返回即可,同时注意要返回自身,返回值要引用&
如果返回值不加引用
myInteger operator++(){m_a++; // 先自增return *this; // 再返回自身}
会导致每次返回的是新的对象,不是原来接收的数据本身
因此要加上&
后置递增
思路差不多,只是需要先保存值,再递增,最后输出保存的值
注意,只要在参数列表里写个int,编译器就会认为是后置递增了,而这个int是占位参数
myInteger operator++(int) // int 占位参数{myInteger tmp = *this;m_a++; // 先自增return tmp; // 再返回自身}
注意返回值不能写引用&,因为返回的是tmp,是局部变量,函数执行完就被销毁了,引用会返回它自身但是已经被销毁了,相当于非法访问
因此,也不能连续递增(myint++)++
总结:前置递增返回引用,后置递增返回值
而递减与递增基本相同,只需有把++改成--即可
四、赋值运算符重载
前提:C++编译器至少给1个类添加4个函数
①默认构造函数(无参,空实现,函数体为空)
②默认析构函数(无参,空实现,函数体为空)
③默认拷贝构造函数(对属性进行值拷贝)
④赋值运算符operator=(对属性进行值拷贝)
当值拷贝时,也会出现堆区内存重复释放的问题
首先,创建person类和测试函数test01
class person
{
public:person(int age){m_age = new int(age); // 堆区开辟空间存储} int *m_age;
};void test01()
{person p1(10);cout << *p1.m_age << endl;person p2(12);cout << *p2.m_age << endl;p2 = p1;cout << *p2.m_age << endl;
}
正常输出
p2=p1是将p1所有属性浅拷贝给p2
而此时,由于我们上面new出的堆区数据需要我们手动释放,因此补全析构函数
~person(){if (m_age != NULL){delete m_age;m_age = NULL;}}
再次运行
因为p1与p2指向同一块m_age,delete一次后又进行delete,造成非法访问
这就是因为浅拷贝造成的堆区内存重复释放的问题
因此,我们赋值时,使用深拷贝,同时要先判断对象是否有属性在堆区,有则释放
void operator=(person& p1) // 复制运算符的重载{if (m_age != NULL) // 判断是否有属性在堆区,有则释放{delete m_age;m_age = NULL;}m_age = new int(*p1.m_age);}
不会再崩溃
但是,如果想连续赋值
因为函数返回值是void,所以不能实现,因此修改返回值为person&,而且必须是引用才是返回自身,否则返回一个新的对象
person& operator=(person& p1) // 复制运算符的重载,返回引用才是返回自身{if (m_age != NULL) // 判断是否有属性在堆区,有则释放{delete m_age;m_age = NULL;}m_age = new int(*p1.m_age);return *this;}
成功实现
五、关系运算符重载
作用:重载关系运算符,使2个自定义对象进行对比
而内置数据类型,int等编译器知道如何比对
class person
{
public:person(string name, int age){m_name = name;m_age = age;}
private:string m_name;int m_age;
};
void test01()
{person p1("joyce", 22);person p2("tatina", 22);if (p1 == p2){cout << "相等" << endl;}else{cout << "不等" << endl;}
}
目前是不行的
接下来实现对比函数
bool operator==(person& p){if (this->m_age == p.m_age && this->m_name == p.m_name){return true;}return false;}
如果要实现不等关系(!=)判断,把==改为!=,判断条件反过来就行
六、函数调用运算符重载
函数调用运算符()也可以重载
实现一个 打印 类
class func
{
public:void operator()(string line) // 打印操作{cout << line << endl;}string line;
};void test01()
{func fun;fun("hello,nana");
}
接下来实现一个同样作用的函数
void print(string line)
{cout << line << endl;
}
正常输出,而由于使用非常类似于函数调用,因此又称为仿函数
实现一个 加法 类
class my_add
{
public:int operator()(int x, int y){return x + y;}
};
void test01()
{my_add myadd;int ret = myadd(100, 200);cout << ret << endl;
}
因此,仿函数非常灵活
同时,加上匿名函数对象
void test01()
{my_add myadd;int ret = myadd(100, 200);cout << ret << endl;// 匿名函数对象cout << my_add()(200, 300) << endl;
}