1

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

头文件: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;
}
4

6 回答 6

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
于 2013-08-23T12:30:17.997 回答
4

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

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

Fred f2(*new Fred());
于 2013-08-23T12:27:50.267 回答
4

因为您正在复制由该表达式创建的动态分配的对象,所以创建了一个副本:

new Fred();

进入Fred f2

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

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

请参阅此演示

于 2013-08-23T12:28:03.207 回答
4

你可能打算写的是:

Fred f2 = Fred();

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

Fred f2;
于 2013-08-23T12:30:41.373 回答
2

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

我建议你查看这个线程:我什么时候应该在 C++ 中使用 new 关键字?

于 2013-08-23T12:36:39.607 回答
1

通过取消引用堆上新创建的对象并将其分配给Fred f2您,隐式调用复制构造函数。这是一样的

Fred f1;
Fred f2 = f1;

此外,您“丢失”了指向堆上对象的指针,该指针不会被自动删除 --> Memory Leak


如果您不使用 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;
}
于 2013-08-23T13:57:16.820 回答