#include "stdafx.h"
#include <iostream>
#include <vector>
using namespace std;
class Demo
{
public:
Demo(const char* a)
{
str = new char[32];
strcpy(str,a);
}
~Demo()
{
if (str)
delete [] str;
}
private:
char *str;
};
int _tmain(int argc, _TCHAR* argv[])
{
Demo d("Hello,World!");
vector<Demo> *p = new vector<Demo>();
p->push_back(d);
delete p;
return 0;
}
问:这段代码有错误,请找出其中出错的原因。
代码自己定义了一个类,类中有一个变量,有构造函数,析构函数。在主函数中,实例化一个Demo类型的变量d,用Vector存放这个变量d,最后删除vector。执行这段代码之后,程序会出错,无法执行下去。
要明白这个程序,得先了解当执行vector的push_back(d)函数时,加入到vector中的是原来的d对象呢,还是d对象的副本?
答案是d对象的副本,当vector中添加元素的时候,是将元素值复制到容器里。就是说容器中存放的是原始元素的副本。被复制的原始值与新容器中的元素各不相关,此后,容器内元素值发生变化时,被复制的原值不会受到影响。
既然加入到vector中的对象是副本,那么就涉及到d对象的复制,程序中并没有写复制构造函数,但是类会调用默认的复制构造函数(或者称为合成复制构造函数),默认构造函数只是简单的将对象的每个非static成员,依次复制到正创建的对象。Demo类只有一个成员变量char * str,这是一个指针类型的变量,因此会执行this.str = d.str;其结果是两个指针共同指向了同一块儿内存区域。(这就是所谓的浅层复制,不清楚的可以看看)
因为当执行delete p;时,在销毁vector对象的同时,也会将vector中存储的对象同时销毁掉,d的副本str指向的区域被销毁掉,此时d的str成员就成了悬垂指针。当main执行完毕后,需要将d销毁掉,此时d的str成员是悬垂指针,因此会找不到要销毁的内容,程序就会出错。
这个问题实质上的问题在于,类的深层复制和浅层复制的区别。当有指针类型成员变量的时,应该采取深层复制。当执行复制操作时,保证每个对象都有自己的独立的内存空间。
程序应该修改如下:
#include "stdafx.h"
#include <iostream>
#include <vector>
using namespace std;
class Demo
{
public:
Demo(const char* a)
{
str = new char[32];
strcpy(str,a);
}
Demo(const Demo& d)
{
this->str = new char[32];
strcpy(str,d.str);
}
~Demo()
{
if (str)
delete [] str;
}
private:
char *str;
};
int _tmain(int argc, _TCHAR* argv[])
{
Demo d("Hello,World!");
vector<Demo> *p = new vector<Demo>();
p->push_back(d);
delete p;
return 0;
}
总结:
1. 为了管理具有指针成员的类,必须自己定义三个复制控制成员:复制构造函数,复制操作符,析
构函数。
2. vector中添加元素的时候,是将元素值复制到容器里。就是说容器中存放的是原始元素的副本。被
复制的原始值与新容器中的元素各不相关,此后,容器内元素值发生变化时,被复制的原值不会
受到影响,反之亦然。
3. 当删除vector对象时,会将vector中存储的对象一并删除掉。