explicit destructor
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 ha开发者_运维知识库ve 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.
You need to use the placement-new syntax:
new (element + 5) string("abc");
It would be incorrect to say element[5] = "abc"
; this would invoke operator=
on element[5]
, which is not a valid object, yielding undefined behavior.
When program ends, the
arr<string>
will call its own destructor which also callsdelete [] element
.
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.
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 a delete[] 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:
struct T
{
T() : p_(new int) { }
~T() { delete p_; }
int* p_;
};
Here, the p_
value set by new
would be deleted during an erase()
, and if unchanged when ~array()
runs. To fix this, p_
must be changed to something for which delete
is valid before ~array()
- either by somehow clearing it to 0 or to another pointer returned by new
. The most sensible way to do that is by the placement new
, which will construct a new object, obtaining a new and valid value for p_
, 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 the delete
. 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.
精彩评论