Malloc 和构造函数
与 new
和 delete
表达式不同,std::malloc
在分配对象内存时不会调用构造函数。那么,我们如何创建一个对象,以便构造函数也被调用呢?
Unlike new
and delete
expressions, std::malloc
does not call the constructor when memory for an object is allocated. In that case, how must we create an object so that the constructor will also be called?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
呃...使用
new
?这就是重点。您也可以显式调用构造函数,但没有什么理由这样做正常使用 new/delete:
显式调用构造函数/析构函数 ("新展示位置"):
Er...use
new
? That's kind of the point. You can also call the constructor explicitly, but there's little reason to do it that wayUsing new/delete normally:
Calling the constructor/destructor explicitly ("placement new"):
如果您确实需要,可以使用“placement new”语法来做到这一点:
MyClassName* foo = new(pointer) MyClassName();
其中
pointer
是一个指针分配到足够大以容纳对象实例的已分配内存位置。You can use "placement new" syntax to do that if you really, really need to:
MyClassName* foo = new(pointer) MyClassName();
where
pointer
is a pointer to an allocated memory location large enough to hold an instance of your object.更喜欢
新的
。但是,如果由于某种原因你有原始内存,你可以用“placement new”来构造它:
并且由于你不会使用删除,所以你需要直接调用析构函数:
Prefer
new
.But if for some reason you have raw memory, you can construct it with "placement new":
And since you won't be using delete, you'll need to call the destructor directly:
看一下放置新运算符,它在预先分配的缓冲区上构造一个对象。
Take a look at placement new operator, which constructs an object on a pre-allocated buffer.
您误解了 malloc 的作用。 malloc 不创建对象,它分配内存。由于它不创建对象,因此没有对象可供它调用构造函数来创建。
如果需要在 C++ 中动态创建对象,则需要使用某种形式的 new。
You mis-understand what malloc does. malloc does not create objects, it allocates memory. As it does not create objects there is no object for it to call a constructor to create.
If you need to dynamically create an object in C++ you need to use some form of new.
这个问题的答案随着时间的推移而不断变化,因此这里有一个更新的答案:
malloc
只是为对象分配存储空间。以下代码在 C 中有效,但在 C++ 中可能无效:在 C 中,写入
*object
隐式开始T
对象的生命周期,这是由malloc
分配的内存。在 C++ 中,
*object =
将在对象的生命周期尚未开始时尝试写入该对象。这种未定义的行为。但是,如果T
是 隐式生命周期类型,此代码定义明确 (C++20 起):- [c.malloc] §5
C++98 - C++17
在 C++20 之前,唯一合法的解决方案是使用 placement-new:
请注意,我们将
new
表达式的结果分配给object
。如果我们不这样做,编译器将被允许假设object
仍然指向std::malloc
中的未初始化内存!通过object
访问对象将是未定义的行为,或者我们必须使用std::launder
。C++20
C++20 添加了两个比placement-new 更好的函数。它们可以在编译时使用,并且像常规函数一样使用:
感谢
std::construct_at
和std:: destroy_at
后,我们不再在三个不同的地方重复类型T
。它是从函数参数推导出来的,这要好得多。注意:如上所述,只有当我们的类型不是 时,我们才需要这些函数隐式生命周期类型,例如初始化
std::string
时。开头所示的 C 代码就是一个有效的示例,尽管您应该更喜欢 C++ 中的reinterpret_cast
和std::malloc
。C++23
< code>std::malloc 隐式创建一个对象,因为它被标准特殊对待。但是,如果我们想实现自己的
malloc
函数,我们将无法从中受益。 C++23 引入了显式生命周期管理 来解决这个问题。始终使用 std::construct_at 也是不可行的,因为它对存储进行值初始化,即我们无法创建在写入之前保持未初始化的缓冲区。
仅适用于隐式生命周期类型的解决方案 C++23 如下所示:
注意:隐式生命周期类型 总是容易被破坏,这就是为什么我们可以省略
std::destroy_at< /code>
.
The answer to this question has evolved over time, so here's an updated answer:
malloc
merely allocates storage for an object. The following code works in C, but might not in C++:In C, writing to
*object
implicitly begins the lifetime of aT
object, which is a special property of the memory allocated bymalloc
.In C++,
*object =
would attempt to write to the object when its lifetime hasn't begun. This undefined behavior. However, ifT
is an implicit-lifetime type, this code is well-defined (since C++20):- [c.malloc] §5
C++98 - C++17
Before C++20, the only legal solution would be to use placement-new:
Pay attention to the fact that we are assigning the result of the
new
expression toobject
. If we didn't do that, the compiler would be allowed to assume thatobject
is still pointing to unitialized memory fromstd::malloc
! Accessing the object throughobject
would be undefined behavior, or we would have to usestd::launder
.C++20
C++20 has added two functions which are preferable to placement-new. They can be used at compile time, and they are used like regular functions:
Thanks to
std::construct_at
andstd::destroy_at
, we are no longer repeating the typeT
in three different places. It is deduced from the function arguments, which is much nicer.Note: as explained above, we only need these functions if our type is not an implicit-lifetime type, e.g. when initializing
std::string
. The C code shown in the beginning is a working example of this, although you should preferreinterpret_cast
andstd::malloc
in C++.C++23
std::malloc
implicitly creates an object because it is treated specially by the standard. However, if we wanted to implement our ownmalloc
function, we wouldn't benefit from this. C++23 introduces explicit lifetime management to solve this.Always using
std::construct_at
is also not viable, because it value-initializes the storage, i.e. we can't create a buffer that remains uninitialized until written to.The solution for implicit-lifetime types only in C++23 looks as follows:
Note: implicit-lifetime types are always trivially destructible, which is why we can omit
std::destroy_at
.使用
新展示位置
。这个建议很方便:Use
placement new
. The advice is handy: