1.赋值运算符重载
c++至少给一个类添加四种函数
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝
- 赋值运算符operator=,对属性进行值拷贝
如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝的问题
为什么用赋值运算符重载:因为编译器提供的是浅拷贝,会出现内存重复释放的问题,所以需要我们自己创建重载,用深拷贝解决
代码:
- 使用了new指令在堆区开辟内存,而堆区的内存要由自己写的析构函数来释放掉。所以属性必须是一个指针变量
- 不赋值重载的p2 = p1,这两个使用的浅拷贝,赋值后值相同且地址相同,所以析构函数释放内存时会产生重复释放的错误
- 赋值重载函数的形参必须是引用的形式,因为如果不是引用的形式,新开辟的地址会在赋值重载函数结束时被释放掉。在test函数结束时还要再释放一次内存,重复释放。而用引用的形式,用的同一地址,只是用的别名。最后只触发一次析构函数释放内存。(理解可能错误,注意修改)
- 注意:p2 = p1,调用赋值重载函数的是p2
- 在赋值重载时应先判断调用重载函数的对象本身是否还有属性在堆区,若有,要先释放干净,再赋值。
- 因为可能多个对象连续赋值,所以赋值重载函数返回值必须是对象本身,即*this,而且类型必须是person &,以保证返回的是本体,不然返回的是一个浅拷贝的不同地址的相同值
class person
{
public:
person(int age)
{
m_age = new int(age); //在堆区开辟内存,赋予这地址一个整型的值,将地址存到指针变量中
}
int *m_age; //因为new返回的是一个地址,所以创建一个指针变量
~person() //堆区的内存要由析构函数来释放
{
if (m_age != NULL)
{
delete m_age;
m_age = NULL;
}
}
//将=重载为深拷贝,解决浅拷贝的重复释放内存的问题
person& operator=(person &p) //这里必须是&引用,因为如果不是&而是新开辟复制值的新地址,则将赋值右边p1的值传入
//p1的属性m_Age的新地址会在赋值重载函数结束时由析构函数释放掉。之后p1虽然因为赋值重载函数有了相同值不同地址
//但最后test函数运行完析构函数运行的时候,p1还是会出现重复释放内存的问题
//主要是因为堆区内存必须手动释放,而函数结束析构函数就运作一次,所以新地址被释放两次
{
//应该先判断是否有属性在堆区,先释放干净,再做深拷贝。因为p2 = p1是p2在调用赋值这个歌函数,注意这个调用的对象
if (m_age != NULL) //所以这里清除的是p2的属性,是要被赋值的那个对象的属性。可以理解为this->m_age
{
delete m_age;
m_age = NULL;
}
m_age = new int(*p.m_age); //注意加*号,因为m_age是指针常量
return *this; //返回一个真正的自身的值。理解:p调用函数被赋值后返回自身的地址,为下一个链式赋值做准备
//而这里必须用person& operator=,引用的形式,不然返回的是一个浅拷贝的不同地址的相同值,不是自身
}
};
void test()
{
person p1(17);
person p2(23);
person p3(33);
p2 = p1; //注意这里是p2调用赋值这个函数
//这里的拷贝是浅拷贝,使得p1和p2指向同一个地址,而类中的析构函数释放内存时,也会同时释放两次内存,会崩溃
//所以必须用深拷贝来解决浅拷贝带来的问题
p2 = p1 = p3;//如果返回值为空,p1 = p3后返回一个空,不能实现,所以一次赋值后要返回一个自身的值,如p1,再赋值
cout << *p2.m_age << endl;
}
int main()
{
test();
system("pause");
return 0;
}
2.重载关系运算符
让两个自定义数据类型比对
!=运算符自己写
class person
{
public:
person(int age,string name)
{
m_age = age;
m_name = name;
}
int m_age;
string m_name;
//重载 == 运算符
bool operator==(person &p) //if 主要是真假,用bool
{
if (this->m_age == p.m_age && this->m_name == p.m_name)
{
return true;
}
return false;
}
};
void test()
{
person p1(15, "阿斯顿");
person p2(15, "阿斯顿");
person p3(78, "老费");
if (p3 == p2) //这里是p1在调用==这个函数
{
cout << "相等" << endl;
}
else
{
cout << "不等 " << endl;
}
}
int main()
{
test();
system("pause");
return 0;
}
3.函数调用运算符重载
()也可以重载
重载后被称为仿函数
写法灵活
注意其中的匿名函数对象
//打印输出的类
class person
{
public:
//调用重载函数
void operator()(string name)
{
cout << "调用重载函数 " << name << endl;
}
};
//加法类
class add
{
public:
int operator()(int a, int b)
{
return (a + b);
}
};
void name(string name)
{
cout << "一般函数 " << name << endl;
}
void test()
{
person p;
p("团长"); //由于使用时非常像函数调用,所以被称为仿函数
name("团长");
add a; //哪个类重载了调用,就用哪个类的对象
int ret = a(1, 2);
cout << "加法类结果 " << ret << endl;
//匿名函数对象
cout << add()(100, 200) << endl; //上面那个实例化了一个对象a,而这个没有名字,用完就释放
}
int main()
{
test();
system("pause");
return 0;
}