几天前我看到一个面试问题,是关于 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
不会造成任何内存泄漏?谁能进一步分析这个例子?
提前致谢。
最佳答案
首先,delete pt2;
不是专门的内存泄漏。我最初说这是未定义的行为,因此标准允许任何事情,包括内存泄漏。但是仔细检查这些类,实际上对于代码的第一个版本是 int
阵列,sub
是微不足道的可破坏的,因此,尽管看起来很奇怪,这段代码是正确的。然后你改变了代码使sub
不再是微不足道的破坏(由于 vector
数据成员),所以它现在是未定义的行为。
提出问题的人可能一直在寻找一个特定的答案,如果是这样我不知道那是什么,但这个错误可以通过不止一种方式“设计消除”:
- 构造
super
的析构函数虚拟,以便删除一个sub
通过父指针工作。一个常见的经验法则是说具有虚函数的类应该始终具有虚析构函数,因为此类被设计为通过指向基类的指针/引用来使用。 - 在用户代码中,使用RAII技术确保正确销毁。在这种情况下,智能指针。
shared_ptr
让你写shared_ptr<super> pt2(new sub());
即使使用非虚拟析构函数,该对象也会被正确删除,尽管有些人认为该功能晦涩难懂。 - 很难将此称为“实现设计”决策,因为“不要犯严重的编码错误”不是设计规则,而是语言规则。但是调用代码可以“设计”为不通过指向其基类的指针删除对象,除非这样做是有效的(对于此类而言不是)。类可以通过一致的文档来帮助解决这个问题。
- 在类里面
super
和sub
, 使用vector<int>
而不是普通的int
大批。这使得对象本身小得多,将大部分数据存储在外部,因此对于像这个例子这样的用途,用户不会觉得他们必须动态分配对象,而是可以将它们放在堆栈上(不一定是 1000 个整数 < em>不能进入堆栈,只是它的大小可能会让用户感到紧张)。因此,如果您对super
进行相同的更改你对sub
所做的, 那么调用代码可以通过将尽可能使用自动变量而不是动态分配作为设计原则来降低此类错误的风险。
关于c++ - 如何在设计实现层面避免内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35843318/