繁体   English   中英

在下面的代码中,有什么方法可以避免内存泄漏?

[英]Is there any way to avoid memory leak in the following code?

#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的条件更改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原则

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM