C++ 安全放置新&;显式析构函数调用

C++ 安全放置新&;显式析构函数调用,c++,constructor,destructor,placement-new,C++,Constructor,Destructor,Placement New,这是我的代码示例: 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;

这是我的代码示例:

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;
    }
}
模板结构MyStruct{
T对象;
}
模板类MyClass{
MyStruct*structPool;
大小/结构计数;
我的班级(人数){
此->结构计数=计数;
this->structPool=newmystruct[count];
对于(size_t i=0;istructPool[i].object)t();
}
}
~MyClass(){
对于(大小i=0;istructCount;i++){
//显式析构函数调用
this->structPool[i].object.~T();
}
删除[]此->结构池;
}
}

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

否,因为构造函数和析构函数都会被调用两次。因为你有:

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

然后,您需要放置new和explict析构函数调用。

否,因为构造函数和析构函数都被调用了两次。因为你有:

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

然后,您可能会想要放置新的、明确的析构函数调用。

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

删除也存在同样的问题

你想达到什么目的?只需执行
newmystruct[count]()
(注意额外的空
()
),它就已经为数组的每个元素执行了值初始化(这正是您之后尝试“手动”执行的操作)。为什么您觉得必须通过就地施工来完成

同样,当你这样做的时候

delete[] this->structPool;

它会自动为数组中的每个
MyStruct::object
成员调用析构函数。不需要手动操作。

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

删除也存在同样的问题

你想达到什么目的?只需执行
newmystruct[count]()
(注意额外的空
()
),它就已经为数组的每个元素执行了值初始化(这正是您之后尝试“手动”执行的操作)。为什么您觉得必须通过就地施工来完成

同样,当你这样做的时候

delete[] this->structPool;
它会自动为数组中的每个
MyStruct::object
成员调用析构函数。不需要手动操作

  • 记住new将始终调用构造函数,不管它是否是placement
  • --所以你的代码使用了两次new。这将调用构造函数两次。如果要避免这种情况,请执行以下任一操作:

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

    删除第二个新位置

    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;
        }
    }
    
  • 要删除数组中的对象,最好的方法是:调用每个对象的析构函数;释放内存
  • --因此,您可以执行以下任一操作:

    如果使用的是新[],请使用Delete[]删除对象

    如果使用malloc和placement new,则调用每个析构函数并执行C风格的free

    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;
        }
    }
    
  • 记住new将始终调用构造函数,不管它是否是placement
  • --所以你的代码使用了两次new。这将调用构造函数两次。如果要避免这种情况,请执行以下任一操作:

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

    删除第二个新位置

    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;
        }
    }
    
  • 要删除数组中的对象,最好的方法是:调用每个对象的析构函数;释放内存
  • --因此,您可以执行以下任一操作:

    如果使用的是新[],请使用Delete[]删除对象


    如果您正在使用malloc和placement new,则调用每个析构函数并执行一个C风格的free

    if
    this->structPool=new MyStruct[count]如何替换为
    this->structPool=(MyStruct*)malloc(sizeof(MyStruct)*count)。在这种情况下,我们需要放置新的显式析构函数?是的,您需要这样做,但您需要为MyStruct本身调用构造函数/析构函数,而不是为MyString.object调用构造函数/析构函数。@uray-是的,如果您使用malloc获取原始内存,则需要放置新的/显式析构函数。也就是说,看起来你是在寻找使用placement new/explicit析构函数的理由,而不是真正需要它。@Samuel:哈哈,不,我不是在寻找理由,我只是想确保我的理解是正确的。@uray-明白了。我的建议是去掉
    MyStruct
    ,因为这会混淆问题。如果
    this->structPool=newmystruct[count]替换为
    this->structPool=(MyStruct*)malloc(sizeof(MyStruct)*count)。在这种情况下,我们需要放置新的显式析构函数?是的,您需要这样做,但您需要为MyStruct本身调用构造函数/析构函数,而不是为MyString.object调用构造函数/析构函数。@uray-是的,如果您使用malloc获取原始内存,则需要放置新的/显式析构函数。也就是说,看起来你是在寻找使用placement new/explicit析构函数的理由,而不是真正需要它。@Samuel:哈哈,不,我不是在寻找理由,我只是想确保我的理解是正确的。@uray-明白了。我的建议是去掉
    MyStruct
    ,因为这会混淆问题。如果
    T
    的构造函数需要参数来构造,
    T
    具有
    T()
    T(arg,…)
    构造函数,但我需要在构造过程中提供参数,该怎么办?我可以继续吗
    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;
        }
    }