// pointer is destroyed because it goes out of scope,
// but not the object it pointed to. memory leak
if (1) {
Foo *myfoo = new Foo("foo");
}
// pointer is destroyed because it goes out of scope,
// object it points to is deleted. no memory leak
if(1) {
Foo *myfoo = new Foo("foo");
delete myfoo;
}
// no memory leak, object goes out of scope
if(1) {
Foo myfoo("foo");
}
#include <iostream>
#include <new>
struct Foo
{
Foo(int i_) : i(i_) {}
int i;
};
int main()
{
// Allocate a chunk of memory large enough to hold 5 Foo objects.
int n = 5;
char *chunk = static_cast<char*>(::operator new(sizeof(Foo) * n));
// Use placement new to construct Foo instances at the right places in the chunk.
for(int i=0; i<n; ++i)
{
new (chunk + i*sizeof(Foo)) Foo(i);
}
// Output the contents of each Foo instance and use an explicit destructor call to destroy it.
for(int i=0; i<n; ++i)
{
Foo *foo = reinterpret_cast<Foo*>(chunk + i*sizeof(Foo));
std::cout << foo->i << '\n';
foo->~Foo();
}
// Deallocate the original chunk of memory.
::operator delete(chunk);
return 0;
}
10 回答
这取决于指针的类型 . 例如,智能指针通常在删除对象时删除它们 . 普通指针没有 . 当指针指向不同的对象时也是如此 . 一些智能指针会破坏旧对象,或者如果它没有更多引用就会销毁它 . 普通指针没有这样的智慧 . 它们只保存一个地址,并允许您通过专门执行操作对它们指向的对象执行操作 .
这取决于链表的实现 . 典型的集合在销毁时会销毁所有包含的对象 .
因此,链接的指针列表通常会破坏指针,但不会破坏它们指向的对象 . (这可能是正确的 . 它们可能是其他指针的引用 . )但是,专门设计用于包含指针的链表可能会删除自身销毁的对象 .
智能指针的链接列表可以在删除指针时自动删除对象,如果没有更多引用,则可以自动删除对象 . 这一切都取决于你选择你想要的东西 .
当然 . 一个例子是,如果你想用另一个相同类型的对象替换一个对象,但又不想释放内存只是为了再次分配它 . 您可以在适当的位置销毁旧对象并构建一个新对象 . (但是,通常这是一个坏主意 . )
其他人已经解决了其他问题,所以我只想看一点:你是否想要手动删除一个对象 .
答案是肯定的 . @DavidSchwartz给出了一个例子,但这是一个相当不寻常的例子 . 我在许多C程序员一直使用的东西的引导下:
std::vector
(和std::deque
,虽然它没有被使用得那么多) .正如大多数人所知,
std::vector
会在/当你添加的项目超过其当前分配所能容纳的时候分配更大的内存块 . 但是,当它执行此操作时,它具有一个内存块,能够容纳比当前在向量中更多的对象 .为了管理它,
vector
所做的是通过Allocator
对象分配原始内存(除非另有说明,否则表示它使用::operator new
) . 然后,当您使用(例如)push_back
将项添加到vector
时,向量内部使用placement new
在其内存空间的(先前)未使用部分中创建项 .现在,如果你从矢量中找到一个项目会发生什么?它不能只使用
delete
- 它会释放整个内存块;它需要销毁该内存中的一个对象而不破坏任何其他对象,或释放它控制的任何内存块(例如,如果你从一个向量中的5个项目,然后立即push_back
5个项目,它保证向量将这样做时不要重新分配内存 .为此,向量通过显式调用析构函数直接销毁内存中的对象,而不是使用
delete
.如果,另外,其他人使用连续存储来编写一个容器,就像
vector
那样(或者像std::deque
那样的某些变体),你几乎肯定想要使用相同的技术 .例如,让我们考虑如何为循环编写代码环形缓冲器 .
与标准容器不同,它直接使用
operator new
和operator delete
. 对于实际使用,您可能确实想要使用分配器类,但目前它会分散注意力而不是贡献(IMO,无论如何) .使用
new
创建对象时,您负责调用delete
. 当您使用make_shared
创建对象时,生成的shared_ptr
负责保持计数并在使用计数变为零时调用delete
.超出范围确实意味着留下一个区块 . 这是在调用析构函数时,假设该对象未使用
new
分配(即它是一个堆栈对象) .关于唯一需要显式调用析构函数的时间是使用placement new分配对象时 .
1)对象不是“通过指针”创建的 . 有一个指针分配给你'新'的任何对象 . 假设这就是你的意思,如果你在指针上调用'delete',它实际上会删除(并调用析构函数)指针取消引用的对象 . 如果将指针指定给另一个对象,则会发生内存泄漏; C中没有任何东西可以为你收集垃圾 .
2)这是两个单独的问题 . 当声明的堆栈帧从堆栈中弹出时,变量超出范围 . 通常这是你离开一个街区 . 堆中的对象永远不会超出范围,尽管它们在堆栈上的指针可能会 . 没有什么特别保证会调用链表中对象的析构函数 .
3)不是真的 . 可能会有暗魔法暗示其他情况,但通常您希望将“新”关键字与“删除”关键字相匹配,并将所有必需品放入您的析构函数中,以确保它能够正确清理自己 . 如果不这样做,请务必向使用该类的任何人详细说明析构函数,以了解如何手动清理该对象的资源 .
为了给问题3提供详细的答案:是的,有一些(罕见的)你可以明确地调用析构函数,特别是作为一个新的位置对应,就像dasblinkenlight观察到的那样 .
举一个具体的例子:
这种事情的目的是将内存分配与对象构造分离 .
Pointers - 常规指针不支持RAII . 没有明确的
delete
,就会有垃圾 . 幸运的是C有auto pointers为你处理这个!Scope - 想一想变量何时对程序不可见 . 正如你所指出的那样,通常这是在
{block}
的末尾 .Manual destruction - 永远不要尝试这个 . 让范围和RAII为您带来魔力 .
每当你使用"new",即将地址附加到指针,或者说,你在堆上声明空间时,你需要"delete"它 .
1.yes,当你删除某些东西时,会调用析构函数 .
2.当调用链表的析构函数时,会调用's objects'析构函数 . 但如果它们是指针,则需要手动删除它们 . 3.当空间被"new"声明时 .
是的,如果对象超出范围(如果它在堆栈上)或者在指向对象的指针上调用
delete
时,则会调用析构函数(a.k.a. dtor) .如果通过
delete
删除指针,则将调用dtor . 如果重新分配指针而不先调用delete
,则会出现内存泄漏,因为该对象仍然存在于内存中 . 在后一种情况下,不调用dtor .一个好的链表实现将在列表被销毁时调用列表中所有对象的dtor(因为你要么调用一些方法来破坏它,要么它超出了范围本身) . 这取决于实现 .
我对此表示怀疑,但如果有一些奇怪的情况我不会感到惊讶 .
如果不是通过指针创建对象(例如,A a1 = A();),则在对象被破坏时调用析构函数,总是在对象所在的函数完成时调用 . 例如:
当代码被执行到行"finish"时,将调用析构函数 .
如果通过指针创建对象(例如,A * a2 = new A();),则在删除指针时调用析构函数(删除a2;) . 如果该点未被用户明确删除或给定删除之前的新地址,发生内存泄漏 . 那是一个错误 .
在一个链接如果我们使用std :: list <>,我们不需要关心析构函数或内存泄漏,因为std :: list <>已经为我们完成了所有这些 . 在我们自己编写的链表中,我们应该编写析构函数并明确删除指针 . 否则会导致内存泄漏 .
我们很少手动调用析构函数 . 它是为系统提供的功能 .
抱歉我的英语不好!
请记住,在为该对象分配内存后立即调用对象的构造函数,而在释放该对象的内存之前调用析构函数 .