0

为什么会崩溃?我确实发现 malloc() 不调用构造函数,所以我自己手动调用了它们,但它仍然崩溃,我不明白为什么。

PS。我知道 std::vector 和 new[] 存在。不要告诉我使用vectors/new[] 作为答案。

struct MyStruct {
    vector<int> list;
};
void make_crash(){
    MyStruct *array = (MyStruct *)malloc(100*sizeof(MyStruct));
    MyStruct element; // initialize element here since malloc() doesnt do it.
    array[0] = element; // copy, everything should be alright?
    array[0].list.push_back(1337); // nope, BANG!
    // The above line makes these:
    // First-chance exception at 0x7c970441 in test.exe: 0xC0000005: Access violation reading location 0xbaadf005.
    // First-chance exception at 0x00401cd0 in test.exe: 0xC0000005: Access violation reading location 0xbaadf00d.
    // Unhandled exception at 0x00401cd0 in test.exe: 0xC0000005: Access violation reading location 0xbaadf00d.
}
4

4 回答 4

7

array[0] = element;您调用operator=of的行上array[0]。由于array[0]未初始化,因此这是未定义的行为。调用任何方法或运算符,包括operator=在尚未调用其构造函数的对象上,都是未定义的行为。

要解决您的问题,您要么需要使用placement new 来调用构造函数,array[0]要么只使用new代替malloc. 除非您有充分的理由使用malloc,否则后者更可取(甚至更好:使用向量)。

于 2012-06-01T21:14:11.907 回答
4

这不是就地初始化元素的方式。(隐式创建的)赋值运算符(调用向量的赋值运算符)正在一个不存在的对象上调用,这显然是个坏消息。

您必须改用placement new:

new (array) MyStruct;

对于数组:

new (array) MyStruct[100];

于 2012-06-01T21:14:32.307 回答
1

当你分配给一个MyStruct

array[0] = element;

首先尝试破坏结构的旧成员 - 但没有任何,因为它们从未被构造。繁荣!

获得一百个MyStructs 的最简单方法是使用另一个向量

vector<MyStruct>  v(100);

无需使用malloc.

于 2012-06-01T21:21:54.340 回答
1
MyStruct *array = (MyStruct *)malloc(100*sizeof(MyStruct));

这就是你出错的地方。

array不是指向一个或多个MyStruct对象的指针,无论您给它什么类型。的返回值malloc是 a void*。C++ 的规则不允许你从 a 隐式void*转换为其他类型,这就是为什么你必须把它(MyStruct*)放在那里的原因。仅需要明确的演员表就应该告诉您您正在做一些阴暗的事情。

C++ 的规则规定,如果您将 a 显式void*转换为 some Type*(在某些特殊类型之外),则void*当您进行转换的最初 aType*本身被转换为 a 时,这才是合法的void*。这不是原因;这void*来自malloc并且从来不是一个MyStruct*. 您在对编译器撒谎,因此会引发未定义的行为。因此崩溃。

如果您想要定义的行为,那么您需要实际使用 C++,而不是您正在发明的这种“我不敢相信它不是 C++”的语言。例如:

void *block = malloc(100 * sizeof(MyStruct));
MyStruct* array = new(block) MyStruct[100];

请注意这里完全没有演员表操作。

当然,删除这个数组很痛苦:

for(int i = 99; i >= 0; --i)
  array[i].~MyStruct();

free(block);

请注意,您必须以与它们构建时相反的顺序向后销毁它们。


想知道这些东西是如何在内部工作的。new[] 是否会产生一些额外的内存来告诉我的 CPU 一些 malloc 没有的东西?以及为什么/什么/如何在没有 new[] 的情况下模仿它?还是新的安置?我如何用 CPU 的原始二进制代码编写它?有可能吗?神奇的 new[] 到底是什么?

所有这些都取决于实现。就语言而言,究竟发生了什么是明确定义的。除其他外,放置 new 将调用对象的构造函数。数组放置 new 将调用数组中所有对象的构造函数,从第一个到最后一个。如果其中一个抛出,那么它将在任何先前构造的对象上调用析构函数,然后发出异常。

正在做的事情是依赖于实现的,只不过是继承是如何实现的等等。显然,编译器会为它发出一些代码,但同样,发出的确切内容取决于实现。

只要您实际上是在编写 C++,就不可能“用 CPU 的原始二进制代码编写它”。要实现placement new,您必须能够调用类的构造函数。而且...嗯,这就是placement new 的用途。您甚至不允许获得指向构造函数的成员指针(即使可以,成员指针的内容也取决于实现,并且它们并不总是指向某个汇编函数的裸指针)。因此,如果没有特定于平台的陪审团操纵,甚至无法识别构造函数代码。

您可以通过查看生成的程序集来调用placement new,了解如何为特定系统执行此操作。但是每个编译器都会有所不同。

于 2012-06-01T22:58:39.513 回答