如何在使用malloc()分配结构体时,在结构体中使用C++字符串?

22

我编写了下面的示例程序,但它会因为段错误而崩溃。问题似乎出在在结构体中使用mallocstd::string上。

#include <iostream>
#include <string>
#include <cstdlib>

struct example {
 std::string data;
};

int main() {
 example *ex = (example *)malloc(sizeof(*ex));
 ex->data = "hello world";
 std::cout << ex->data << std::endl;
}

我无法弄清楚如何使它工作,不知道是否有可能同时使用 malloc() std::string 。谢谢,Boda Cydo。

5个回答

34

在 C++ 中,无法使用 malloc 分配具有非平凡构造函数的类。由 malloc 得到的是一块 原始 内存块,其中不包含正确构造的对象。任何试图将该内存用作“真实”对象的尝试都将失败。

与其使用 malloc 分配对象,不如使用 new

example *ex = new example;

你原来的代码也可以使用malloc来运行,只需按照以下步骤进行操作:首先使用malloc来申请原始内存,然后在该原始内存中构建对象。

void *ex_raw = malloc(sizeof(example));
example *ex = new(ex_raw) example;

上面使用的new形式称为“放置新”。但在您的情况下,没有必要进行所有这些诡计。


我使用malloc()是因为new会抛出异常而不是返回NULL。我只是不想在那段代码中有异常。那段代码发生在一个C回调函数中,我不想在C回调函数中抛出C++异常。所以我使用了malloc。您对此有什么想法吗? - bodacydo
2
@bodacydo:你可以使用new(std::nothrow) example来使new返回空指针而不是抛出异常。你必须包含头文件<new>才能使用std::nothrow常量。 - AnT stands with Russia
2
如果你想让new在失败时返回一个空指针,请使用new(nothrow) example。但是,你应该使用try/catch来捕获任何抛出的异常。(例如,std::string赋值也可能会因为无法分配内存而抛出异常。) - jamesdlin
这非常有用,因为我正在使用一个库,该库要求我使用他们自定义的alloc命令来为传递到其函数调用中的对象分配内存,但是没有提供使用包含非平凡结构体的它们结构派生的方式。好答案! - TheBat

6
对于像你的example这样的classstruct,正确的答案是使用new而不是malloc()来分配实例。只有operator new知道如何调用struct及其成员的构造函数。你的问题是由于字符串成员从未被构造引起的。
然而,有一些罕见情况下需要一个特定的内存块表现得好像它持有一个类的实例。如果你真的有这样的情况,那么有一种变体的operator new允许指定对象的位置。这被称为“placement new”,必须非常小心地使用。
void *rawex = malloc(sizeof(example));  // allocate space
example ex = new(rawex) example();      // construct an example in it
ex->data = "hello world";               // use the data field, not no crash
// time passes
ex->~example();                         // call the destructor
free(rawex);                            // free the allocation

使用placement new时,您需要提供正确大小和对齐的内存区域。如果未提供正确的大小或对齐方式,将会导致神秘的问题出现。错误的对齐通常更容易引起问题,但也可能很神秘。
此外,使用placement new时,您需要手动调用析构函数,并根据内存块的来源将其释放给其所有者。
总之,除非您已经知道需要使用placement new,否则您几乎肯定不需要它。它有合法的用途,但只存在于框架的晦涩角落,不是日常发生的事情。

从未听说过这些。谢谢! - bodacydo

3

malloc函数分配内存时不会调用任何构造函数,不要混用C风格的内存分配和C++对象,它们不兼容。相反,在C++代码中应该使用new运算符来分配对象:

example *ex = new example;

这是更智能的代码,并将调用std::string::string()构造函数来初始化字符串,这将修复你看到的段错误。别忘了在完成后删除它以释放内存并调用适当的析构函数:

delete ex;

2
问题在于malloc不会调用example的构造函数。由于string通常被表示为堆栈上的指针,因此它被设置为零,您会解引用一个空指针。您需要使用new

-1

你不应该使用

example *ex = (example *)malloc(sizeof(*ex));

因为 sizeof(*ex) 返回的值相当于 long 或 int 的大小,这是由于你使用不同的编译环境。你可以使用以下代码:

example *ex = (example *)malloc(sizeof(example));


如果你不小心的话(因为它是错误的),有人会给这个点踩。 - Martin York

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接