将 placement new 与存储一起使用时的额外构造 class
Extra construction when using placement new with a storage class
在我想避免动态内存分配的情况下,我将 new 运算符替换为一个进程,该进程本质上使用一些静态分配的对象的内存(下面的 Storage
class ).您可以在下面看到一个最小的工作示例:
#include <cassert>
#include <iostream>
struct Object {
Object() { std::cout << "Creating a new object\n"; }
static void *operator new(size_t);
static void operator delete(void *p);
};
static struct {
Object where;
bool allocated = false;
} Storage; // 1
void *Object::operator new(size_t) {
assert(!Storage.allocated);
auto p = ::new (&Storage.where) Object; // 2
Storage.allocated = true;
return p;
}
void Object::operator delete(void *p) {
assert(Storage.allocated);
static_cast<Object *>(p)->~Object();
Storage.allocated = false;
}
int main() { Object *obj = new Object; } // 3
我的问题与构造函数的调用次数有关。当我 run 上面的程序时,我希望调用构造函数两次(在上面的注释中标记为 1 和 2)但是我得到的输出是:
Creating a new object
Creating a new object
Creating a new object
为什么构造函数被调用了三次?我只希望通过静态对象调用构造函数和调用 placement new。我尝试使用 gdb 跟踪代码,但这对我来说毫无意义,因为位置 //3
是 where
对构造函数的第三次调用。
我想知道的原因是因为出现了一个案例,这个额外的构造函数调用会导致不需要的副作用;到目前为止,这个额外的调用没有被注意到。
Object *obj = new Object;
做了两件事:
通过调用operator new
分配内存
调用构造函数。
你的operator new
也调用了构造函数,所以这个语句调用了两次构造函数(一次是全局变量初始化)。
注意delete
是一样的。 delete obj;
做了两件事:
调用析构函数。
通过调用 operator delete
释放内存
你的operator delete
也不应该调用析构函数,因为这样析构函数就会被调用两次。
出于某些奇怪的原因,您的 operator new
在应该分配内存时调用了构造函数。这意味着对 new
的调用最终会调用 Object
的构造函数两次。在 operator new
中有一个调用,在 main
中有另一个调用。
你可能想要这个:
void *Object::operator new(size_t) {
assert(!Storage.allocated);
Storage.allocated = true;
return reinterpret_cast<void *> (&Storage.where);
}
想象一下,如果构造函数采用整数参数并且 main
中的行看起来像这样:
Object *obj = new Object(7);
operator new
怎么知道如何正确构造对象?那不是你应该做的地方!
在我想避免动态内存分配的情况下,我将 new 运算符替换为一个进程,该进程本质上使用一些静态分配的对象的内存(下面的 Storage
class ).您可以在下面看到一个最小的工作示例:
#include <cassert>
#include <iostream>
struct Object {
Object() { std::cout << "Creating a new object\n"; }
static void *operator new(size_t);
static void operator delete(void *p);
};
static struct {
Object where;
bool allocated = false;
} Storage; // 1
void *Object::operator new(size_t) {
assert(!Storage.allocated);
auto p = ::new (&Storage.where) Object; // 2
Storage.allocated = true;
return p;
}
void Object::operator delete(void *p) {
assert(Storage.allocated);
static_cast<Object *>(p)->~Object();
Storage.allocated = false;
}
int main() { Object *obj = new Object; } // 3
我的问题与构造函数的调用次数有关。当我 run 上面的程序时,我希望调用构造函数两次(在上面的注释中标记为 1 和 2)但是我得到的输出是:
Creating a new object
Creating a new object
Creating a new object
为什么构造函数被调用了三次?我只希望通过静态对象调用构造函数和调用 placement new。我尝试使用 gdb 跟踪代码,但这对我来说毫无意义,因为位置 //3
是 where
对构造函数的第三次调用。
我想知道的原因是因为出现了一个案例,这个额外的构造函数调用会导致不需要的副作用;到目前为止,这个额外的调用没有被注意到。
Object *obj = new Object;
做了两件事:
通过调用
operator new
分配内存
调用构造函数。
你的operator new
也调用了构造函数,所以这个语句调用了两次构造函数(一次是全局变量初始化)。
注意delete
是一样的。 delete obj;
做了两件事:
调用析构函数。
通过调用
operator delete
释放内存
你的operator delete
也不应该调用析构函数,因为这样析构函数就会被调用两次。
出于某些奇怪的原因,您的 operator new
在应该分配内存时调用了构造函数。这意味着对 new
的调用最终会调用 Object
的构造函数两次。在 operator new
中有一个调用,在 main
中有另一个调用。
你可能想要这个:
void *Object::operator new(size_t) {
assert(!Storage.allocated);
Storage.allocated = true;
return reinterpret_cast<void *> (&Storage.where);
}
想象一下,如果构造函数采用整数参数并且 main
中的行看起来像这样:
Object *obj = new Object(7);
operator new
怎么知道如何正确构造对象?那不是你应该做的地方!