返回在堆上存储的类

时间:2011-10-06 03:19:34

标签: c++ visual-studio-2010 memory

我的项目中遇到了与内存相关的崩溃。我设法将其减少到以下“玩具”项目:

class Foo {
public:

    Foo() : bar(nullptr) {
        bar = new int(3);
    }

    ~Foo() {
        if (bar) {
            delete bar;
            bar = nullptr;
        }
    }

private:
    int* bar;
};

Foo test() {
    Foo foo;
    return foo;
}

int main() {
    test();
             // <--- Crash!
    return 0;
}

我无法弄清楚为什么我会在指定的线路上崩溃。这是我到目前为止收集的内容,如果我错了,请纠正我:

基本上,我在foo的堆栈上创建test()Foo在堆上分配一些内存。一切都很好。然后我尝试返回foo。返回foo,但会立即销毁。然后,当我退出时,我再次试图摧毁foo;因此我两次调用Foo的析构函数,然后崩溃了。我没有得到的是为什么这是一个问题。我在删除它之前检查Foo::bar是否为null,如果我删除它,我之后将其设置为null。

为什么会导致崩溃?我怎样才能解决这个问题?我究竟做错了什么?我很困惑! :(

更新:发生这种情况的主要原因是缺少复制构造函数,如下面的答案中所述。但是,我的原始项目确实有复制构造函数,或者我认为。如果您的类是派生的,那么您必须为派生类显式创建一个复制构造函数。 Base(const Derived& other)不算作复制构造函数!

2 个答案:

答案 0 :(得分:9)

您违反了rule of three

当您在构造函数和析构函数中手动管理内存时,您还必须提供复制构造函数和赋值运算符,否则您的程序将在删除后双重删除并使用内存。

在您的特定示例中,您具有编译器提供的复制构造函数。将foo复制到test的返回值时,您有两个具有相同bar指针的对象。本地foo超出范围并被销毁,然后其bar设置为nullptr。但是副本(返回值)仍然具有非空指针。然后它也被销毁,并再次删除相同的内存。

答案 1 :(得分:2)

您需要实现一个复制构造函数,并且很可能是一个赋值运算符。

但是,真的,你的问题在于手动管理内存。这几乎总是一个糟糕的主意,因为它是错误进入代码的最常见方式之一。使用shared_ptr而不是手动管理代码中的内存,令人惊讶的是代码的维护更加轻松!