【问题标题】:Initializer list for dynamic arrays?动态数组的初始化列表?
【发布时间】:2011-10-30 19:03:13
【问题描述】:

可以为静态数组的定义提供一个初始化列表。示例:

int main()
{
  int int_static[2] = {1,2};
}

动态数组是否可以使用类似的初始化列表?

int main()
{
  int* int_ptr = new int[2];
}

这更接近我想要做的:

struct foo
{
  foo(){}
  foo(void * ptr): ptr_(ptr) {}
  void * ptr_;
};

int main()
{
  foo* foo_ptr = new foo[10];
}

在初始化时不应调用默认构造函数,而是调用 foo:foo(void*)。

对于只有有限数量的可用堆栈的加速器内核进行即时编译时,为动态数组提供静态初始化器列表可能会派上用场,但同时您构建您的具有(加速器编译时间 = 主机运行时间)静态初始化列表的对象。

我假设不是(因为这需要编译器生成额外的代码,即将参数的值复制到堆位置)。我认为 c++0x 支持其中的一些,但我不能使用它。 现在我可以使用这样的构造。也许有人知道一个窍门..

最好的!

【问题讨论】:

    标签: c++ arrays dynamic


    【解决方案1】:

    在 OP 发布此问题时,C++11 支持可能还不是很普遍,这就是为什么接受的答案说这是不可能的。但是,现在所有主要的 C++ 编译器都应该支持使用显式初始化列表初始化动态数组。

    new int[3] {1, 2, 3} 的语法在 C++11 中被标准化。引用 cppreference.com 上的 new expression 页面:

    new-expression 创建的对象根据以下规则进行初始化:
    ...
    如果 type 是数组类型,则初始化对象数组:
    ...
    如果 initializer 是用大括号括起来的参数列表,则数组是聚合初始化的。 (C++11 起)

    因此,鉴于 OP 的示例,以下在使用 C++11 或更新版本时是完全合法的:

    foo * foo_array = new foo[2] { nullptr, nullptr };
    

    请注意,通过在初始化列表中提供指针,我们实际上是在哄骗编译器应用 foo(void * ptr) 构造函数(而不是默认构造函数),这是所期望的行为。

    【讨论】:

      【解决方案2】:

      不,你不能那样做。

      我认为 C++ 不允许这样做,因为允许这样的事情不会向语言添加任何 nice-to-have 功能。换句话说,如果你使用 static 初始化器来初始化 dynamic 数组,它的意义何在?

      动态数组的重点是创建一个大小为N的数组,在运行时已知,这取决于实际需要。也就是代码

      int *p = new int[2]; 
      

      对我来说没有以下意义:

      int *p = new int[N]; //N is known at runtime
      

      如果是这样,那么您如何提供 static 初始化程序中的元素数量,因为 N 直到运行时才知道?

      假设您可以这样写:

      int *p = new int[2] {10,20}; //pretend this!
      

      但是你写这篇文章有什么好处呢?没有。它的几乎与:

      int a[] = {10,20};
      

      真正的优势是当您被允许为N 元素的数组编写它时。但是问题是这样的:

       int *p = new int[N] {10,20, ... /*Oops, no idea how far we can go? N is not known!*/ };
      

      【讨论】:

      • 为了记录,“动态”并不意味着“可变长度”。能够创建一个新的动态对象并将其初始化为已知的初始内容确实是一个“不错的功能”。这正是构造函数对类和结构类型对象所做的事情。
      • 澄清一下,C++ 中的“动态”意味着分配在堆上而不是堆栈上。我广泛使用这两种内存存储,因此让初始化列表对两者都有效会非常方便。事实上,在许多情况下,当我想在堆上创建一个数组列表时,我不得不求助于对象(如 std::vector)而不是普通数组,因为正如@MikeHousky 所说,C++ 仅支持在堆上初始化对象此刻,不是数组。这里没有技术或性能问题 - 只是 C++ 的限制。
      【解决方案3】:

      不,您必须动态创建元素。

      或者,您可以使用本地数组并将其元素复制到动态分配数组的元素上:

      int main() {
         int _detail[] = { 1, 2 };
         int * ptr = new int[2];
         std::copy( _detail, _detail+(sizeof detail / sizeof *detail), ptr );
         delete [] ptr;
      }
      

      在将所有元素设置为0的限制版本中,您可以在new调用中使用额外的一对括号:

      int * ptr = new int[2]();  // will value initialize all elements
      

      但你似乎在寻找不同的东西。

      【讨论】:

      • 您第一句话中的标点符号有点误导;有“固定”。如果您不同意,则回滚。
      • 我正在寻找一种方法来调用动态分配的每个元素的构造函数(当然它不仅仅是一个“int”)。但它不是必须调用的标准构造函数。它必须采用 1 个 arg,并且该参数的左值对于每个元素都是不同的。
      • 不应该 _detail 是一个 int* 吗?
      • 感谢 Tomalak 和 James 修复了答案。
      • 下一组问题是关于实际需求的(同样,与大多数其他人一样,您没有提供):为什么必须动态分配?为什么不能使用std::vector?你控制分配和解除分配吗?
      【解决方案4】:

      鉴于您是真正的类,它比 int 更复杂,并且由不同的值构成,所以它很复杂。 如果您有一个具有正确值的现有数组/向量作为默认值,或者您必须使用placement new,则可以使用迭代器构造向量。

      //vector
      int main()
      {
        int int_static[2] = {1,2};
        std::vector<int> int_dynamic(int_static, int_static+2);
        //this is what everyone else is saying.  For good reason.
      }
      //placement new
      int function_that_returns_constructed_from_values() {
          return rand();
      }
      int main() 
      {
          int count = 2;
          char *char_dynamic = new char[count * sizeof(int)];
          int *int_dynamic = char_dynamic;
          for(int i=0; i<count; ++i)
              new(int_dynamic+i)int(function_that_returns_constructed_from_values());
          //stuff
          for(int i=0; i<count; ++i)
              (int_dynamic+i)->~int(); //obviously not really int
          delete []char_dynamic;
      }
      

      显然,向量是执行此操作的首选方式。

      【讨论】:

        【解决方案5】:

        初始化程序数据无论如何都必须在某个地方。简单命名。

        例如,

        #include <stddef.h>
        #include <algorithm>        // std::copy
        #include <vector>
        
        typedef ptrdiff_t   Size;
        
        template< class Type, Size n >
        Size countOf( Type (&)[n] ) { return n; }
        
        int main()
        {
            using namespace std;
        
            static int const    initData[]  = {1,2};
            static Size const   n           = countOf( initData );
        
            // Initialization of a dynamically allocated array:
            int*        pArray  = new int[n];
            copy( initData, initData + n, pArray );
        
            // Initialization of a vector:
            vector<int> v( initData, initData + n );
        }
        

        编辑:修复了上述代码中的一个想法。我赶紧应要求添加示例。所以我放的确实错误地使用了来自std::copy的返回值。

        干杯,

        【讨论】:

        • 这不是特别清楚。你能举个例子吗?
        • @Oli,我猜他指的是类似于我在回答中建议的解决方案,只是忽略了所需副本的血腥细节。
        • 然而,我似乎“明白”了这一点,所以为了让答案得到理解,需要用非常详细的例子来充实它,给男人一条鱼。我通常不认为这样做,因为为了帮助某人而绝对不这样做。尽管这是为了获得支持而做的事情。
        猜你喜欢
        • 2023-03-19
        • 1970-01-01
        • 2011-06-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-27
        • 2016-06-13
        相关资源
        最近更新 更多