我有以下类,其中包括一个复制构造函数:
标题文件: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;
}
答案 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;
这样的句子时,你正在做的是在栈上分配一个指针,指向你在堆上分配的新对象。
您的代码忘记释放由指针指向的对象使用的内存,因此该对象永远不会被释放,及其析构函数永远不会被调用。
答案 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;
}