一个类默认会创建4个函数:默认构造、拷贝构造、析构、和operator=函数。最后一个就是赋值运算符重载,可以进行简单的值传递。
注意:这个是值传递。问题就在这;还有一种传递叫:引用传递。几乎所有语言(C\C++\C#等)都有引用传递。
一旦涉及引用传递,那么指针的指向问题(指针重新指向新地址之后,原地址导致内存泄漏)就必须认真考虑。
如下代码:
#include
using namespace std;class Person1
{
public:Person1(int age){this->mAge = age;}public:int mAge;
};void test01()
{Person1 p1(10);Person1 p2(20);p2 = p1;cout << p2.mAge << endl; // 10
}int main()
{test01();return EXIT_SUCCESS;
}
我们自定义类型对象可以直接调用赋值运算符。完成值传递。这没什么问题。
现在再来看下面的代码。
class Person2
{
public:Person2(const char* name){this->pName = new char[strlen(name) + 1];strcpy(this->pName, name);}char* pName;
};void test02()
{Person2 p1("Tom");Person2 p2("Jack");p2 = p1;cout << p2.pName << endl; // Tom
}
程序也忠实的完成了自己的任务,
赋值运算符将指针的指向地址,作为一个值,传递了过去。
- 释放时,重复释放。
- 指向是转了,但是原指向的内存没有搭理。
指针在转向时:一定要注意这两个安全问题。
但是我们在程序中调用的new,就必须对这个堆中指针进行释放。需要加上我们的析构函数。
class Person2
{
public:Person2(const char* name){this->pName = new char[strlen(name) + 1];strcpy(this->pName, name);}~Person2(){if (this->pName != NULL){delete this->pName;this->pName = NULL;}}char* pName;
};
这个析构函数,我们必须加上,因为前面我们用的new。结果程序崩了。
问题就出在,之前提到的指针转向时,重复释放上。此时为了实现自定义类型的安全赋值。需要重载一下赋值运算符。
///
///
///
/// 为了避免修改传入值选择const引用
/// 为了链式编程(连续等于a = b = c)返回本体引用。
Person2& operator=(const Person2& p2)
{// 指针转向前,先释放掉原来的空间。if (this->pName != NULL){delete this->pName;this->pName = NULL;}this->pName = new char[strlen(p2.pName) + 1];strcpy(this->pName, p2.pName);return *this;
}
此时程序不再崩。
指针转向时,一定要注意两个问题: