有什么方法可以避免以下代码中的内存泄漏吗


#include <new>
class Foo {
public:
int *x;
mutable size_t count {1};
Foo() : x {new int}  {}
Foo(const Foo &rhs) : x {new int} {
if(++rhs.count > 5) {
throw runtime_error("");
}
}
~Foo() {
delete x;
}
Foo &operator=(const Foo &) = delete;
};
int main(int argc, char *argv[]) {
Foo *p {reinterpret_cast<Foo *>(::operator new (sizeof(Foo) * 5))};
Foo f;
for(auto i {0}; i < 5; ++i) {
try {
new (p + i) Foo(f);
}catch(...) {
for(auto j {0}; j < i; ++j) {    //!
(p + j)->~Foo();
}
}
}
::operator delete (p);
}

请注意catch(...) {}中的for(auto j {0}; j < i; ++j)

在这段代码中,我可以将for的条件更改为j <= i,以避免内存泄漏。但如果p在模板容器中,则之前的修订可能会导致UB。由于p + i还没有完全构造好,直接破坏它会导致未定义的行为。

有没有办法避免它,或者这是类设计者的责任?

如果这是面试问题,请告诉面试官你没有写那样的代码,那么你就得到了这份工作。

如果这是一些家庭作业,请给你的老师以下链接,这样他就可以学到一些东西:https://www.aristeia.com/EMC++.html

最后,回答您的问题:

int main(int argc, char *argv[]) {
std::unique_ptr<Foo> p[5];
Foo f;
try {
for (int i=0;i<5;++i) {
//p[i]=std::make_unique<Foo>(f); //Only for C++14
p[i]=std::unique_ptr<Foo>(new Foo(f));
}
} catch (...) {
//Nothing, all is done "magically" by unique_ptr
}
}

现在,实际上,回答您的问题并使您的代码更加做作,您可以使用构造函数初始值设定项列表try-catch(更多信息(

class Foo {
public:
int *x;
mutable size_t count {1};
Foo() : x {new int}  {}
Foo(const Foo &rhs) try: x {new int} {
if(++rhs.count > 5) {
throw runtime_error("");
}
} catch (...) {
delete x;
throw;
}
~Foo() {
delete x;
}
Foo &operator=(const Foo &) = delete;
};

主菜和你的一样。

至于内存泄漏和未定义的行为:

  1. ::operator delete (p);不会销毁您在分配的存储中手动创建的对象。

  2. 如果在复制构造函数中抛出两个异常,您将尝试多次删除同一对象。

  3. catch块中的for循环不应泄漏内存。如果构造函数抛出,那么它之后应该处于未初始化状态。换句话说,如果new int抛出,则不会分配需要释放的空间。手动异常抛出需要确保在构造函数抛出之前再次删除new int分配。

  4. 您尝试在main中编写的所有代码基本上都是在重新设计Foo* p = new Foo[5]{f};的功能,并且类中的内存管理将自动工作,即使您使用std::unique_ptr<int>而不是int*,也会从构造函数中抛出。

这与main((中发生或没有发生的事情无关。在构造函数中:

if(++rhs.count > 5) {
throw runtime_error("");

因为对象永远不会完成构造,所以它的析构函数永远不会被调用。除非某个对象是先构造的,否则无法销毁它,并且在对象完成构造之前抛出异常。

但是,由于类的一个成员是用new构造的,并且没有调用析构函数,所以内存泄漏就是在这里发生的。

避免内存泄漏的唯一实用方法是在抛出此异常之前手动delete x,并清理您分配的内存;或者使x成为一个负责清理自身的对象,就像unique_ptr一样,当抛出异常时,它的析构函数将处理它。这将更符合RAII原则。

最新更新