Pat*_*ray 100 c++ destructor
基本问题:程序何时在C++中调用类的析构函数方法?有人告诉我,只要一个物体超出范围或受到一个物体的影响就会被召唤delete
更具体的问题:
1)如果通过指针创建对象并且稍后删除该指针或给出指向的新地址,那么它所指向的对象是否会调用其析构函数(假设没有其他指向它)?
2)关注问题1,什么定义了一个对象何时超出范围(不是关于对象何时离开给定的{block}).那么,换句话说,什么时候是一个析构函数调用链表中的对象?
3)你想要手动调用析构函数吗?
Dav*_*rtz 66
1)如果通过指针创建对象并且稍后删除该指针或给出指向的新地址,那么它所指向的对象是否会调用其析构函数(假设没有其他指向它)?
这取决于指针的类型.例如,智能指针通常在删除对象时删除它们.普通指针没有.当指针指向不同的对象时也是如此.一些智能指针会破坏旧对象,或者如果它没有更多引用就会销毁它.普通指针没有这样的智慧.它们只保存一个地址,并允许您通过专门执行操作对它们指向的对象执行操作.
2)关注问题1,什么定义了一个对象何时超出范围(不是关于对象何时离开给定的{block}).那么,换句话说,什么时候是一个析构函数调用链表中的对象?
这取决于链表的实现.典型的集合在销毁时会销毁所有包含的对象.
因此,链接的指针列表通常会破坏指针,但不会破坏它们指向的对象.(这可能是正确的.它们可能是其他指针的引用.)但是,专门设计用于包含指针的链表可能会删除自身销毁的对象.
智能指针的链接列表可以在删除指针时自动删除对象,如果没有更多引用,则可以自动删除对象.这一切都取决于你选择你想要的东西.
3)你想要手动调用析构函数吗?
当然.一个例子是,如果你想用另一个相同类型的对象替换一个对象,但又不想释放内存只是为了再次分配它.您可以在适当的位置销毁旧对象并构建一个新对象.(但是,通常这是一个坏主意.)
// 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");
}
Run Code Online (Sandbox Code Playgroud)
Jer*_*fin 15
其他人已经解决了其他问题,所以我只想看一点:你是否想要手动删除一个对象.
答案是肯定的.@DavidSchwartz给出了一个例子,但这是一个相当不寻常的例子.我将举例说明许多C++程序员一直使用的内容:( std::vector
并且std::deque
,尽管它没有那么多使用).
正如大多数人所知,std::vector
当你添加的项目多于当前分配所能容纳的项目时,会分配更大的内存块.但是,当它执行此操作时,它具有一个内存块,能够容纳比当前在向量中更多的对象.
为了管理这个问题,有些vector
内容是通过对象分配原始内存Allocator
(除非另有说明,否则表示它使用::operator new
).然后,当您使用(例如)push_back
将项添加到vector
内部时,向量使用a placement new
在其内存空间的(先前)未使用部分中创建项.
现在,如果你erase
是一个来自向量的项目会发生什么?它不能只使用delete
- 这将释放其整个内存块; 它需要销毁该内存中的一个对象而不破坏任何其他对象,或者释放它控制的任何内存块(例如,如果你erase
从向量中获取push_back
5个项目,那么立即再添加5个项目,则保证向量不会重新分配你这样做的记忆.
为此,向量通过显式调用析构函数直接销毁内存中的对象,而不是使用delete
.
如果,其他人使用连续存储来写一个容器,大致就像一个vector
(或者其中一些变体,就像std::deque
真的一样),你几乎肯定想要使用相同的技术.
例如,让我们考虑如何为循环环形缓冲区编写代码.
#ifndef CBUFFER_H_INC
#define CBUFFER_H_INC
template <class T>
class circular_buffer {
T *data;
unsigned read_pos;
unsigned write_pos;
unsigned in_use;
const unsigned capacity;
public:
circular_buffer(unsigned size) :
data((T *)operator new(size * sizeof(T))),
read_pos(0),
write_pos(0),
in_use(0),
capacity(size)
{}
void push(T const &t) {
// ensure there's room in buffer:
if (in_use == capacity)
pop();
// construct copy of object in-place into buffer
new(&data[write_pos++]) T(t);
// keep pointer in bounds.
write_pos %= capacity;
++in_use;
}
// return oldest object in queue:
T front() {
return data[read_pos];
}
// remove oldest object from queue:
void pop() {
// destroy the object:
data[read_pos++].~T();
// keep pointer in bounds.
read_pos %= capacity;
--in_use;
}
// release the buffer:
~circular_buffer() { operator delete(data); }
};
#endif
Run Code Online (Sandbox Code Playgroud)
不同于标准集装箱,这使用operator new
和operator delete
直接.对于实际使用,您可能确实想要使用分配器类,但目前它会分散注意力而不是贡献(IMO,无论如何).
1) 对象不是“通过指针”创建的。有一个指针分配给您“新建”的任何对象。假设这就是您的意思,如果您在指针上调用“删除”,它实际上会删除(并在其上调用析构函数)指针取消引用的对象。如果将指针分配给另一个对象,则会出现内存泄漏;C++ 中的任何内容都不会为您收集垃圾。
2) 这是两个不同的问题。当变量在其中声明的堆栈帧从堆栈中弹出时,它就会超出范围。通常这是你离开一个街区的时候。堆中的对象永远不会超出范围,尽管它们在堆栈上的指针可能会。没有特别保证会调用链表中对象的析构函数。
3) 不是真的。Deep Magic 可能会提出不同的建议,但通常您希望将“新”关键字与“删除”关键字相匹配,并将所有必要的内容放入析构函数中,以确保它正确地自我清理。如果您不这样做,请务必向使用该类的任何人说明如何手动清理该对象的资源,并使用特定说明对析构函数进行注释。