为什么在删除对象之前调用复制构造函数?

时间:2013-08-23 12:25:26

标签: c++ memory-management constructor destructor copy-constructor

我有以下类,其中包括一个复制构造函数:

标题文件:Fred.h

namespace foo {


class Fred {
private:
    int _x;
    int _y;


public:
    Fred();                         // Default constructor
    Fred(Fred const &other);        // Copy constructor
    Fred(int x, int y);             // Regular parameters
    ~Fred();                        // Destrcutor

};


}

实施文件:Fred.cpp

#include "Fred.h"
#include <iostream>

foo::Fred::Fred(){
    _x = 0;
    _y = 0;

    std::cout << "Calling the default constructor\n";
}

foo::Fred::Fred(Fred const &other){

    _x = other._x;
    _y = other._y;

    std::cout << "Calling the copy constructor";
}

foo::Fred::Fred(int x, int y){
    _x = x;
    _y = y;

    std::cout << "Calling the convenience constructor\n";
}

foo::Fred::~Fred(){

    std::cout << "Goodbye, cruel world!\n";
}

我希望看到析构函数在超出范围时调用,而是调用复制构造函数然后调用析构函数。为什么要创建副本?我泄漏了记忆吗?

using namespace foo;

int main(int argc, const char * argv[])
{



    {
        Fred f2 = *new Fred();

    } // I was expecting to see a destructor call only


    return 0;
}

6 个答案:

答案 0 :(得分:5)

那是因为您正在使用内存泄漏运算符 *new

分配有new的对象永远不会自动删除;只有明确使用delete。您的代码动态分配一个对象,将其复制到f2,然后丢失唯一指向动态对象的指针。

如果您只是想创建一个本地对象:

Fred f2;

当您确实需要动态分配时(换句话说,如果对象需要比当前范围更长),请始终使用RAII对象(如智能指针)来避免内存泄漏。例如:

std::unique_ptr<Fred> f2(new Fred);   // C++11 - no "delete" needed
auto f2 = std::make_unique<Fred>();   // C++14 - no "new" or "delete" needed

答案 1 :(得分:4)

是的,代码泄漏内存:new Fred()在堆上分配一个对象,但代码不保存返回的指针,也不删除对象。

调用复制构造函数的原因是f2的创建复制了参数,就像它已被编写一样

Fred f2(*new Fred());

答案 2 :(得分:4)

创建副本是因为您正在复制由此表达式创建的动态分配对象:

new Fred();

进入Fred f2

Fred f2 = *new Fred(); // f2 is a copy of RHS object

永远不会调用动态分配对象的析构函数。这是内存泄漏。只会调用f2的析构函数。

请参阅this demo

答案 3 :(得分:4)

你可能想写的是:

Fred f2 = Fred();

由于Fred具有用户定义的默认构造函数,因此您可以将其缩短为:

Fred f2;

答案 4 :(得分:2)

默认情况下,C ++在堆栈上分配变量。当你写一个像Foo* foo = new Foo;这样的句子时,你正在做的是在栈上分配一个指针,指向你在堆上分配的新对象。
您的代码忘记释放由指针指向的对象使用的内存,因此该对象永远不会被释放,及其析构函数永远不会被调用

我建议您查看此主题:When should I use the new keyword in C++?

答案 5 :(得分:1)

通过在堆上取消引用新创建的对象并将其分配给Fred f2,您隐含地调用了复制构造函数。它与

相同
Fred f1;
Fred f2 = f1;

除此之外,您“丢失”指向堆上对象的指针,该指针不会自动删除 - &gt; 内存泄漏


如果您不使用RAII(但是),则需要手动清除。看起来像这样:

using namespace foo;

int main(int argc, const char * argv[])
{



    {
        Fred* pF2 = new Fred();  // keep the pointer to manually delete

    } 
    // no destructor call even though scope is left --> delete manually
    delete pF2;
    pF2 = 0;

    return 0;
}