以下是面试宝典 CHAP 9 STL模板与容器章的一个例子:
#include <vector>
#include <iostream>
using namespace std;
int i = 0;
int j = 0;
class CDemo{
public:
CDemo():str(NULL){
cout << "constructor:" << i++ << endl;
}
/以下函数为修改后添加的部分//
CDemo(const CDemo& cd)
{
cout << "copy constructor:" << i++ << endl;
this->str = new char[strlen(cd.str) + 1];
strcpy(this->str, cd.str);
}
~CDemo(){
cout << "destructor:" << j++ << endl;
if(str)
{
delete [] str;
str = NULL;
}
}
char* str;
};
void 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;
a1 = NULL;
return;
}
相关的问题
1. strcpy_s的使用
采用的环境是VS2015,编译就会提示以下错误:
error C4996: ‘strcpy’: This function or variable may be unsafe. Consider using strcpy_s instead.
主要因为一些C库的函数是不进行参数检验(包括越界 ),VS担心使用这些会造成内存异常,所以就改写了同样功能的函数。
但是替换以后,程序提示:
error C2660: “strcpy_s”: 函数不接受 2 个参数
原因是strcpy_s(strname,strlen(str) + 1,str)需要第二个参数用来限制拷贝大小的。
运行的结果为:
2. 本题目考察知识点
该题目原来是一道改错题,原题并没有修改添加的那个拷贝函数。运行时会出现delete 同一片内存导致程序崩溃。书上说,有人认为这段程序的错误是vector对象指针能够自动析构,所以不需要调用delete a1, 否则会造成两次析构。这种观点是不正确的。
3. new与指针
vector *a1 = new vector();
定义了一个向量指针a1, a1是new出来的,所以必须要手工delete。所以delete a1并没有问题。另外, a1本身是存在于栈空间上的一个变量,a1所指向的存储空间是new在堆空间上开辟的,所以 delete a1仅仅是将堆上的空间释放,此时a1仍存在于栈空间上,因此需要通过 a1 = NULL将其赋值为空,防止误引用。
4. 问题的实际原因
因为vector是存储类,而不是类指针。所以执行
a1->push_back(d1) ;
首先会在栈上创建d1的一个拷贝,压入栈,作为参数传递给push_back。delete a1的时候会调用这个d1_1的析构。另外,main函数退出的时候,d1这个局部变量也要析构。因为class CDemo没有拷贝构造函数,所以创建拷贝时只是简单的把新对象中每个成员变量的值设置成与原来的对象相等。相当于运行memcpy。这时问题就来了,因为成员是char * str,这样d1, d1_1的str都是指向同一个地*址。所以只有第一次调用CDemo的析构函数时能运行正确,以后的都会出错。因为一个地址只能释放一次。 所以修改办法是定义自己的拷贝构造函数实现深拷贝,如上所增加的程序片段。
如果vector改为vector <CDemo*> *a1 = new vector <CDemo * >(); 即存储类指针,那么在执行delete a1之前,还要手工去删除vector中的每个元素。