1

这是我的代码示例:

template <typename T> struct MyStruct {
    T object;
}

template <typename T> class MyClass {
    MyStruct<T>* structPool;
    size_t structCount;

    MyClass(size_t count) {
        this->structCount = count;
        this->structPool  = new MyStruct<T>[count];
        for( size_t i=0 ; i<count ; i++ ) {
            //placement new to call constructor
            new (&this->structPool[i].object) T(); 
        }
    }

    ~MyClass() {
        for( size_t i=0 ; i<this->structCount ; i++ ) {
            //explicit destructor call
            this->structPool[i].object.~T(); 
        }
        delete[] this->structPool;
    }
}

我的问题是,这是一种安全的方法吗?我是否在某些情况下犯了一些隐藏的错误?它适用于每种类型的对象(PO​​D 和非 POD)吗?

4

4 回答 4

7

不,因为您的构造函数和析构函数都被调用了两次。因为你有这个:

template <typename T> struct MyStruct {
    T object;
}

当您构造 a 时MyStruct<T>,编译将构造内部T,当您删除对象时,内部T将自动调用析构函数。

对于这个例子,不需要放置 new 或显式的析构函数调用。

如果您分配原始内存,则放置 new 将很有用。例如,如果您将新的更改为:

this->structPool  = new char[sizeof(T) * count];

那么你会想要放置新的和显式的析构函数调用。

于 2010-06-07T23:41:22.977 回答
1

不,这当然不是远程安全的方法。当您new MyStruct<T>[count]为 non-POD执行此操作时T,数组中的每个MyStruct<T>对象都已默认构造,这意味着该object成员的构造函数会自动调用。然后,您尝试在此之上执行就地构造(通过值初始化)。结果行为是未定义的。

删除也存在同样的问题。

你想达到什么目的?只需这样做new MyStruct<T>[count]()(注意额外的 empty ()),它就会为数组的每个元素执行值初始化(这正是您之后尝试“手动”执行的操作)。为什么你觉得你必须通过就地施工来做到这一点?

同样,当你这样做

delete[] this->structPool;

MyStruct<T>::object它会自动为数组中的每个成员调用析构函数。无需手动进行。

于 2010-06-07T23:44:03.137 回答
0
  1. 记住 new 将始终调用构造函数,无论它是否是放置。

- 所以你的代码使用了两次 new 。这将调用构造函数两次。如果你想避免这种情况,要么:

将您的第一个 new 更改为 malloc(或任何类型的 alloc)

删除您的第二个展示位置新

  1. 要删除数组中的对象,最好的方法是:调用每个对象的析构函数;释放内存。

-- 所以你可以做:

如果您使用的是 new[],请使用 delete[] 删除对象

如果您使用 malloc 和放置 new,请调用每个析构函数并免费执行 C 样式

于 2010-06-07T23:48:37.830 回答
0
template <typename T> class MyClass {
    void* structPool;
    size_t structCount;

    MyClass(size_t count)
      : structPool(new char[sizeof(T)*count], structCount(count)
    {
        //placement new to call constructor
        for( size_t i=0 ; i<count ; i++ )
            new (structPool+i*sizeof(T)) T(); 
    }

    ~MyClass() {
        //explicit destructor call
        for( size_t i=0 ; i<structCount ; i++ )
            reinterpret_cast<T*>(structPool+i*sizeof(T))->~T(); 
        delete[] structPool;
    }
}

请注意,这不是异常安全的:如果其中一个构造函数导致异常,它不会调用已构造对象的析构函数,并且会泄漏内存。当其中一个析构函数抛出时,这也会失败。

查看std::vector您最喜欢的 std lib 实现,以了解如何正确执行此操作。然而,这就引出了一个问题:你为什么要首先这样做?
Astd::vector已经做了这一切,做的对,你可以开箱即用,每个看你的代码的人都会立刻明白:

template <typename T> class MyClass {
    std::vector<T> data_;
    MyClass(size_t count) : data() {data.resize(count);}
    //~MyClass() not needed
}
于 2010-06-08T07:05:25.050 回答