假设你有一个这样的课程
class Level
{
public:
Level(std::string);
~Level();
private:
Bitmap* map;
}
在课堂上你有这个
Level::Level(std::string)
{
map = new Bitmap(path);
}
想知道你能打电话吗
Level::~Level()
{
delete map;
}
因为我担心课程是否超出范围而且我没有删除地图。然后,这不会导致内存泄漏。我是否必须手动呼叫才能删除地图。如果我在我的程序中的构造函数中调用delete,则会崩溃。
就像我可以添加一个方法到Level调用说破坏地图我删除地图。但是,想知道为什么我不能将删除添加到析构函数中。
答案 0 :(得分:4)
当Level
对象超出范围时,将调用其析构函数,因此释放内存非常有用,因为不再需要该内存。您还可以使用unique_ptr
,从而自动执行内存释放。
答案 1 :(得分:3)
答案已经指出,当对象超出范围时,您可以信任您的析构函数被调用。我不会重申这一点。我只是想指出,不需要使用Bitmap
分配new
(除非您使用的是自定义内存分配器,但这不是这种情况)。您可以使用初始化列表构建它:
class Level
{
public:
Level(std::string);
private:
Bitmap map;
};
Level::Level(std::string)
: map(path)
{
}
现在它具有自动范围,您不必担心您的析构函数。
答案 2 :(得分:2)
这就是析构者所代表的原因。当对象超出范围(驻留在堆栈对象上的内存)或调用delete
时(对于动态分配的对象),将显式调用析构函数,以便释放对象保留的内存。如果要在销毁时释放成员对象内存,可以使用delete
(或delete[]
用于数组)调用每个对象的析构函数。最好使用智能指针,以避免无意的内存泄漏,并确保在所有情况下都能正确释放内存,因为它们使用RAII概念(RAII and smart pointers in C++)。
答案 3 :(得分:2)
这基本上是正确的。
然而:
如果您以这种方式管理内存,则需要确保创建复制构造函数和赋值运算符。 (这就是你的崩溃所在。)
另一种选择,最好的方法,是使用RAII并且不存储原始指针而是存储范围或自动指针。甚至只是一个直接封装的对象!那么您根本不需要delete
。
答案 4 :(得分:0)
因为我担心课程是否超出范围而且我没有删除地图。然后,这不会导致内存泄漏。
你是对的 - 你应该delete map
完全按照你的代码做。但是,您还应该使您的对象不可复制(从boost或C ++ 11不可复制的基类派生,或者添加operator=
复制赋值和复制构造函数的私有声明(没有定义/实现)。否则,如果您(故意或偶然或偶然地 - 例如,当您将对象存储在有时会复制它的容器中时,例如std::vector
)复制您的对象,那么第一个被解构的副本将delete
{ {1}},尝试使用它的任何其他副本都可能会崩溃,并且任何其他副本的析构函数也会尝试map
它也会有未定义的行为。
我是否必须手动调用以删除地图。
是的,在您的代码中。
更好的选择是使用智能指针,其自己的析构函数将删除指向的对象。
如果我在程序的构造函数中调用delete,那么我会崩溃。
好吧,如果你在delete
之后调用delete
,那么构造函数不会崩溃,但是在构造函数返回后你不会有new
个对象使用,并且如果你然后在析构函数中再次尝试map
,你会得到未定义的行为,这可能表现为崩溃。
如果您希望有时{@ 1}}地图早于析构函数,您可以将指针设置为delete
,以便将来delete
无法安全地执行任何操作。然后,您应该在尝试使用NULL
之前检查NULL。
就像我可以添加一个方法到Level调用说破坏地图我删除地图。但是,想知道为什么我不能将删除添加到析构函数中。
如上所述,您可以拥有delete
,但必须与析构函数协调。
如果没有令人信服的理由不这样做,最好制作map
成员数据,而不是通过引用存储它。当指针有用时,如果可能的话,使用智能指针。如果要实现明确的手动内存管理,请注意上述问题。
答案 5 :(得分:-3)
这是一种不寻常的方式。通常,对象的生命周期由对象之外的因素决定。
但实际上MFC曾经(仍然如此?)在Window被销毁时就是这么做的。 (响应WM_NCDESTROY,我相信。)这可以确保在窗口消失后没有Window实例泄漏内存。
所以我想说它在某些情况下是有效的。你可以称之为自杀!