问题描述
在使用vector或者list等容器push_back一个对象时,会对象进行拷贝。当对象有成员是使用堆内存(即需要new和delete的指针成员)且对象没有对应的深拷贝操作,则对象的指针成员释放内存后,再使用成员,就会有数据异常问题。
代码展示
//深浅拷贝测试
#include <list>
namespace PointerCopy
{
#if 0
//浅拷贝
class DataClass
{
public:
DataClass() = default;
~DataClass()
{
}
public:
char* pc = nullptr;
int len = 0;
};
#else
//深拷贝
class DataClass
{
public:
DataClass(){}
DataClass(const DataClass& rhs)
{
if (rhs.len != 0)
{
pc = new char[rhs.len];
strcpy(pc, rhs.pc);
len = rhs.len;
}
}
~DataClass()
{
if (nullptr != pc)
delete[] pc;
}
//参考《C++ primer 第五版》 453-454页
DataClass& operator=(const DataClass& rhs)
{
if (this == &rhs) //如果是拷贝自身,则直接返回
return *this;
char * temp = new char[rhs.len];
strcpy(temp, rhs.pc);//先拷贝原始的
if (pc != nullptr)
delete[] pc; //释放旧的内存,前面先拷贝是避免自身复制
pc = temp;
len = rhs.len;
return *this;
}
public:
char* pc = nullptr;
int len = 0;
};
#endif
void Run()
{
std::list<DataClass> obj;
auto func = [](std::list<DataClass>& plst){
DataClass temp;
temp.pc = new char[15];
strcpy(temp.pc, "wingkin");
temp.len = sizeof("wingkin");
plst.push_back(temp);
DataClass temp2;
temp2 = temp; //为了测试进入拷贝赋值函数
delete[] temp.pc; //new给pc的地址值还在,但是内容无法确定
temp.pc = nullptr; //必须赋值nullptr,否则析构会再次delete
};
func(obj);
for each (auto a in obj)
{
cout << "data=" << a.pc << ", len=" << a.len << endl;
}
}
}
int main()
{
PointerCopy::Run();
system("pause");
return 0;
}
测试结果
1.深拷贝运行结果
字符串内容正常输出。
2.浅拷贝运行结果
由于浅拷贝没有对指针成员分配新的地址,因此push_back(temp)后,list中的DataClass对象中pc和temp的pc指向同一个地址,当执行完delete后,内存数据会变得不确定。导致输出字符串时乱码。
总结
当对象(类对象和结构体对象)有指针成员时,拷贝构造函数和拷贝赋值函数一定要记得进行深拷贝操作。