运算符重载
概念:对已有运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
目录
一、加号运算符重载
分类:
①成员函数重载+号
②全局函数重载+号
二、左移运算符重载
作用:以输出自定义数据类型
三、递增运算符重载
前置递增
后置递增
总结:前置递增返回引用,后置递增返回值
四、赋值运算符重载
前提: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;
}