首页 > 解决方案 > 为什么不总是使用 ::operator new 而不是 new (甚至不构造)?

问题描述

我一直在尝试这段代码,其中 Vector3 有 3 个成员 x、y、z。在 Vector3 中,我制作了副本、移动副本等。

    Vector3* data = (Vector3*)::operator new(2 * sizeof(Vector3));

    //new (&data[0]) Vector3(1., 2., 3.);
    //new (&data[1]) Vector3(2., 4., 5.);

    data[0] = Vector3(1., 2., 3.);
    data[1] = Vector3(2., 4., 5.);

    std::cout << data[0].x << " " << data[0].y << " " << data[0].z << '\n';
    std::cout << data[1].x << " " << data[1].y << " " << data[1].z << '\n';

    data[0].~Vector3();
    data[1].~Vector3();

    ::operator delete(data, 2 * sizeof(Vector3));

在第一行中,我只分配内存。以下注释的 2 行是我被告知应该使用新位置构造向量的方式,以便在那里构造和放置类。但是,如果我改为编写接下来的 2 行,而在每个索引处没有任何特定的类构造,它似乎仍然有效。问题是什么?它不应该工作吗?它会在某个时候破裂吗?是否存在未正确复制的内容?

因为,如果它有效,那么使用 new/new[] 运算符构建类还有什么意义,因为构建类需要更多时间?

标签: c++

解决方案


C++ 没有指定它如何在您编译到的硬件上工作。C++ 指定了它在抽象机器上的工作方式。该抽象机器具有“非物理”属性,因为它们不太可能实际存在于您物理编译到的机器上。

抽象机器的非物理属性的一个例子是它实际上跟踪存在哪些对象以及它们在哪里。而且,如果您尝试与不存在或类型错误的对象(某些特定例外除外)进行交互,则 C++ 标准不再指定抽象机器应具有的行为。对于整个程序的执行,不仅仅是在你与不存在的对象交互的指令集中,就好像它存在一样。

理论上,C++ 编译器将您的 C++ 代码转换为该抽象机器上的行为,然后将其转换为汇编指令。但是,如果违反了抽象机器的规则,则允许从抽象机器到汇编的翻译做任何事情。这最常发生在优化中,其中不是将操作从 C++ 天真地映射到目标硬件,而是重新排序并假设什么是合法的,什么是不合法的。

这种优化的一个常见例子是别名。在 C++ 中,如果一个函数采用 adouble*和 aint const*以及一个长度,然后遍历将int const*它们转换为doubles 并将其写出,则两个缓冲区重叠是不合法的。

因为一个int存在于每个内存地址或一个double,而不是两者,点。

反过来,这意味着编译器可以加载一批 int,将它们批量转换为双精度数,然后将该批次写出。如果没有无别名要求,则每次读取都必须在执行另一次读取之前先写入,因为较早的写入可能会覆盖后面的 int 读取!

C++ 编译器将对象标识视为不仅仅是以某种特定方式解释的某些字节。编译器理解对象何时被读取或写入的能力是它能够明智地将 C++ 代码转换为比完全偏执的未优化版本可能需要的更优化的程序集的关键。

在 C++ 中创建对象的方法只有几种。一个新的表达式(放置与否),声明一个变量,以正确的方式参与联合,以及(在较新的标准版本中)涉及将原始内存视为原始类型的某些操作(可以说,当对象追溯存在于一些读数,包括时间旅行)。

但实际上,如果您正在使用要放入对象的原始未初始化内存,只需使用placement new。这是您至少可以做的。


推荐阅读