C++学习(12)—— 运算符重载
运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
1.加号运算符重载
作用:实现两个自定义数据类型相加的运算
#include <iostream>
#include <string>
using namespace std;
//加号运算符重载
class Person{
public:
//1.成员函数重载+号
Person operator+(Person &p){
Person temp;
temp.m_a = this->m_a + p.m_a;
temp.m_b = this->m_b + p.m_b;
return temp;
}
int m_a;
int m_b;
};
//2.全局函数重载+号
Person operator+(Person &p1,Person &p2){
Person temp;
temp.m_a = p1.m_a + p2.m_a;
temp.m_b = p1.m_b + p2.m_b;
return temp;
}
//3.函数重载的版本
Person operator+(Person &p1,int num){
Person temp;
temp.m_a = p1.m_a + num;
temp.m_b = p1.m_b + num;
return temp;
}
void test01(){
Person p1;
p1.m_a = 10;
p1.m_b = 10;
Person p2;
p2.m_a = 10;
p2.m_b = 10;
//成员函数重载的本质调用
//Person p3 = p1.operator+(p2);
//全局函数重载的本质调用
//Person p3 = operator+(p1,p2);
Person p3 = p1 + p2;
//运算符重载也可以发生函数重载
Person p4 = p3 + 100;
cout << "p3.m_a = " << p3.m_a <<endl;
cout << "p3.m_b = " << p3.m_b <<endl;
cout << "p4.m_a = " << p4.m_a <<endl;
cout << "p4.m_b = " << p4.m_b <<endl;
}
int main(){
test01();
return 0;
}
总结1:对于内置的数据类型的表达式的运算符是不可能改变的
总结2:不要滥用运算符重载
2.左移运算符重载
作用:可以自输出自定义数据类型
#include <iostream>
#include <string>
using namespace std;
//左移运算符重载
class Person{
public:
//1.利用成员函数重载左移运算符 p.operator<<(cout) 简化版本p<<cout
//通常不会利用成员函数重载左移运算符,因为无法实现cout在左侧
int m_a;
int m_b;
};
//2.利用全局函数重载左移运算符
ostream & operator<<(ostream &cout,Person &p){
cout << "m_a = " << p.m_a << " m_b = " << p.m_b << endl;
return cout;
//必须返回引用,因为只能存在一个,不能开辟新的空间
}
void test01(){
Person p;
p.m_a = 10;
p.m_b = 10;
cout << "p.m_a = " << p.m_a <<endl;
cout << "p.m_b = " << p.m_b <<endl;
cout << p << endl;
}
int main(){
test01();
return 0;
}
-
通常不会利用成员函数重载左移运算符,因为无法实现cout在左侧
利用成员函数重载左移运算符 p.operator<<(cout) 简化版本p<<cout
-
左移运算符重载必须返回ostream引用,因为只能存在一个,不能开辟新的空间
3. 递增运算符重载
作用:通过重载递增运算符,实现自己的整型数据
注意:前置返回的是变量的引用,后置返回的是常量。所以++++c合法,而c++++不合法
#include <iostream>
#include <string>
using namespace std;
//重载递增运算符
//自定义整型
class MyInterger{
friend ostream & operator<<(ostream &cout, MyInterger myint);
public:
MyInterger(){
m_num = 0;
}
//1.重载前置++运算符
//返回引用是为了一直对一个数据进行递增操作
MyInterger & operator++(){
m_num = m_num+1;
return *this; //返回自身
}
//2.重载后置++运算符
//int代表一个占位参数,可以用于区分前置和后置递增
//后置递增一定不能返回引用,因为temp为局部变量,函数运行完后自动释放
MyInterger operator++(int){
//先 记录当时结果
MyInterger temp = *this;
//后 递增
m_num = m_num+1;
//最后 返回所记录结果
return temp;
}
private:
int m_num;
};
//重载左移运算符
ostream & operator<<(ostream &cout, MyInterger myint){
cout << myint.m_num;
return cout;
}
void test01(){
MyInterger myint;
cout << ++myint << endl;
}
void test02(){
MyInterger myint;
cout << myint++ << endl;
cout << myint << endl;
}
int main(){
test02();
return 0;
}
总结:前置递增返回的是引用,后置递增返回的是值
4.赋值运算符重载
c++编译器至少给一个类添加4个函数
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性值进行拷贝
- 赋值运算符operator=,对属性值进行拷贝
如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题
#include<iostream>
using namespace std;
class Person {
public:
Person(int age) {
m_age = new int(age);
}
~Person() {
if (m_age != NULL) {
delete m_age;
m_age = NULL;
}
}
//重载赋值运算符
Person& operator=(Person &p) {
//编译器提供的浅拷贝
//m_age = p.m_age
//应该先判断是否有属性在堆区,如果有先释放干净,然后再深拷贝
if (m_age != NULL) {
delete m_age;
m_age = NULL;
}
m_age = new int(*p.m_age);
return *this;
}
int *m_age;
};
void test01() {
Person p1(18);
Person p2(20);
Person p3(30);
p3 = p2 = p1; //赋值操作
cout << "p1的年龄为:" << *p1.m_age << endl;
cout << "p2的年龄为:" << *p2.m_age << endl;
cout << "p3的年龄为:" << *p3.m_age << endl;
}
int main() {
test01();
system("pause");
return 0;
}
5.关系运算符重载
作用:重载关系运算符,可以让两个自定义类型对象进行对比操作
#include<iostream>
#include<string>
using namespace std;
class Person {
public:
Person(string name, int age) {
m_name = name;
m_age = age;
}
bool operator==(Person &p) {
if (this->m_name == p.m_name && this->m_age == p.m_age) {
return true;
}
return false;
}
bool operator==(Person &p) {
if (this->m_name == p.m_name && this->m_age == p.m_age) {
return false;
}
return true;
}
string m_name;
int m_age;
};
void test01() {
Person p1("xia",18);
Person p2("xia", 18);
if (p1 == p2) {
cout << "p1 == p2" << endl;
}
else {
cout << "p1 != p2" << endl;
}
}
int main() {
test01();
return 0;
}
6.函数调用运算符重载
- 函数调用运算符()也可以重载
- 由于重载后使用的方式非常像函数的调用,因此称为仿函数
- 仿函数没有固定写法,非常灵活
#include<iostream>
#include<string>
using namespace std;
//函数调用运算符的重载
//打印输出类
class MyPrint {
public:
//重载函数调用运算符
void operator()(string test) {
cout << test << endl;
}
};
class MyAdd {
public:
int operator()(int num1, int num2) {
return num1 + num2;
}
};
void MyPrint02(string test) {
cout << test << endl;
}
void test01() {
MyPrint myPrint;
myPrint("hello world"); //由于使用起来非常类似于函数调用,因此称为仿函数
MyPrint02("hello world");
}
void test02() {
MyAdd myadd;
int ret = myadd(100, 100);
cout << "ret = " << ret << endl;
//匿名函数对象
cout << MyAdd()(100, 100) << endl;
}
int main() {
test01();
test02();
return 0;
}
Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.