【问题标题】:C++: constructor initializer for arraysC++:数组的构造函数初始化器
【发布时间】:2011-01-25 11:21:25
【问题描述】:

我脑筋急转弯...如何在 C++ 中正确初始化对象数组?

非数组示例:

struct Foo { Foo(int x) { /* ... */  } };

struct Bar { 
     Foo foo;

     Bar() : foo(4) {}
};

数组示例:

struct Foo { Foo(int x) { /* ... */  } };

struct Baz { 
     Foo foo[3];

     // ??? I know the following syntax is wrong, but what's correct?
     Baz() : foo[0](4), foo[1](5), foo[2](6) {}
};

编辑:我们很欣赏狂野而疯狂的解决方法,但它们对我的情况没有帮助。我正在开发一个嵌入式处理器,其中 std::vector 和其他 STL 构造不可用,明显的解决方法是创建一个默认构造函数并具有一个可以在构造时间之后调用的显式 init() 方法,以便我根本不必使用初始化程序。 (这是我被 Java 的 final 关键字 + 构造函数的灵活性所宠坏的情况之一。)

【问题讨论】:

  • (为了教学简单,访问关键字被保留)
  • 为了教学简单,使用struct 代替class 不是更容易吗?我发现编译的代码更容易学习;-)
  • 当我将您的代码复制到我的编译器中时,我必须添加您遗漏的内容。因此,为了教学的简单性,您可能会考虑不要让人们在未来为您提供帮助。
  • 史蒂夫/约翰:在这两个方面都是正确的。过失。
  • @Jason:买一个,非常宝贵。你也可以使用codepad.org 来写类似这样的代码。

标签: c++ constructor initializer


【解决方案1】:

这是我的解决方案供您参考:

struct Foo
{
    Foo(){}//used to make compiler happy!
    Foo(int x){/*...*/}
};

struct Bar
{
    Foo foo[3];

    Bar()
    {
        //initialize foo array here:
        for(int i=0;i<3;++i)
        {
            foo[i]=Foo(4+i);
        }
    }
};

