2011-02-04 78 views
3

以下代码仅用于说明我的问题。显式析构函数

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<string> arr在main.cpp中。当我使用erase(5)时,element[5]的对象被破坏,但是element[5]的空间不会被释放,我可以使用element[5] = "abc"在这里放置一个新值吗?或者我是否必须使用贴装新功能将新的价值放在element [5]的空间中?

程序结束时,arr<string>将调用它自己的析构函数,该函数也调用delete [] element。因此,字符串的析构函数将运行以首先销毁该对象,然后释放该空间。但是因为我已经明确地破坏了element[5],那么析构函数(被arr的destuctor调用)运行两次以破坏element[5]?我知道这个空间不能被释放两次,这个对象怎么样?我做了一些测试,发现它看起来很好,如果我只是破坏对象两次而不是释放空间两次。

更新

的答案是:

(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.

+1

这是一个不好的方法。调用析构函数两次为未定义的行为,其中“看起来很好”是完全可以接受的结果。不过,这并不好。无论如何,尽管阵列中的“擦除”对我来说毫无意义。 – GManNickG 2011-02-04 02:03:02

+1

我同意@GMan:从数组中删除元素意味着什么?你想模仿C++`std :: vector`实现吗?如果是这样,你是否将删除元素之后的所有元素移动到“填充空洞”? – 2011-02-04 02:05:42

+0

@ Gman:“擦除”对我来说也没什么意义。 – Sean 2011-02-04 02:22:04

回答

6

您需要使用与放置新的语法:

new (element + 5) string("abc"); 

element[5] = "abc"将是不正确的;这将调用 element[5],这不是一个有效的对象,产生未定义的行为。

程序结束时,arr<string>将调用其自身的析构函数,该函数也称为delete [] element

这是错误的:您将最终调用已析构函数已被调用的对象的析构函数(例如,上述示例中的elements[5])。这也产生未定义的行为。

考虑使用std::allocator及其接口。它使您可以轻松地将施工分配与施工分开。它由C++标准库容器使用。

2

只是为了进一步UD究竟如何可能咬你解释....

如果erase()一个元素 - 明确调用析构函数 - 那析构函数做这样的事情递减引用计数器+清理,删除指针等等。当你的数组解析器然后做一个delete[] element,它将依次调用每个元素上的析构函数,而对于被擦除的元素,这些析构函数可能会重复引用计数维护,指针删除等,但是这次是初始化状态并不像他们期望的那样,他们的行为很可能会使程序崩溃。因此,正如Ben在他对詹姆斯的回答的评论中所说的那样,在数组的析构函数被调用之前,你绝对必须用一个已擦除的元素 - 使用放置新元素 - 取代,因此析构函数将有一些合法的状态,破坏。

说明这一问题的T最简单的类型是:

struct T 
{ 
    T() : p_(new int) { } 
    ~T() { delete p_; } 
    int* p_; 
}; 

这里,p_值由new设置将一个erase()期间被删除,并且如果不变时~array()运行。要解决这个问题,必须将p_更改为delete~array()之前有效的内容 - 以某种方式将其清除为0或由new返回的另一个指针。最明智的做法是放置new,它将构建一个新对象,获取p_的新有效值,覆盖旧的无用内存内容。也就是说,你可能会认为你可以构造重复析构函数是安全的类型:例如,在delete后面设置p_为0。这可能在大多数系统上都能正常工作,但我很肯定标准中说有两次调用析构函数是UD。