坛友们,这段代码会内存泄露?

hengshan 2011-07-15 03:32:10
加精
如下代码,delete pa;以后,内存泄露吗?欢迎讨论^_^

class A
{
public:
A(){}
virtual void f(){}

private:
int m_a;
};

class B : public A
{
public:
virtual void f(){}
private:
int m_b;
};

int main()
{
A *pa = new B;
delete pa;
pa = NULL;

return 0;
}
...全文
20552 2 收藏 464
写回复
464 条回复
切换为时间正序
请发表友善的回复…
发表回复
xinzhanghu 2013-10-18
引用 169 楼 jjajun 的回复:
顺着lz的问题,也问一个问题: 为什么将A类中的 virtual void f(){}注释掉,然后程序会在指针释放时,出错?? class A { public: A(){} // virtual void f(){} private: int m_a; }; class B : public A { public: virtual void f(){} private: int m_b; }; int main() { A *pa = new B; delete pa; pa = NULL; return 0; }
我需要问这个问题。。。
回复
unituniverse2 2013-10-05
看这个吧 http://blog.csdn.net/unituniverse2/article/details/12302139
回复
arvin_xiaoting 2013-08-31
引用 468 楼 xiaoting451292510 的回复:
个人认为是没有内存泄露的,而且与编译器无关。 要解决这个问题,我们从如下几个方面入手好了。 第一, new 的执行过程 (1)通过operator new申请内存 (这一步与malloc 是一样的) 这里所有的编译器都会记录这个指针申请的内存空间大小是多少,这样delete时间就知道删除多大空间了。 (主要取决于编译器的具体实现,一般情况下,由于malloc在申请内存之前指定了申请内存大小,编译器应该记录这个信息,在free时编译器根据这个记录信息进行释放即可。) 于是就有了如下,一个删除鸟样的代码。
int* p = new int[10];
	memset(p, 0x00, sizeof(int)*10);

	void* pp = reinterpret_cast<void*>(p);
	if (NULL!=pp) {
		delete pp;
		pp = NULL;
	}

	if (NULL!=p) {
		delete[] p;
		p = NULL;
	}

	if (NULL!=p) {
		delete p;
		p = NULL;
	}
(2)使用placement new调用构造函数(简单类型忽略此步) (3)返回内存指针 第二,构造函数 实例化对象,初始化数据的 第三,析构函数 放弃在类对象的构造函数或生命期中获得的资源(这里确实是包括释放互斥锁或删除内存,但我们分二步,删除内存放后面),这步,认为做一些简单的清理工作。 第四,删除内存 OK,删除内存就如第一步对应的了。 因此,楼主的当时的情况,是绝对不会内存泄露的。只是没有调用子类的析构函数而已。 如果不调用子类的析构函数会出现怎么情况,自己查书去吧.....偶就不多说了。 ----------------- PS: 楼主,跟据当时情况,我也断定那个Manager技术上是有些问题的。 但同样,如果是我我也不大会招你的。 原因如下: 《如果析构函数没什么 用,就可以不调用啊。》 上述话,你觉得应该说么? 事实上确实是这样说的,但是我们确实不应该说,程序并不是哪一个人写的,是大家一起合作的,你不能保证所有人技术方面都有很深入的理解。当他看到你的代码没有释放析构函数会怎么想。 OK,即使这样,万一哪天有人需要添加一些东西,然后在析构函数里释放怎么办?如果他不是很会呢? 再次,学会沟通,工作不如研究,沟通太重要了。技术感觉还是其次。 最后,写代码,写程序尽量让整个代码保持通用性,和一般性。即使可能有些感觉上写和不写是一样的代码。 例如,有个零时申请的new, 用完delete后,同样把指针置为NULL,尽管置不置都一样。这点类似你说的那个析构函数要不要virtual是一样的。 ----------- 最最后,我相信楼主技术方面肯定是没什么问题的,我也确信楼主将来在技术方面会有一定成就的,不过希望楼主也多站在一般人,或者新人的角度来想想问题,这样您的职业生涯将会更加平坦,祝:楼主早日找到好的工作呢~~~
删除方法一 void* pp = reinterpret_cast<void*>(p); if (NULL!=pp) { delete pp; pp = NULL; } 删除方法二 if (NULL!=p) { delete[] p; p = NULL; } 删除方法三 if (NULL!=p) { delete p; p = NULL; } 指三种删除方式,是一个鸟样的。如果直接运行代码会崩溃的。。为何我就不多说了。。
回复
arvin_xiaoting 2013-08-31
个人认为是没有内存泄露的,而且与编译器无关。 要解决这个问题,我们从如下几个方面入手好了。 第一, new 的执行过程 (1)通过operator new申请内存 (这一步与malloc 是一样的) 这里所有的编译器都会记录这个指针申请的内存空间大小是多少,这样delete时间就知道删除多大空间了。 (主要取决于编译器的具体实现,一般情况下,由于malloc在申请内存之前指定了申请内存大小,编译器应该记录这个信息,在free时编译器根据这个记录信息进行释放即可。) 于是就有了如下,一个删除鸟样的代码。
int* p = new int[10];
	memset(p, 0x00, sizeof(int)*10);

	void* pp = reinterpret_cast<void*>(p);
	if (NULL!=pp) {
		delete pp;
		pp = NULL;
	}

	if (NULL!=p) {
		delete[] p;
		p = NULL;
	}

	if (NULL!=p) {
		delete p;
		p = NULL;
	}
(2)使用placement new调用构造函数(简单类型忽略此步) (3)返回内存指针 第二,构造函数 实例化对象,初始化数据的 第三,析构函数 放弃在类对象的构造函数或生命期中获得的资源(这里确实是包括释放互斥锁或删除内存,但我们分二步,删除内存放后面),这步,认为做一些简单的清理工作。 第四,删除内存 OK,删除内存就如第一步对应的了。 因此,楼主的当时的情况,是绝对不会内存泄露的。只是没有调用子类的析构函数而已。 如果不调用子类的析构函数会出现怎么情况,自己查书去吧.....偶就不多说了。 ----------------- PS: 楼主,跟据当时情况,我也断定那个Manager技术上是有些问题的。 但同样,如果是我我也不大会招你的。 原因如下: 《如果析构函数没什么 用,就可以不调用啊。》 上述话,你觉得应该说么? 事实上确实是这样说的,但是我们确实不应该说,程序并不是哪一个人写的,是大家一起合作的,你不能保证所有人技术方面都有很深入的理解。当他看到你的代码没有释放析构函数会怎么想。 OK,即使这样,万一哪天有人需要添加一些东西,然后在析构函数里释放怎么办?如果他不是很会呢? 再次,学会沟通,工作不如研究,沟通太重要了。技术感觉还是其次。 最后,写代码,写程序尽量让整个代码保持通用性,和一般性。即使可能有些感觉上写和不写是一样的代码。 例如,有个零时申请的new, 用完delete后,同样把指针置为NULL,尽管置不置都一样。这点类似你说的那个析构函数要不要virtual是一样的。 ----------- 最最后,我相信楼主技术方面肯定是没什么问题的,我也确信楼主将来在技术方面会有一定成就的,不过希望楼主也多站在一般人,或者新人的角度来想想问题,这样您的职业生涯将会更加平坦,祝:楼主早日找到好的工作呢~~~
回复
sqlnew 2013-08-28
神帖,果断留言并收藏学习
回复
ninainaigedan0 2013-08-26
引用 463 楼 unituniverse2 的回复:
因该不会有泄漏的。原因如下: 我们看一下分配和释放期间发生了什么: 首先一段代码调用了new。这个new实际上会分成两步:先调用 void * operator new (size_t cb); 如果成功则以其返回的地址指针为基址调用类构造函数。 释放时则相反,一段代码调用delete,也是分成两步:先调用析构函数,再调用 void operator delete (void *); 看上面的讨论,也就是说,如果类及其父类的构造析构函数都没涉及到额外的内存分配释放操作(另行调用其他的new和delete),那有没有内存泄露就要看是否这两个operator操作符是否会导致内存泄露了。 但是我们看new的返回参数或者delete的形参都是void *,并没有涉及到、delete也没有使用到具体的类型信息,而是一个二进制内存模型地址:void。在单继承情况下类和派生类的基地址是共用的,所以不管用哪种类指针都只是同一个void*值,因此不会产生泄露问题。
完全赞同!!!
回复
ninainaigedan0 2013-08-26
引用 65 楼 hengshan 的回复:
不好意思,这两天有点忙,所以没有上来更新,看到这么多热心的同志,不更新,对不起大家啊。 本人已经辞职,没有骑驴找马,本打算休息2个月再找工作,但是休息了一个礼拜就心虚了,一想起房贷、不久就要当父亲了,再看看手中的银子,就坐不住了,于是开始找工作了。 上个礼拜五(7月15号)去了p公司面试,面试前这个A哥们说:“我们公司是一家注重技术的公司,来这里的人技术要经过2到3轮的面试,我们宁愿不招,也要求技术必须过关”。听了后,俺还小担心了一下,会不会问很复杂的问题啊,还小准备了一下。 去了p公司以后,先去找了A哥们,然后他找了另一个B哥们面试我,此时我确认A哥们是HR。我和B哥们聊了大概一个小时,气氛很和谐,合作很愉快,他大概问了c++,stl,tcp/ip,设计模式,ace方面的东西,都比较基础,我也答的可以。完了以后,B找了半天A没有找到,就告诉我先回去,后续打电话。 然后我就走了,那天正是下午1点多,热啊,附件没有卖水的,快要着火。我走出去大概30分钟了,B打电话说A回来了,想找我聊聊,还说神马不好意思啊之类的,哥没多想就答应回去了。 回去后,见到了A,他简单介绍了他们公司,说他自己是这个地区的最大经理(哦不是hr),然后又是说了一通他们的技术要求怎么怎么高啊。我一想,这是经理面试技术,这人一定很牛逼啊,感觉他好像比我大^_^。然后开始了技术面试: A : 你说一下pure virtual function 是咋回事?语法啥样子? 我: 我说一般在接口类中声明,叫做虚基类,声明此函数的类不能实例化对象,此类的派生类如果没有 定义此函数,也将会是虚基类。语法就是,virtual void f()=0;形式 A : virtual deconstructor有什么用? 我: 当通过delete基类的指针时,可以调用到子类的析构函数。 A :基类析构函数是不是必须要声明为virtual? 我:不一定,如果子类的析构函数没有什么用,不需要释放资源的话,可以不声明为virtual A: 你确定这种情况不需要? 我:确定 A:然后他很不屑的、鄙视的一笑,然后在纸上不知道写了个啥玩意儿。 我当时感觉很不爽,还是忍了。 A:构造函数为什么不需要声明为virtual? 我: 对象构造的时候,构造函数是从父类到子类的调用过程,所有的都能调用到,也不存在多态的问题。 回答完,这个问题后,我越想越郁闷,决定和他把问题讨论清楚。 我:你刚才说的 virtual析构函数的问题,你一笑,是不是觉的不声明为virtual,会发生内存泄露啊? A:肯定会,然后写了,我帖子开头发的那段代码。 我:delete运算符,他是先调用析构函数,然后调用operate delete(void *)的啊, 如果析构函数没什么 用,就可以不调用啊。 A:你先不管delete怎么运行的,就是有问题 我:就算不管,按照你的意思只要有多态,基类的析构函数必须要声明为virtual啊? A :没回答我这个问题,然后说了delete运算符的意思,然后说这和编译器有关,有的编译器可能泄露, 有的不泄露。 我:我在之前的公司已经讨论过此问题,有人就是说和编译器有关。但是,就你举的这个例子而言, 和编译器没有任何关系。 A:没能说服我,然后说下一个吧 我:算了,算了吧,不面试了,你连这个问题都没搞清楚,还说自己是manager,哥不淡定的走了。 这就是故事整个过程。 欢迎拍砖!
完全认同楼主的观点!
回复
enjolras 2012-04-11
尝试了一下,不会有泄漏,effective c++中的什么格式化你的硬盘应该只是调侃。
换个位置思考下,如果你是个编译器生产商,你会在这种标准未定义行为的时候去格式化硬盘吗?正常人都会弃格式化硬盘而选择只调用派生类的析构函数吧?

就事论事,首先要鄙视一下面试官,不应该出现轻蔑的表情,作为一个求职者的话会很不爽,即使面试官是对的。。另外呢,楼主也要看下是不是可以表达得更好,毕竟人为刀俎,我为鱼肉。。
回复
unituniverse2 2012-03-28
因该不会有泄漏的。原因如下:
我们看一下分配和释放期间发生了什么:
首先一段代码调用了new。这个new实际上会分成两步:先调用
void * operator new (size_t cb);
如果成功则以其返回的地址指针为基址调用类构造函数。
释放时则相反,一段代码调用delete,也是分成两步:先调用析构函数,再调用
void operator delete (void *);

看上面的讨论,也就是说,如果类及其父类的构造析构函数都没涉及到额外的内存分配释放操作(另行调用其他的new和delete),那有没有内存泄露就要看是否这两个operator操作符是否会导致内存泄露了。
但是我们看new的返回参数或者delete的形参都是void *,并没有涉及到、delete也没有使用到具体的类型信息,而是一个二进制内存模型地址:void。在单继承情况下类和派生类的基地址是共用的,所以不管用哪种类指针都只是同一个void*值,因此不会产生泄露问题。
回复
ofthink 2012-02-07
看到一半 还没有结果 留个马克 继续
回复
zeroyiwang 2012-02-02
应该不会有泄露的、
回复
coFinder 2012-01-24
mark```有时间还要好好看看··
···Code_GodFather
回复
myhaikuotiankong 2011-11-11
看来这个问题的争议很大啊。。。等大牛给权威的解释啊。。。
回复
a_madman 2011-11-11

我任务会泄露,用A的指针去指向B规格的对象,在delete的时候,应为析构函数不是虚函数,虽然delete时传了一个指针,真实对象的大小系统会有记录,但是会按照A的规格释放而不会像多态那样调用B的析构函数析构B规格的内存!
回复
libin1201119 2011-09-16
我个人认为如果基类有virtual函数,就应该把析构函数也定义为virtual
这是一个习惯,个人看法
回复
horicon 2011-09-06
mark一下
回复
chen16501560 2011-08-17
mark。。。
回复
PowerCock 2011-08-02
LZ不错,别去理会那些只顾喷的人。好久没有看到过这种技术探讨的帖子了,怀念以前CSDN纯纯的时光:)~~~~~~~~~(一个老ID的马甲)

C/C++标准的问题,真的没有深入的研究过,一般都是工作中遇到了再去弄个究竟。具体到LZ这个问题来说,就windows + vc环境而言,确实不会产生内存泄漏!
1、B没有要在析构函数中释放的堆内存;
2、windows的运行库函数malloc/free只记录同一块申请的内存最初始的信息(地址、长度),它不会因为你把这段内存转作它用(A*)而重新对这段内在的信息进行改写。所以当你释放的时候,不存在会把后面的内存给截断。
至于其它系统及编译环境,没有研究过,或许会有一些奇怪的设计可能导致后4个字节被丢弃(这个产生的可能是OS提供的内存管理函数,当你把某段内存转作它用时会重新设置该内存的信息),因此不能断言是否没问题。

至于子类是否也要申明为virtual的,这要看该子类是否存在有派生类的可能。一般来说,在对象被大数量使用的时候,省去virtual可以节省对象的内存开销(不扩充virtual数量的时候没有影响)。这一般看代码习惯了,也许写上是一种比较严谨的做法。
回复
cscscs1027 2011-08-01
纯纯的得分
回复
cscscs1027 2011-08-01
差不多~~~~~
回复
发动态
发帖子
C++ 语言
创建于2007-09-28

5.9w+

社区成员

C++ 语言相关问题讨论,技术干货分享,前沿动态等
申请成为版主
社区公告
暂无公告