【讨论】:

    【解决方案2】:

    你可以做到,但它并不漂亮:

    #include <iostream>
    
    class A {
        int mvalue;
    public:
        A(int value) : mvalue(value) {}
        int value() { return mvalue; }
    };
    
    class B {
        // TODO: hack that respects alignment of A.. maybe C++14's alignof?
        char _hack[sizeof(A[3])];
        A* marr;
    public:
        B() : marr(reinterpret_cast<A*>(_hack)) {
            new (&marr[0]) A(5);
            new (&marr[1]) A(6);
            new (&marr[2]) A(7);
        }
    
        A* arr() { return marr; }
    };
    
    int main(int argc, char** argv) {
        B b;
        A* arr = b.arr();
        std::cout << arr[0].value() << " " << arr[1].value() << " " << arr[2].value() << "\n";
        return 0;
    }
    

    如果你把它放在你的代码中,我希望你有一个很好的理由。

    【讨论】:

      【解决方案3】:

      在visual studio 2012或更高版本中,你可以这样做

      struct Foo { Foo(int x) { /* ... */  } };
      
      struct Baz { 
           Foo foo[3];
      
           Baz() : foo() { }
      };
      

      【讨论】:

        【解决方案4】:

        只是为 C++11 更新这个问题,现在这既可以做到又非常自然:

        struct Foo { Foo(int x) { /* ... */  } };
        
        struct Baz { 
             Foo foo[3];
        
             Baz() : foo{{4}, {5}, {6}} { }
        };
        

        为了更简洁,也可以省略这些大括号:

        struct Baz { 
             Foo foo[3];
        
             Baz() : foo{4, 5, 6} { }
        };
        

        这也可以很容易地扩展到多维数组:

        struct Baz {
            Foo foo[3][2];
        
            Baz() : foo{1, 2, 3, 4, 5, 6} { }
        };
        

        【讨论】:

        • 有没有很好的方法来初始化 Foo foo[3][2];?
        • @dshin 同样的方式。最多支持:: foo{{{1}, {2}}, {{3}, {4}}, {{5}, {6}}},或较少支持foo{{1, 2}, {3, 4}, {5, 6}},或最少支持foo{1, 2, 3, 4, 5, 6}
        • Foo 的构造函数被显式声明时是否有解决方法?
        【解决方案5】:

        没有办法。你需要一个数组成员的默认构造函数,它会被调用,之后,你可以在构造函数中做任何你想要的初始化。

        【讨论】:

        • 不幸的是,你是对的。 +1 请注意,C++1x 的统一初始化语法将允许您执行此操作。
        • @sbi 除非所需的构造函数被标记为显式
        【解决方案6】:
        class C
        {
           static const int myARRAY[10];  // only declaration !!!
        
           public:
           C(){}
           }
        
        const int C::myARRAY[10]={0,1,2,3,4,5,6,7,8,9};  // here is definition
        
        int main(void)
        {
           C myObj;
           }
        

        【讨论】:

          【解决方案7】:

          您可以将C++0x auto 关键字与模板特化 一起用于例如名为boost::make_array() 的函数(类似于make_pair())。对于 N 是 1 个或 2 个参数的情况,我们可以将 variant A 写为

          namespace boost
          {
          /*! Construct Array from @p a. */
          template <typename T>
          boost::array<T,1> make_array(const T & a)
          {
              return boost::array<T,2> ({{ a }});
          }
          /*! Construct Array from @p a, @p b. */
          template <typename T>
          boost::array<T,2> make_array(const T & a, const T & b)
          {
              return boost::array<T,2> ({{ a, b }});
          }
          }
          

          变体B

          namespace boost {
          /*! Construct Array from @p a. */
          template <typename T>
          boost::array<T,1> make_array(const T & a)
          {
              boost::array<T,1> x;
              x[0] = a;
              return x;
          }
          /*! Construct Array from @p a, @p b. */
          template <typename T>
          boost::array<T,2> make_array(const T & a, const T & b)
          {
              boost::array<T,2> x;
              x[0] = a;
              x[1] = b;
              return x;
          }
          }
          

          带有-std=gnu++0x-O3 的GCC-4.6 生成完全相同的二进制代码

          auto x = boost::make_array(1,2);
          

          同时使用 AB 就像它对

          所做的那样
          boost::array<int, 2> x = {{1,2}};
          

          但是,对于用户定义类型 (UDT),变体 B 会导致额外的复制构造函数,这通常会减慢速度,因此应该避免。 p>

          请注意,boost::make_array 使用显式 char 数组字面量调用它时会出错,如下例所示

          auto x = boost::make_array("a","b");
          

          我相信这是一件好事,因为 const char* 文字在使用中可能会欺骗

          可变参数模板,自 4.5 起在 GCC 中可用,可进一步用于将每个 N 的所有模板特化样板代码减少为一个单个模板boost::make_array() 的定义定义为

          /*! Construct Array from @p a, @p b. */
          template <typename T, typename ... R>
          boost::array<T,1+sizeof...(R)> make_array(T a, const R & ... b)
          {
              return boost::array<T,1+sizeof...(R)>({{ a, b... }});
          }
          

          这非常符合我们的预期。第一个参数确定boost::array 模板参数T,所有其他参数都转换为T。在某些情况下,这可能是不可取的,但我不确定是否可以使用可变参数模板来指定。

          也许boost::make_array() 应该进入 Boost 库?

          【讨论】:

          • 谢谢,但 C++0x 在低端嵌入式处理器上不可用(C++ 编译器很难找到)
          【解决方案8】:

          这似乎可行,但我不相信它是正确的:

          #include <iostream>
          
          struct Foo { int x; Foo(int x): x(x) { } };
          
          struct Baz { 
               Foo foo[3];
          
              static int bar[3];
               // Hmm...
               Baz() : foo(bar) {}
          };
          
          int Baz::bar[3] = {4, 5, 6};
          
          int main() {
              Baz z;
              std::cout << z.foo[1].x << "\n";
          }
          

          输出:

          $ make arrayinit -B CXXFLAGS=-pedantic && ./arrayinit
          g++ -pedantic    arrayinit.cpp   -o arrayinit
          5
          

          告诫购买者。

          编辑:不,Comeau 拒绝它。

          另一个编辑:这是一种作弊,它只是将逐个成员的数组初始化推到不同的地方。所以它仍然需要 Foo 有一个默认的构造函数,但是如果你没有 std::vector 那么你可以自己实现你需要的绝对最低限度:

          #include <iostream>
          
          struct Foo { 
              int x; 
              Foo(int x): x(x) { }; 
              Foo(){}
          };
          
          // very stripped-down replacement for vector
          struct Three { 
              Foo data[3]; 
              Three(int d0, int d1, int d2) {
                  data[0] = d0;
                  data[1] = d1;
                  data[2] = d2;
              }
              Foo &operator[](int idx) { return data[idx]; }
              const Foo &operator[](int idx) const { return data[idx]; }
          };
          
          struct Baz { 
              Three foo;
          
              static Three bar;
              // construct foo using the copy ctor of Three with bar as parameter.
              Baz() : foo(bar) {}
              // or get rid of "bar" entirely and do this
              Baz(bool) : foo(4,5,6) {}
          };
          
          Three Baz::bar(4,5,6);
          
          int main() {
              Baz z;
              std::cout << z.foo[1].x << "\n";
          }
          

          z.foo 实际上并不是一个数组,但它看起来就像一个向量一样。将 begin()end() 函数添加到 Three 是微不足道的。

          【讨论】:

          • ...这给了我一些想法,这些想法可能比我所拥有的更干净地适用于我的情况。谢谢!
          【解决方案9】:

          不幸的是,在 C++0x 之前无法初始化数组成员。

          您可以在构造函数主体中使用 std::vector 和 push_back Foo 实例。

          你可以给 Foo 一个默认的构造函数(可能是私有的并且让 Baz 成为朋友)。

          您可以使用 可复制的数组对象(boost 或 std::tr1)并从静态数组初始化:

          #include <boost/array.hpp>
          
          struct Baz {
          
              boost::array<Foo, 3> foo;
              static boost::array<Foo, 3> initFoo;
              Baz() : foo(initFoo)
              {
          
              }
          };
          
          boost::array<Foo, 3> Baz::initFoo = { 4, 5, 6 };
          

          【讨论】:

          • +1。想知道为什么没有人想出这个,直到我看到你的答案。 array 实现起来很简单,既不疯狂也不疯狂。你也可以写一个像array&lt;Foo, 3&gt; create() { array&lt;Foo, 3&gt; a = { ... }; return a; } 这样的函数来避免静态变量。
          • 对我来说似乎也很明显,即使目标编译器对模板的支持很弱(没有std::vector 似乎很可疑),生成方法也会起作用(预处理器或脚本生成必要的类)。跨度>
          【解决方案10】:

          目前,您不能将初始化列表用于数组成员。你被困在艰难的道路上。

          class Baz {
              Foo foo[3];
          
              Baz() {
                  foo[0] = Foo(4);
                  foo[1] = Foo(5);
                  foo[2] = Foo(6);
              }
          };
          

          在 C++0x 中你可以这样写:

          class Baz {
              Foo foo[3];
          
              Baz() : foo({4, 5, 6}) {}
          };
          

          【讨论】:

          • 除非您显式声明构造函数,否则将为 int 调用单参数构造函数。
          • 很有趣...在我的示例中,我可能应该使用int 之外的其他东西,因为它太“容易”处理。 :-)
          【解决方案11】:

          来自扭曲头脑的想法:

          class mytwistedclass{
          static std::vector<int> initVector;
          mytwistedclass()
          {
              //initialise with initVector[0] and then delete it :-)
          }
          
          };
          

          现在在实例化一个对象之前将这个initVector 设置为你想要的东西。然后使用您的参数初始化您的对象。

          【讨论】:

            【解决方案12】:

            没有可以在这种情况下使用的数组构造语法,至少不能直接使用。您可以通过以下方式完成您想要完成的事情:

            Bar::Bar()
            {
                static const int inits [] = {4,5,6};
                static const size_t numInits = sizeof(inits)/sizeof(inits[0]);
                std::copy(&inits[0],&inits[numInits],foo);  // be careful that there are enough slots in foo
            }
            

            ...但是你需要给 Foo 一个默认的构造函数。

            【讨论】:

              【解决方案13】:

              在特定情况下,当数组是类的数据成员时,您不能在当前版本的语言中对其进行初始化。没有语法。要么为数组元素提供默认构造函数,要么使用std::vector

              可以使用聚合初始化器初始化独立数组

              Foo foo[3] = { 4, 5, 6 };
              

              但不幸的是,构造函数初始化列表没有对应的语法。

              【讨论】:

                【解决方案14】:

                在数组中创建对象时,只能调用默认构造函数。

                【讨论】:

                  猜你喜欢
                  • 2018-06-30
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-05-02
                  • 2013-11-17
                  • 2014-05-23
                  相关资源
                  最近更新 更多