几天前我在c ++中看到了一个关于内存泄漏的采访问题。 代码就像(如果我没记错的话):
#include <iostream>
using namespace std;
class super {
int value;
int arr[1000];
public:
super() :value(0) {}
super(int value) :value(value) {}
virtual int getValue() const{
return this->value;
}
};
class sub : public super {
int val;
super sup;
vector<int> v1;
public:
sub() :val(0), sup(0) {}
sub(int value) :val(value), sup(value), v1(10,0) {}
int getValue() const{
return this->val;
}
};
int main() {
sub* pt1 = new(sub);
super* pt2 = pt1;
pt1 = new(sub);
delete pt2; //memory leak ??
//more code here...
delete pt1;
return 0;
}
问题是如何在实现 - 设计级别中避免此类内存泄漏。我想这个问题不仅仅是回答“不要使用那样的指针”。
是否与将析构函数实现为虚拟或使用动态强制转换有关?我们如何实现析构函数,以便delete pt2
不会造成任何内存泄漏?任何人都可以进一步分析这个例子吗?
提前致谢。
答案 0 :(得分:2)
首先,delete pt2;
并不特别是内存泄漏。我最初说它是未定义的行为,因此标准允许任何内容,包括内存泄漏。但是仔细检查这些类,实际上对于带有int
数组的代码的第一个版本,sub
是非常容易破坏的,因此看起来很奇怪,这段代码是正确的。然后你改变了代码,使sub
不再是简单的可破坏的(由于vector
数据成员),所以它现在是未定义的行为。
提出问题的人可能一直在寻找具体的答案,如果是这样,我不知道那是什么,但这个错误可能会被设计掉#34;不止一种方式:
super
的析构函数为virtual,以便通过父指针删除sub
。一个常见的经验法则是,具有虚函数的类应该始终具有虚拟析构函数,因为这些类被设计为通过指针/对基类的引用来使用。shared_ptr
允许您编写shared_ptr<super> pt2(new sub());
,即使使用非虚拟析构函数也会正确删除对象,尽管有些人认为该功能模糊不清。super
和sub
中,使用vector<int>
而非普通int
数组。这使得对象本身更小,从外部存储大部分数据,因此对于像这个例子的用户,用户不会觉得他们必须动态分配对象,而是可以将它们放在堆栈上(而不是1000个int)必须不能进入堆栈,只是它的大小可能会让用户感到紧张。因此,如果对super
所做的sub
进行相同的更改,则调用代码可以通过将设计原则设置为使用自动变量而不是动态分配来降低此类错误的风险尽可能。答案 1 :(得分:1)
它会泄漏内存,因为super
没有虚拟析构函数(virtual ~super() = default;
)。
现在,当您在super*
上调用指向sub
的删除时,sub
的析构函数未被调用,泄露其资源。
如果从中派生的任何类具有任何要解除分配的资源,则始终将基类析构函数声明为虚拟。
答案 2 :(得分:0)
这是不经典内存泄漏。经典的内存泄漏会产生内存分配而不会释放。您的新/删除对实际上匹配。
这是未定义的行为。您会看到一个相当冗长的解释here为什么有public
但非virtual
析构函数会产生问题。
使用他们的特定编译器,这个可能会泄漏内存,因为编译器尽其所能在面对未定义的行为时表现得很好。另一个编译器或其他版本的编译器可能会炸毁或创建粉红色的跳舞独角兽。它不是内存泄漏的一个很好的例子,因为内存泄漏是编译器在更糟糕的情况下可以产生的最好的。
答案 3 :(得分:0)
如果您要求设计级的解决方案,讨论可以变得非常广泛 而且我相信你并不真正讨论如何实现析构函数。
关于如何避免内存泄漏的设计级别讨论&#39;已经由C ++字段中的大多数专家完成了:Stroustrup和Sutter。
我建议观看他们的演示视频并阅读他们的文章。