【问题标题】:Why can I not specify the constructor when newing an array?为什么我在新建数组时不能指定构造函数?
【发布时间】:2013-09-20 21:53:54
【问题描述】:

更新:

上面的链接和下面的答案没有回答为什么这个功能没有标准化。这正是让我想知道的原因。请考虑std::vector<A> arr(8, 7);new A[8](7); 之间的性能问题

如果我们使用std::vector<A> arr(8, 7);,它可能(不是必须)如下实现:

this->internal_buf = new A[8]; // Call default constructor A() 8 times!
for (auto i = 0; i < 8; ++i)
{
    this->internal_buf[i] = A(7); // Call constructor A(7) 8 times AGAIN!!!
}

如果C++支持new A[8](7);可以如下实现:

A* arr = (A*)malloc(sizeof(A) * 8 + extra_size);

for (auto i = 0; i < 8; ++i)
{
    new (&arr[i]) A(7); // Call constructor A(7) 8 times ONLY.
}

比较这两种方法,很明显new A[8](7);std::vector&lt;A&gt; arr(8, 7);更快

另外,我也觉得new A[8](7);std::vector&lt;A&gt; arr(8, 7);更简洁,更有表现力

无论如何,我认为 C++ 应该为程序员提供另一种替代工具,例如此功能。因为 C++ 的哲学之一是“为您提供尽可能多的工具,但您不必为不需要的东西付费。”

以下为原帖:

struct A
{
    int n;
    A() : n() {}
    A(int n) : n(n) {}
};

int main()
{
    new A[8];    // OK
    new A[8]();  // OK
    new A[8](7); // Error
}

为什么新建数组时不能指定构造函数?

为什么 C++ 标准不支持这么方便的功能? 原因是什么?

【问题讨论】:

  • 可以为数组指定一个初始化器。但是数组的初始化器与要应用于每个元素的数组元素的初始化器不同。另外,new 分配的数组在这方面与其他数组没有什么不同。
  • 抱歉学究但this-&gt;internal_buf[i] = A(7);错了;应该是new (&amp;this-&gt;internal_buf[i]) A(7);

标签: c++ arrays c++11 constructor initialization


【解决方案1】:

为什么 C++ 标准不支持这么方便的功能?理由是什么?

因为有更好的替代方案可以在(几乎)所有用例中替换内置数组(是的,甚至用于传递给 C-API):std::vector 用于动态数组(使用new 分配)和std::array 用于堆栈分配的。

使用std::vector,您可以使用std::vector&lt;A&gt; arr(8, 7)(正如 WhozCraig 评论的那样)创建一个包含 8 个元素的向量,并用 7 初始化每个元素。省略第二个参数将使用它们的默认构造函数(内置类型初始化为0false)。

除了这个和其他便利功能(特别是自动调整大小/push_back())之外,std::vector 相对于使用new 创建数组的最大优势在于它遵循RAII,这意味着它会自动delete[]一旦离开作用域,它就是对象/内存——无论是“脱离函数的末尾”、return 语句、breakcontinuegoto 还是抛出异常。

【讨论】:

  • 我正在使用 Windows 内核,其中 std::vector 和其他 STL 构造不可用。我该怎么办?
  • @xmllmx:你是说你在写windows内核代码吗?我对windows一点都不熟悉,但是windows内核可以用C++?
  • 这听起来像是一项不寻常的工作;)。但是如果这么大的项目(a)使用 C++ 并且(b)不能依赖 STL,那么肯定有替代方案吗?
  • @xmllmx:std::vector 是“不可用”是什么意思?这一切都是模板化的,你可以专门化 std::allocator 以使用内核模式分配器,std::vector 的其余部分应该可以正常工作......它不需要运行时支持。
  • @xmllmx:啊。对不起,它比我看起来更复杂。查找 _HAS_EXCEPTIONS 宏并将其定义为零,然后再包含任何标准标题;这应该避免使库避免使用 C++ try/catch (不过,析构函数仍然可以)。还要重新定义_RAISE(x) 来做任何你想做的事情(例如使内核崩溃),这将告诉内核如何引发异常。这样做应该可以摆脱这种依赖,让你在内核中使用向量。
【解决方案2】:

Vector 不是这样实现的:

this->internal_buf = new A[8]; // Call default constructor A() 8 times!
for (auto i = 0; i < 8; ++i)
{
    this->internal_buf[i] = A(7); // Call constructor A(7) 8 times AGAIN!!!
}

大致实现如下:

typedef UninitializedBackingStoreForA B;

this->internal_buf = new B[8];
for (auto i = 0; i < 8; i++)
    new (&B[i]) A(7);

也就是说,它使用未初始化的存储和放置 new 来构造元素。

所以你的直觉是错误的,vector 已经有了你想要的性能。每个元素调用一次构造函数,不需要默认构造函数。

【讨论】:

    【解决方案3】:

    请考虑

    之间的性能问题

    你对实际的实现是错误的。在 C++11 中,std::vector 有一个分配器对象,默认情况下是 std::allocator。它首先通过调用Allocator::allocate 来初始化内存,它返回原始内存;然后它调用Allocator::construct 在原始内存上一一构造对象,默认使用placement new。换句话说,它与您指出的第二种可能的实现基本相同。

    【讨论】:

    • 我说过“它可以按如下方式实现”而不是“它必须按如下方式实现”,;)
    • @xmllmx:由于标准的要求,它绝不能作为您的建议实施。因此,您对这个问题的更新实际上是错误的,并且对您的问题没有任何价值。
    • @xmllmx:既然你的更新实际上是错误的,那你最好删掉那部分,我删掉这个“答案”。
    【解决方案4】:

    对于 C++11 之前的版本,您可以这样做:

    new A[8]{ A(7), A(7), A(7), A(7), A(7), A(7), A(7), A(7) };
    

    【讨论】:

    • 新 A[8](7);与新 A[8]{ A(7), A(7), A(7), A(7), A(7), A(7), A(7), A(7) };.哪个更优雅?
    • @xmllmx 什么更优雅?? 这个更“优雅”:std::vector&lt;A&gt; arr(8, {7});
    • 事实上,您可以指定构造函数。为什么没有用于初始化具有相同参数的所有元素的语法糖是另一回事。可能没有人想让解析 C++ 变得比现在更复杂。
    • @Alex,我认为复杂不是原因。 C++提供了各种工具,你可以决定是否使用它们。因为您不必为不需要的东西付费。
    • "对于 pre-C++11" 等等,pre C++11?
    【解决方案5】:

    你试过for_each 和 lambda 吗?

    A* B = new A[8];
    
    std::for_each(B, B+8, [](A &elem) {
        elem.n = 7;
    });
    

    【讨论】:

    • 首先调用A的默认构造函数。没有的话就不行了。
    猜你喜欢
    • 2014-05-10
    • 1970-01-01
    • 2021-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-24
    • 2017-04-11
    相关资源
    最近更新 更多