通过placement-new手动构建一个简单的基类

时间:2017-03-22 09:12:06

标签: c++ language-lawyer object-lifetime placement-new

当心,我们正在绕过龙的巢穴。

考虑以下两个类:

struct Base {
    std::string const *str;
};

struct Foo : Base {
    Foo() { std::cout << *str << "\n"; }
};

如您所见,我正在访问未初始化的指针。还是我?

我们假设我只使用trivialBase个类,只不过是(可能是嵌套的)指针。

static_assert(std::is_trivial<Base>{}, "!");

我想通过三个步骤构建Foo

  1. Foo

  2. 分配原始存储空间
  3. 通过placement-new初始化一个放置合适的Base子对象

  4. 通过placement-new构建Foo

  5. 我的实施如下:

    std::unique_ptr<Foo> makeFooWithBase(std::string const &str) {
    
        static_assert(std::is_trivial<Base>{}, "!");
    
        // (1)
        auto storage = std::make_unique<
            std::aligned_storage_t<sizeof(Foo), alignof(Foo)>
        >();
    
        Foo * const object = reinterpret_cast<Foo *>(storage.get());
        Base * const base = object;
    
        // (2)
        new (base) Base{&str};
    
        // (3)
        new (object) Foo(); 
    
        storage.release();
        return std::unique_ptr<Foo>{object};
    }
    

    由于Base是微不足道的,我的理解是:

    • 跳过在Base构建的(2)的普通析构函数很好;

    • 作为Base Foo (3)的一部分构建的Foo子对象的普通默认构造函数不执行任何操作;

    所以 <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/> <uses-permission android:name="android.permission.android.permission.ACCESS_NETWORK_STATE"/> 收到一个初始化的指针,一切都很好。

    当然,这就是实践中发生的事情,即使在-O3(see for yourself!) 但它是安全的,还是龙有一天会抓住并吃掉我?

1 个答案:

答案 0 :(得分:8)

标准似乎明确禁止。 结束对象生命周期,并启动新对象 明确允许在同一位置的生命周期, 除非它是基类:

  

§3.8对象生命周期

     

§3.8.7 - 如果在对象的生命周期结束之后和存储之前   对象占用的是重用或释放的,一个新的对象是   在原始对象占用的存储位置创建,a   指向原始对象的指针,引用的引用   到原始对象,或原始对象的名称   自动引用新对象,一旦生命周期   新对象已启动,可用于操作新对象, if

     
      
  • 新对象的存储空间正好覆盖存储位置   原始对象占用了哪个,

  •   
  • 新对象属于   与原始对象相同的类型(忽略顶级   cv-qualifiers)和

  •   
  • [snip]和

  •   
  • 原始对象是类型为T的最派生对象(1.8)   新对象是T类型的最派生对象(也就是说,它们不是   基类子对象)。

  •