一个类默认会创建4个函数:默认构造、拷贝构造、析构、和operator=函数。最后一个就是赋值运算符重载,可以进行简单的值传递。
注意:这个是值传递。问题就在这;还有一种传递叫:引用传递。几乎所有语言(C\C++\C#等)都有引用传递。
一旦涉及引用传递,那么指针的指向问题(指针重新指向新地址之后,原地址导致内存泄漏)就必须认真考虑。
如下代码:
#include<iostream>
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。结果程序崩了。
问题就出在,之前提到的指针转向时,重复释放上。此时为了实现自定义类型的安全赋值。需要重载一下赋值运算符。
/// <summary>
///
/// </summary>
/// <param name="p2">为了避免修改传入值选择const引用</param>
/// <returns>为了链式编程(连续等于a = b = c)返回本体引用。</returns>
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;
}
此时程序不再崩。
总结:
指针转向时,一定要注意两个问题:
- 原空间内存释放。
- 指针指向的地址,存在二次释放的风险,所以需要深拷贝。在堆中重新开辟一块空间,将指向的内容传入进去。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)