关于析构函数的疑惑

zjuwy1984 2009-09-04 04:02:10
题目:找错

#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,
否则会造成两次析构。
但是我认为不是这样,因为vector<CDemo>是通过new建立在堆上,如果不delete的话
会一直存在,执行完delete a1;后会调用vector<CDemo>中的d1的析构
函数。反之,在整个程序执行结束时,一开始在栈上创建的d1因为超出作用域而自动调用
析构函数,而在vector<CDemo>中的d1由于是在栈上创建的对象的副本,位于堆上,不delete
的话就会一直存在,从而可能造成内存泄露。
不知道我的理解对不对?
...全文
412 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
zgjxwl 2009-09-05
  • 打赏
  • 举报
回复
a1 -> push_back(d1);

这个push_back看过没。。这里会调用拷贝构造的。。

而你的类成员里有指针的数据成员。。。默认的拷贝。。即浅拷贝显然不是你想要的

记住:当类的数据成员里有指针或者引用的时候。。请自己实现拷贝构造函数。。而不是用默认的。

浅拷贝只会拷贝指针的值,最后会让两个指针指向同一块内存。。。也容易出现野指针之类的。一个释放了就都不能用了。
icerlion 2009-09-05
  • 打赏
  • 举报
回复
浅拷贝和深拷贝的问题

要搞清楚你的指针到底指向何处
xuyanziming 2009-09-04
  • 打赏
  • 举报
回复
首先:new是在堆上分配内存,必须在结束时手动释放。
这个程序的错误是没有分清楚对象本体与对象实体这两个概念。在由含有指针的派生类的对象中,对象本体就是对象所拥有的数据成员本身,而对象实体不仅包含数据成员,还包含数据成员中指针指向的内容。这就是为什么在含有指针的类中,一定要在类的析构函数中也要释放指针指向的内容。
浅拷贝就是只拷贝对象本体。深拷贝就是拷贝对象实体。
qinlang007 2009-09-04
  • 打赏
  • 举报
回复
这上面的是程序员面试宝典上的代码:楼主只给了一部分 书上给了正确代码的。
按我的理解:
代码执行到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是应该的,只是要在类里面加一个深拷贝的拷贝构造函数。
whg01 2009-09-04
  • 打赏
  • 举报
回复
总结一下
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输出字符串。
lnuyasha_hrb 2009-09-04
  • 打赏
  • 举报
回复
首先应该明白这2点:
1,vector在程序结束时会自动通过调用自身析构函数进行析构;
2,悬挂指针的问题;
那么如果在delete之后a1成为了悬挂指针,在程序结束时调用vector的析构函数时,会找不到a1所只想的内容,所以会报错;
你可以跟踪一下,看看是在第一次delete时报错,还是在程序结束时报错的
我的分析就是这样的。
Victor_Dinho 2009-09-04
  • 打赏
  • 举报
回复
LZ说的是不是《程序员面试宝典》里面的?我也看了,也很疑惑~~~觉得问题应该还是上面说的缺少复制构造函数的问题~~~
xiao_ke 2009-09-04
  • 打赏
  • 举报
回复
不过要保证str里面要存字符串..不然 strlen strcpy这两函数可不是好东西
xiao_ke 2009-09-04
  • 打赏
  • 举报
回复

class CDemo{
public:
CDemo():str(NULL){}
~CDemo(){if(str) delete [] str;}
CDemo(const CDemo& d){
str = new char[strlen(d.str)+1];
strcpy(str, d.str);
}
char *str;
};

///
类这样就没有错了! 你查查什么叫浅拷贝,什么是深拷贝就知道了!
zhaokugua 2009-09-04
  • 打赏
  • 举报
回复
浅拷贝与深拷贝,这个东西必须搞明白啊,说起来有点长,但不难理解(如果你指针用的好的话),这个东西不理解,写的类将会很危险的
zhaokugua 2009-09-04
  • 打赏
  • 举报
回复
不用的,d1的析构函数会自动调用,你在析构函数里面已经delete了,new的东西必须有对应的delete,但有的需要你自己手动调用,如果你写在析构函数里,人家会自动调用析构函数。

但是楼主的代码感觉的确有问题,push_back(d1),如果一个类里面出现指针的话,一般你要写构造函数,拷贝构造函数,赋值运算符重载,析构函数!看看上面说的浅拷贝与深拷贝你就明白了,自己找找看!
slqvstian 2009-09-04
  • 打赏
  • 举报
回复
我的理解是vector里的CDemo里的char*指针在作怪,因为你没有写CDemo的拷贝构造函数,所以Vector里的副本char*所指向的也就是你new char[32]。两个不同的CDemo对象的char*指针指向同一片内存,析构函数时当然会将这片内存析构两次罗,貌似这段代码你不管delete a1还是不delete a1都有错误..
zjuwy1984 2009-09-04
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 xiao_ke 的回复:]
相信书吧  一般写书的人 这些基础上还是没有问题的
[/Quote]

我还是不是太明白啊,a1指向的内容都在堆里,最后肯定要调用delete的啊
而作者的意思是不要这个delete啊
xiao_ke 2009-09-04
  • 打赏
  • 举报
回复
相信书吧 一般写书的人 这些基础上还是没有问题的
xiao_ke 2009-09-04
  • 打赏
  • 举报
回复
char *p1 = new char[100];
char *p2 = p1;

简单的说,就这么回事。 p2是vector里面的, p1是你new出来的!
lcy853142 2009-09-04
  • 打赏
  • 举报
回复
不delete
的话就会一直存在
你所说的是那个副本对象会一直存在从而造成内存泄露吗?

告诉你一点,new出来的对象必须得delete掉
zjuwy1984 2009-09-04
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 lcy853142 的回复:]
vector <CDemo> *a1 = new vector <CDemo>();
    a1 -> push_back(d1);
   
    delete a1;

a1是在堆上创建的,必须得手动释放掉,不释放掉就会造成内存泄露(不管这个a1里有没有元素)
a1中的Cdemo对象是d1的副本,也就是说这个副本跟dl的内存是没有任何关系的

[/Quote]

那这段程序到底是对的还是错的啊?你的意思好像认为是对的?
lcy853142 2009-09-04
  • 打赏
  • 举报
回复
vector <CDemo> *a1 = new vector <CDemo>();
a1 -> push_back(d1);

delete a1;

a1是在堆上创建的,必须得手动释放掉,不释放掉就会造成内存泄露(不管这个a1里有没有元素)
a1中的Cdemo对象是d1的副本,也就是说这个副本跟dl的内存是没有任何关系的
taodm 2009-09-04
  • 打赏
  • 举报
回复
楼主听说过“拷贝构造函数”和“深拷贝”名称没有?
yshuise 2009-09-04
  • 打赏
  • 举报
回复
知道什么叫封装吗?不要有c的想法。。

64,642

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