#include <vector>
using namespace std;
class CDemo{
public:
CDemo():str(NULL){}
~CDemo(){if(str) delete [] str;}
char *str;
};
int main()
{
CDemo d1;
d1.str = new char[32];
strcpy(d1.str, "trend micro");
vector <CDemo> *a1 = new vector <CDemo>();
a1 -> push_back(d1);
delete a1;
return 0;
}
书上说这段程序的错误是vector对象指针能够自动析构,所以不需要调用delete a1,
否则会造成两次析构。
总结一下
1. vector <CDemo> *a1 = new vector <CDemo>(); a1是new出来的,所以必须要手工delete.这是对a1本身而言,而与a1内存储的数据无关。
2. a1 -> push_back(d1); 这部操作比较复杂,因为你的vector是存储类,而不是类指针。所以首先会在栈上创建d1的一个拷贝d1_1,压入栈,作为参数传递给push_back。然后在push_back中,创建d1_1的拷贝d1_2,d1_2是存储在a1管理的内存中。然后push_back return,d1_1出栈,调用d1_1的析构。
3. delete a1; a1中存有d1_2,所以会删除d1_2,自然会调用d1_2的析构函数。
4. 在main中return 0, d1被自动删除,此时调用d1的析构函数。
5. 因为class CDemo没有拷贝构造函数,所以创建拷贝时只是简单的把新对象中每个成员变量的值设置成与原来的对象相等。相当于运行memcpy。这时问题就来了,因为你的一个成员是char *str; 这样d1,d1_1,d1_2的str都是指向同一个地址。所以只有第一次调用CDemo的析构函数时能运行正确,以后的都会出错。因为一个地址只能释放一次。
6. 如果你的vector改为vector <CDemo*> *a1 = new vector <CDemo*>(); 即存储类指针,那么在执行delete a1之前,还要手工去删除vector中的每个元素。
7. 如何验证:在析构函数中用cout输出字符串。
---------------------------------------------------------------------------------------------------
按我的理解:
代码执行到delete a1的时候vector调用了对象的析构函数~CDemo(){if(str) delete [] str;} ,
这个时候已经把d1.str = new char[32] 给释放掉了,由于你没有定义拷贝构造函数实现深拷贝,当代码执行完毕的时候,
你的CDemo d1对象还会自己析构一次,又会调用~CDemo(){if(str) delete [] str;} 一次,这个时候就出现重复delete,所以会出错。
如果你定义自己的拷贝构造函数实现深拷贝
CDemo(const CDemo &cd)
{
cout < < "copy constructor:" < < i++ < < endl;
this->str = new char[strlen(cd.str)+1];
strcpy(str, cd.str);
}
函数在执行到a1 -> push_back(d1);
d1的副本会在堆栈中申请另外的内存,而不是直接指向d1.str所指向的内存。这个时候你再看代码的执行:
代码执行到delete a1的时候vector调用了对象的析构函数~CDemo(){if(str) delete [] str;} ,这个时候delete掉的是副本里申请的内存(深拷贝实现)。
当代码执行完毕需要析构CDemo d1的时候,delete掉的是d1.str = new char[32]; 没有重复delete,这样就行了。
所以delete a1是应该的,只是要在类里面加一个深拷贝的拷贝构造函数。