将placement new 与存储类一起使用时的额外构造
Posted
技术标签:
【中文标题】将placement new 与存储类一起使用时的额外构造【英文标题】:Extra construction when using placement new with a storage class 【发布时间】:2020-01-21 10:18:35 【问题描述】:在我想避免动态内存分配的情况下,我将 new 运算符替换为本质上使用某些静态分配对象的内存的进程(下面的 Storage
类)。您可以在下面看到一个最小的工作示例:
#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 上述程序时,我希望调用构造函数两次(在上面的 cmets 中标记为 1 和 2)但我得到的输出是:
创建一个新对象
创建一个新对象
创建一个新对象
为什么构造函数被调用三次?我只希望通过静态对象和对放置 new 的调用来调用构造函数。我尝试使用 gdb 跟踪代码,但这对我来说毫无意义,因为位置 //3
是 where
对构造函数的第三次调用。
我想知道的原因是因为出现了一个案例,这个额外的构造函数调用会导致不必要的副作用;到现在为止,这个额外的调用没有被注意到。
【问题讨论】:
@Jarod42 编号的 cmets 是断点告诉我调用的来源。一个来自静态初始化,一个在placement new;问题是:第三个是什么? @Someprogrammerdude 这是#0 Object::Object (this=0x555555756160 <Storage>) at placement.cpp:7 #1 0x0000555555554a4f in main () at placement.cpp:31
(第7行是cout
,第31行是main
中的单行)
不应该placement new 运算符 not(!) 为对象调用placement new 吗?所以第一个是Storage::where
,第二个是operator new
,第三个是main
-function。
对operator new
函数应该做什么是一个误解:它应该只分配内存,而不是构造对象。因此它不应该做placement-new。
附带说明(因为您的问题已经得到解答),您不需要静态分配对象,将其替换为“char buf[sizeof(Object)];”并且构造函数只被调用一次。
【参考方案1】:
出于某种奇怪的原因,您的 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
如何知道如何正确构造对象?那不是你应该这样做的地方!
【讨论】:
【参考方案2】:Object *obj = new Object;
做了两件事:
通过调用operator new
分配内存
调用构造函数。
您的operator new
也调用了构造函数,因此该语句调用了构造函数两次(一次用于全局变量初始化)。
注意delete
是一样的。 delete obj;
做了两件事:
调用析构函数。
通过调用operator delete
释放内存
你的operator delete
也不应该调用析构函数,因为那样析构函数会被调用两次。
【讨论】:
@Someprogrammerdude 但该模式的重点是不在运行时实际分配内存 哇,感谢额外的错误修复,我现在可能不得不更改所选答案以上是关于将placement new 与存储类一起使用时的额外构造的主要内容,如果未能解决你的问题,请参考以下文章
将 Querydsl 与 Spring Data 一起使用时的最佳实践
C++内存分配方法new与placement new使用方法详解