这是我的代码示例:
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;
}
}
我的问题是,这是一种安全的方法吗?在某种情况下我会犯一些隐藏的错误吗?它适用于每种类型的物体(POD和非POD)吗?
答案 0 :(得分:7)
不,因为你的构造函数和析构函数都被调用了两次。因为你有这个:
template <typename T> struct MyStruct {
T object;
}
构造MyStruct<T>
时,编译将构造内部T
,当您删除对象时,内部T
将自动调用析构函数。
对于此示例,不需要放置新的或显式的析构函数调用。
如果您分配原始内存,Placement new将非常有用。例如,如果您将新内容更改为:
this->structPool = new char[sizeof(T) * count];
然后你想要放置new和explict析构函数调用。
答案 1 :(得分:1)
不,这肯定不是远程安全的方法。对非POD new MyStruct<T>[count]
执行T
时,数组中的每个MyStruct<T>
对象都已经默认构造,这意味着object
成员的构造函数会被自动调用。然后,您尝试在其上执行就地构造(通过值初始化)。结果行为未定义。
删除存在同样的问题。
你想要实现的目标是什么?只需执行new MyStruct<T>[count]()
(注意额外的空()
),它就已经为数组的每个元素执行了值初始化(正是你之后“手动”执行的操作)。为什么你觉得你必须通过就地施工来做到这一点?
同样,当你做
时delete[] this->structPool;
它会自动为数组中的每个MyStruct<T>::object
成员调用析构函数。无需手动完成。
答案 2 :(得分:0)
- 所以你的代码使用了两次。这将调用构造函数两次。如果你想避免这种情况,请:
将您的第一个新内容更改为malloc(或任何类型的分配)
删除您的第二个展示位置
- 所以你可以这样做:
如果您使用新的[]
,请使用删除[]删除对象如果您使用malloc和placement new
,请调用每个析构函数并自由地执行C样式答案 3 :(得分: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 lib实现中查看std::vector
,以了解如何正确执行此操作。但是,这导致了一个问题: 为什么要首先执行此操作?
std::vector
已经做到了这一切,做对了,你可以开箱即用,每个看你代码的人都会立即理解它:
template <typename T> class MyClass {
std::vector<T> data_;
MyClass(size_t count) : data() {data.resize(count);}
//~MyClass() not needed
}