Malloc 和构造函数

发布于 2024-09-04 06:24:10 字数 122 浏览 16 评论 0原文

newdelete 表达式不同,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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

迎风吟唱 2024-09-11 06:24:10

呃...使用new?这就是重点。您也可以显式调用构造函数,但没有什么理由这样做

正常使用 new/delete:

A* a = new A();

delete a;

显式调用构造函数/析构函数 ("新展示位置"):

A* a = (A*)malloc(sizeof(A));
new (a) A();

a->~A();
free(a);

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 way

Using new/delete normally:

A* a = new A();

delete a;

Calling the constructor/destructor explicitly ("placement new"):

A* a = (A*)malloc(sizeof(A));
new (a) A();

a->~A();
free(a);
金橙橙 2024-09-11 06:24:10

如果您确实需要,可以使用“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.

提笔落墨 2024-09-11 06:24:10

更喜欢新的

但是,如果由于某种原因你有原始内存,你可以用“placement new”来构造它:

new (ptr) TYPE(args);

并且由于你不会使用删除,所以你需要直接调用析构函数:

ptr->~TYPE();

Prefer new.

But if for some reason you have raw memory, you can construct it with "placement new":

new (ptr) TYPE(args);

And since you won't be using delete, you'll need to call the destructor directly:

ptr->~TYPE();
请叫√我孤独 2024-09-11 06:24:10

看一下放置新运算符,它在预先分配的缓冲区上构造一个对象。

Take a look at placement new operator, which constructs an object on a pre-allocated buffer.

江南月 2024-09-11 06:24:10

您误解了 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.

浪推晚风 2024-09-11 06:24:10

这个问题的答案随着时间的推移而不断变化,因此这里有一个更新的答案:

malloc 只是为对象分配存储空间。以下代码在 C 中有效,但在 C++ 中可能无效:

T* object = (T*) malloc(sizeof(T));
*object = ...;

在 C 中,写入 *object 隐式开始 T 对象的生命周期,这是由malloc分配的内存。

在 C++ 中,*object = 将在对象的生命周期尚未开始时尝试写入该对象。这种未定义的行为。但是,如果 T隐式生命周期类型,此代码定义明确 (C++20 起):

[malloccallocrealloc] 隐式创建对象 ([intro.object]) 在返回的存储区域中,并返回指向合适的已创建对象的指针。
对于callocrealloc,对象分别在存储清零或复制之前创建。

- [c.malloc] §5

C++98 - C++17

在 C++20 之前,唯一合法的解决方案是使用 placement-new

auto object = reinterpret_cast<T>(std::malloc(sizeof(T));) // allocate storage
object = new (object) T(arg0, arg1, ...);                  // begin lifetime

do_something_with(*object);

object->~T();                                              // end lifetime
std::free(object);                                         // de-allocate storage

请注意,我们将 new 表达式的结果分配给 object。如果我们不这样做,编译器将被允许假设 object 仍然指向 std::malloc 中的未初始化内存!通过 object 访问对象将是未定义的行为,或者我们必须使用 std::launder

C++20

C++20 添加了两个比placement-new 更好的函数。它们可以在编译时使用,并且像常规函数一样使用:

auto object = reinterpret_cast<T>(std::malloc(sizeof(T));) // allocate storage
object = std::construct_at(object, arg0, arg1, ...);       // begin lifetime

do_something_with(*object);

std::destroy_at(object);                                   // end lifetime
std::free(object);                                         // de-allocate storage

感谢 std::construct_atstd:: destroy_at 后,我们不再在三个不同的地方重复类型 T。它是从函数参数推导出来的,这要好得多。

注意:如上所述,只有当我们的类型不是 时,我们才需要这些函数隐式生命周期类型,例如初始化std::string时。开头所示的 C 代码就是一个有效的示例,尽管您应该更喜欢 C++ 中的 reinterpret_caststd::malloc

C++23

< code>std::malloc 隐式创建一个对象,因为它被标准特殊对待。但是,如果我们想实现自己的 malloc 函数,我们将无法从中受益。 C++23 引入了显式生命周期管理 来解决这个问题。

