Dav*_*Woo 6 c++ placement-new new-operator language-lawyer
我遇到了一些让我感到恐惧的代码.基本上它遵循这种模式:
class Foo
{
public:
//default constructor
Foo(): x(0), ptr(nullptr)
{
//do nothing
}
//more interesting constructor
Foo( FooInitialiser& init): x(0), ptr(nullptr)
{
x = init.getX();
ptr = new int;
}
~Foo()
{
delete ptr;
}
private:
int x;
int* ptr;
};
void someFunction( FooInitialiser initialiser )
{
int numFoos = MAGIC_NUMBER;
Foo* fooArray = new Foo[numFoos]; //allocate an array of default constructed Foo's
for(int i = 0; i < numFoos; ++i)
{
new( fooArray+ i) Foo( initialiser ); //use placement new to initialise
}
//... do stuff
delete[] fooArray;
}
Run Code Online (Sandbox Code Playgroud)
这段代码多年来一直在代码库中,似乎从来没有引起过任何问题.这显然是一个坏主意,因为有人可以更改默认构造函数以分配不期望第二个构造.简单地用等效的初始化方法替换第二个构造函数似乎是明智的做法.例如.
void Foo::initialise(FooInitialiser& init)
{
x = init.getX();
ptr = new int;
}
Run Code Online (Sandbox Code Playgroud)
尽管仍然存在可能的资源泄漏,但至少一名防御性程序员可能会考虑以正常方法检查先前的分配.
我的问题是:
正在构建两次这样的实际未定义的行为/标准禁止或仅仅是一个坏主意?如果未定义的行为可以引用或指向我正确的位置来查看标准?
Mic*_*nda 10
一般来说,以这种方式处理新位置并不是一个好主意.从第一个新的调用初始化程序,或调用初始化程序而不是新的位置都被认为是比您提供的代码更好的形式.
但是,在这种情况下,很好地定义了在现有对象上调用new的行为.
程序可以通过重用对象占用的存储来结束任何对象的生命周期,或者通过使用非平凡的析构函数显式调用类类型的对象的析构函数来结束任何对象的生命周期.对于具有非平凡析构函数的类类型的对象,程序不需要在重用或释放对象占用的存储之前显式调用析构函数; 但是,如果没有显式调用析构函数或者如果没有使用delete-expression(5.3.5)来释放存储,则不应该隐式调用析构函数,并且任何程序都依赖于析构函数产生的副作用有未定义的行为.
所以发生这种情况时:
Foo* fooArray = new Foo[numFoos]; //allocate an array of default constructed Foo's
for(int i = 0; i < numFoos; ++i)
{
new( fooArray+ i) Foo( initialiser ); //use placement new to initialise
}
Run Code Online (Sandbox Code Playgroud)
放置新操作将结束Foo
那里的生命周期,并在其中创建一个新操作.在许多情况下这可能很糟糕,但鉴于你的析构函数的工作方式,这将是好的.
在现有对象上调用new new可能是未定义的行为,但它取决于特定对象.
这不会产生未定义的行为,因为您不依赖于析构函数产生的"副作用".
对象的析构函数中唯一的"副作用"是delete
包含int
指针,但在这种情况下,new
调用放置时该对象永远不会处于可删除状态.
如果包含的int
指针可能等于除了nullptr
并且可能需要删除之外的其他内容,则调用new
现有对象上的放置将调用未定义的行为.