显式析构函数
下面的代码只是用来说明我的问题。
template<class T>
class array<T>
{
public:
// constructor
array(cap = 10):capacity(cap)
{element = new T [capacity]; size =0;}
// destructor
~array(){delete [] element;}
void erase(int i);
private:
T *element;
int capacity;
int size;
};
template<class T>
void class array<T>::erase(int i){
// copy
// destruct object
element[i].~T(); ////
// other codes
}
如果我有 array
erase(5)
时,element[5]
的对象被销毁,但element[5]
的空间不会被释放,我可以使用 element[5] = "abc"
在这里输入一个新值吗?或者我应该使用placement new 将新值放入element [5]
的空间中?
当程序结束时,arr
将调用它自己的析构函数,该析构函数也调用delete [] element
。所以string的析构函数会先销毁对象,然后释放空间。但由于我已经显式地析构了 element[5]
,析构函数(由 arr 的析构函数调用)运行两次来析构 element[5]
有关系吗?我知道空间不能被释放两次,那么对象呢?我做了一些测试,发现如果我只是破坏对象两次而不是释放空间两次似乎没问题。
更新
答案是:
(1)如果我显式调用析构函数,则必须使用placement new。
(2) 重复析构对象被定义为未定义的行为,大多数系统都可以接受,但应尽量避免这种做法。
The following code is just used to illustrate my question.
template<class T>
class array<T>
{
public:
// constructor
array(cap = 10):capacity(cap)
{element = new T [capacity]; size =0;}
// destructor
~array(){delete [] element;}
void erase(int i);
private:
T *element;
int capacity;
int size;
};
template<class T>
void class array<T>::erase(int i){
// copy
// destruct object
element[i].~T(); ////
// other codes
}
If I have array<string> arr
in main.cpp. When I use erase(5)
, the object of element[5]
is destroyed but the space of element[5]
will not be deallocated, can I just use element[5] = "abc"
to put a new value here? or should I have to use placement new to put new value in the space of element [5]
?
When program ends, the arr<string>
will call its own destructor which also calls delete [] element
. So the destructor of string will run to destroy the object first and then free the space. But since I have explicitly destruct the element[5]
, does that matter the destructor (which is called by the arr's destuctor) run twice to destruct element[5]
? I know the space can not be deallocated twice, how about the object? I made some tests and found it seems fine if I just destruct object twice instead of deallocating space twice.
Update
The answers are:
(1)I have to use placement new if I explicitly call destructor.
(2) repeatedly destructing object is defined as undefined behavior which may be accepted in most systems but should try to avoid this practice.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您需要使用placement-new语法:
element[5] = "abc"
; 是不正确的;这将调用element[5]
上的operator=
,它不是一个有效的对象,从而产生未定义的行为。这是错误的:您最终将调用已调用析构函数的对象的析构函数(例如,上述示例中的
elements[5]
)。这也会产生未定义的行为。考虑使用 std::allocator 及其接口。它使您可以轻松地将分配与构造分开。它由 C++ 标准库容器使用。
You need to use the placement-new syntax:
It would be incorrect to say
element[5] = "abc"
; this would invokeoperator=
onelement[5]
, which is not a valid object, yielding undefined behavior.This is wrong: you are going to end up calling the destructor for objects whose destructors have already been called (e.g.,
elements[5]
in the aforementioned example). This also yields undefined behavior.Consider using the
std::allocator
and its interface. It allows you easily to separate allocation from construction. It is used by the C++ Standard Library containers.只是为了进一步准确地解释 UD 可能会如何咬你......
如果你
erase()
一个元素 - 显式调用析构函数 - 该析构函数可能会执行诸如减少引用计数器 + 清理、删除之类的操作指针等..当你的数组<>然后析构函数执行一个删除[]元素,它将依次调用每个元素的析构函数,对于擦除的元素,这些析构函数可能会重复它们的引用计数维护、指针删除等,但这一次初始状态并不像他们预期的那样,他们的行为可能会导致程序崩溃。因此,正如 Ben 在对 James 的回答的评论中所说,在调用数组的析构函数之前,您绝对必须使用放置 new 替换已删除的元素,这样析构函数将具有一些可以从中进行析构的合法状态。
说明此问题的最简单的
T
类型是:这里,由
new
设置的p_
值将在erase( )
,并且如果~array()
运行时未发生变化。要解决此问题,必须将p_
更改为在~array()
之前delete
有效的内容 - 通过某种方式将其清除为 0 或指向 new 返回的另一个指针。最明智的方法是通过放置new
,它将构造一个新对象,为p_
获取新的有效值,覆盖旧的无用的内存内容。也就是说,您可能认为可以构造重复析构函数安全的类型:例如,通过在
delete
之后将p_
设置为 0。这可能适用于大多数系统,但我很确定标准中有一些内容说调用析构函数两次是 UD 无论如何。Just to explain further exactly how the UD is likely to bite you....
If you
erase()
an element - explicitly invoking the destructor - that destructor may do things like decrement reference counters + cleanup, delete pointers etc.. When your array<> destructor then does adelete[] element
, that will invoke the destructors on each element in turn, and for erased elements those destructors are likely to repeat their reference count maintenance, pointer deletion etc., but this time the initial state isn't as they expect and their actions are likely to crash the program.For that reason, as Ben says in his comment on James' answer, you absolutely must have replaced an erased element - using placement new - before the array's destructor is invoked, so the destructor will have some legitimate state from which to destruct.
The simplest type of
T
that illustrates this problem is:Here, the
p_
value set bynew
would be deleted during anerase()
, and if unchanged when~array()
runs. To fix this,p_
must be changed to something for whichdelete
is valid before~array()
- either by somehow clearing it to 0 or to another pointer returned bynew
. The most sensible way to do that is by the placementnew
, which will construct a new object, obtaining a new and valid value forp_
, overwriting the old and useless memory content.That said, you might think you could construct types for which repeated destructor was safe: for example, by setting
p_
to 0 after thedelete
. That would probably work on most systems, but I'm pretty sure there's something in the Standard saying to invoke the destructor twice is UD regardless.