当心,我们正在绕过龙的巢穴。
考虑以下两个类:
struct Base {
std::string const *str;
};
struct Foo : Base {
Foo() { std::cout << *str << "\n"; }
};
如您所见,我正在访问未初始化的指针。还是我?
我们假设我只使用trivial的Base
个类,只不过是(可能是嵌套的)指针。
static_assert(std::is_trivial<Base>{}, "!");
我想通过三个步骤构建Foo
:
为Foo
通过placement-new初始化一个放置合适的Base
子对象
通过placement-new构建Foo
。
我的实施如下:
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!) 但它是安全的,还是龙有一天会抓住并吃掉我?
答案 0 :(得分:8)
标准似乎明确禁止。 结束对象生命周期,并启动新对象 明确允许在同一位置的生命周期, 除非它是基类:
§3.8对象生命周期
§3.8.7 - 如果在对象的生命周期结束之后和存储之前 对象占用的是重用或释放的,一个新的对象是 在原始对象占用的存储位置创建,a 指向原始对象的指针,引用的引用 到原始对象,或原始对象的名称 自动引用新对象,一旦生命周期 新对象已启动,可用于操作新对象, if :
新对象的存储空间正好覆盖存储位置 原始对象占用了哪个,
新对象属于 与原始对象相同的类型(忽略顶级 cv-qualifiers)和
[snip]和
原始对象是类型为T的最派生对象(1.8) 新对象是T类型的最派生对象(也就是说,它们不是 基类子对象)。