始终使用 std::construct_at 也是不可行的,因为它对存储进行值初始化,即我们无法创建在写入之前保持未初始化的缓冲区。

仅适用于隐式生命周期类型的解决方案 C++23 如下所示:

auto object = reinterpret_cast<T>(my_malloc(sizeof(T));)      // allocate storage
object = std::start_lifetime_as<T>(object, arg0, arg1, ...);  // begin lifetime

do_something_with(*object);

// no need to explicitly end lifetime
my_free(object);                                              // de-allocate storage

注意:隐式生命周期类型 总是容易被破坏,这就是为什么我们可以省略 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++:

T* object = (T*) malloc(sizeof(T));
*object = ...;

In C, writing to *object implicitly begins the lifetime of a T object, which is a special property of the memory allocated by malloc.

In C++, *object = would attempt to write to the object when its lifetime hasn't begun. This undefined behavior. However, if T is an implicit-lifetime type, this code is well-defined (since C++20):

[malloc, calloc, and realloc] implicitly create objects ([intro.object]) in the returned region of storage and return a pointer to a suitable created object.
In the case of calloc and realloc, the objects are created before the storage is zeroed or copied, respectively.

- [c.malloc] §5

C++98 - C++17

Before C++20, the only legal solution would be to use placement-new:

auto object = reinterpret_cast<T>(std::malloc(sizeof(T));) // allocate storage
object = new (object) T(arg0, arg1, ...);                  // begin lifetime

do_something_with(*object);

object->~T();                                              // end lifetime
std::free(object);                                         // de-allocate storage

Pay attention to the fact that we are assigning the result of the new expression to object. If we didn't do that, the compiler would be allowed to assume that object is still pointing to unitialized memory from std::malloc! Accessing the object through object would be undefined behavior, or we would have to use std::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:

auto object = reinterpret_cast<T>(std::malloc(sizeof(T));) // allocate storage
object = std::construct_at(object, arg0, arg1, ...);       // begin lifetime

do_something_with(*object);

std::destroy_at(object);                                   // end lifetime
std::free(object);                                         // de-allocate storage

Thanks to std::construct_at and std::destroy_at, we are no longer repeating the type T 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 prefer reinterpret_cast and std::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 own malloc 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:

auto object = reinterpret_cast<T>(my_malloc(sizeof(T));)      // allocate storage
object = std::start_lifetime_as<T>(object, arg0, arg1, ...);  // begin lifetime

do_something_with(*object);

// no need to explicitly end lifetime
my_free(object);                                              // de-allocate storage

Note: implicit-lifetime types are always trivially destructible, which is why we can omit std::destroy_at.

谁的新欢旧爱 2024-09-11 06:24:10

使用新展示位置 。这个建议很方便:

建议:请勿使用此“新展示位置”
语法,除非你必须这样做。仅使用它
当你真正关心一个物体时
放置在特定位置
记忆。例如,当您的
硬件有一个内存映射 I/O 定时器
设备,并且您想要放置一个时钟
该内存位置的对象。

Use placement new. The advice is handy:

ADVICE: Don't use this "placement new"
syntax unless you have to. Use it only
when you really care that an object is
placed at a particular location in
memory. For example, when your
hardware has a memory-mapped I/O timer
device, and you want to place a Clock
object at that memory location.

我的奇迹 2024-09-11 06:24:10
class A
{
    public:
      A() { printf("Constructor of A"); }
};

int main()
{
   A* pA = (A*) malloc(sizeof(A));  // Construtor is not going to be called.

   __asm { MOV EAX, pA}             // Makes pA as "this" pointer in intel based s/m.

   A();                             // Calling constructor explicitly.

   return 0;
}
class A
{
    public:
      A() { printf("Constructor of A"); }
};

int main()
{
   A* pA = (A*) malloc(sizeof(A));  // Construtor is not going to be called.

   __asm { MOV EAX, pA}             // Makes pA as "this" pointer in intel based s/m.

   A();                             // Calling constructor explicitly.

   return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文