【问题标题】:initialize a const array in a class initializer in C++在 C++ 中的类初始化器中初始化 const 数组
【发布时间】:2010-09-14 18:38:57
【问题描述】:

我在 C++ 中有以下类:

class a {
    const int b[2];
    // other stuff follows

    // and here's the constructor
    a(void);
}

问题是,我如何在初始化列表中初始化b,因为我无法在构造函数的函数体内初始化它,因为b是const

这不起作用:

a::a(void) : 
    b([2,3])
{
     // other initialization stuff
}

编辑:典型的例子是我可以为不同的实例设置不同的 b 值,但已知这些值在实例的生命周期内是恒定的。

【问题讨论】:

    标签: c++ initialization c++03 array-initialize


    【解决方案1】:

    使用 C++11,这个问题的答案现在已经改变,您实际上可以这样做:

    struct a {
        const int b[2];
        // other bits follow
    
        // and here's the constructor
        a();
    };
    
    a::a() :
        b{2,3}
    {
         // other constructor work
    }
    
    int main() {
     a a;
    }
    

    【讨论】:

    • 请注意,也可以像这样在类定义中设置值:struct a {const int b[2]{2, 3};};。仍然可以在构造函数初始化器列表中将数组初始化为不同的值,就像这个答案所示。
    【解决方案2】:

    正如其他人所说,ISO C++ 不支持这一点。但是你可以解决它。只需使用 std::vector 代替。

    int* a = new int[N];
    // fill a
    
    class C {
      const std::vector<int> v;
    public:
      C():v(a, a+N) {}
    };
    

    【讨论】:

    • 这样做的问题是它使用了导致额外开销的向量。
    • 问题不在于它使用向量,或者一种内存与另一种内存。这是您不能使用任意一组值直接初始化向量。 @CharlesB 的技术将与 boost 或 std 一起使用,分两步完成。
    • 您总是可以使用std::array 来避免一些开销。
    【解决方案3】:

    在当前标准中是不可能的。我相信您将能够在 C++0x 中使用初始化列表来完成此操作(请参阅 Bjarne Stroustrup 的 A Brief Look at C++0x,了解有关初始化列表和其他不错的 C++0x 功能的更多信息)。

    【讨论】:

      【解决方案4】:

      std::vector 使用堆。天哪,这只是为了const sanity-check 而造成的浪费。 std::vector 的重点是运行时的动态增长,而不是任何应该在编译时进行的旧语法检查。如果你不打算增长,那么创建一个类来包装一个普通数组。

      #include <stdio.h>
      
      
      template <class Type, size_t MaxLength>
      class ConstFixedSizeArrayFiller {
      private:
          size_t length;
      
      public:
          ConstFixedSizeArrayFiller() : length(0) {
          }
      
          virtual ~ConstFixedSizeArrayFiller() {
          }
      
          virtual void Fill(Type *array) = 0;
      
      protected:
          void add_element(Type *array, const Type & element)
          {
              if(length >= MaxLength) {
                  // todo: throw more appropriate out-of-bounds exception
                  throw 0;
              }
              array[length] = element;
              length++;
          }
      };
      
      
      template <class Type, size_t Length>
      class ConstFixedSizeArray {
      private:
          Type array[Length];
      
      public:
          explicit ConstFixedSizeArray(
              ConstFixedSizeArrayFiller<Type, Length> & filler
          ) {
              filler.Fill(array);
          }
      
          const Type *Array() const {
              return array;
          }
      
          size_t ArrayLength() const {
              return Length;
          }
      };
      
      
      class a {
      private:
          class b_filler : public ConstFixedSizeArrayFiller<int, 2> {
          public:
              virtual ~b_filler() {
              }
      
              virtual void Fill(int *array) {
                  add_element(array, 87);
                  add_element(array, 96);
              }
          };
      
          const ConstFixedSizeArray<int, 2> b;
      
      public:
          a(void) : b(b_filler()) {
          }
      
          void print_items() {
              size_t i;
              for(i = 0; i < b.ArrayLength(); i++)
              {
                  printf("%d\n", b.Array()[i]);
              }
          }
      };
      
      
      int main()
      {
          a x;
          x.print_items();
          return 0;
      }
      

      ConstFixedSizeArrayFillerConstFixedSizeArray 是可重复使用的。

      第一个允许在初始化数组时进行运行时边界检查(与向量可能相同),在此初始化之后可以稍后变为 const

      第二个允许将数组分配在内部另一个对象中,该对象可以在堆上,或者如果对象所在的位置则只是堆栈。从堆中分配不会浪费时间。它还对数组执行编译时常量检查。

      b_filler 是一个提供初始化值的小型私有类。数组的大小在编译时使用模板参数进行检查,因此不会超出范围。

      我相信还有更多奇特的方法可以修改它。这是一个初步的刺。我认为您几乎可以通过类弥补编译器的任何缺点。

      【讨论】:

      • 它在堆上有什么关系?内存将在对象的整个生命周期中使用,无论它是在堆上还是在堆栈上。考虑到许多架构在同一块内存的相对两侧都有堆和堆栈,因此理论上它们可以在中间相遇,为什么对象所在的位置很重要?
      • @Nathan Fellman :这可能被视为过度优化,但在某些情况下,您希望您的对象进行零分配(用于在堆栈上使用)。在这种情况下,new 太多了,如果您在编译时知道需要多少,甚至更多。例如,std::vector 的某些实现确实将其项目分配在内部缓冲区中,而不是使用 new,这使得小向量的构造/销毁非常便宜。
      • 有时,编译器会优化到足以使std::vector 和数组产生完全相同的代码。天哪。
      【解决方案5】:

      ISO 标准 C++ 不允许您这样做。如果是这样,语法可能是:

      a::a(void) :
      b({2,3})
      {
          // other initialization stuff
      }
      

      或者类似的东西。从您的问题来看,实际上您想要的是一个常量类(又名静态)成员,它是数组。 C++ 确实允许您这样做。像这样:

      #include <iostream>
      
      class A 
      {
      public:
          A();
          static const int a[2];
      };
      
      const int A::a[2] = {0, 1};
      
      A::A()
      {
      }
      
      int main (int argc, char * const argv[]) 
      {
          std::cout << "A::a => " << A::a[0] << ", " << A::a[1] << "\n";
          return 0;
      }
      

      输出是:

      A::a => 0, 1
      

      当然,既然这是一个静态类成员,它对于类 A 的每个实例都是相同的。如果这不是您想要的,即您希望 A 的每个实例在数组 a 中具有不同的元素值,那么您'正在尝试使数组 const 开始的错误。你应该这样做:

      #include <iostream>
      
      class A 
      {
      public:
          A();
          int a[2];
      };
      
      A::A()
      {
          a[0] = 9; // or some calculation
          a[1] = 10; // or some calculation
      }
      
      int main (int argc, char * const argv[]) 
      {
          A v;
          std::cout << "v.a => " << v.a[0] << ", " << v.a[1] << "\n";
          return 0;
      }
      

      【讨论】:

      • 为什么以 const 开头的数组是错误的?如果我希望值在实例的生命周期内保持不变,比如某种 id,该怎么办?
      • 那么你应该使用枚举类型。
      • 这里如何使用枚举类型?
      【解决方案6】:

      我有一个常量数组,它总是作为静态的。如果你能接受,这段代码应该可以编译并运行。

      #include <stdio.h>
      #include <stdlib.h>
      
      class a {
              static const int b[2];
      public:
              a(void) {
                      for(int i = 0; i < 2; i++) {
                              printf("b[%d] = [%d]\n", i, b[i]);
                      }
              }
      };
      
      const int a::b[2] = { 4, 2 };
      
      int main(int argc, char **argv)
      {
              a foo;
              return 0;
      }
      

      【讨论】:

      • 假设我确实想要一个静态成员,但情况并非总是如此。事实上,我可以有一个 const 数组,它为类的不同实例具有不同的值,但值在类的生命周期内永远不会改变
      • 好吧,如果你的构造函数没有参数,那么所有的实例化都会有相同的值。除此之外,你是对的。
      • “ISO 标准 C++ 不允许你” - 最好指定你心目中的 ISO C++ 标准版本
      【解决方案7】:

      你不能从初始化列表中做到这一点,

      看看这个:

      http://www.cprogramming.com/tutorial/initialization-lists-c++.html

      :)

      【讨论】:

        【解决方案8】:

        不使用std::vector 的堆的解决方案是使用boost::array,尽管您不能直接在构造函数中初始化数组成员。

        #include <boost/array.hpp>
        
        const boost::array<int, 2> aa={ { 2, 3} };
        
        class A {
            const boost::array<int, 2> b;
            A():b(aa){};
        };
        

        【讨论】:

          【解决方案9】:

          如何通过访问函数模拟 const 数组?它是非静态的(根据您的要求),并且不需要 stl 或任何其他库:

          class a {
              int privateB[2];
          public:
              a(int b0,b1) { privateB[0]=b0; privateB[1]=b1; }
              int b(const int idx) { return privateB[idx]; }
          }
          

          因为 a::privateB 是私有的,所以它在 a:: 之外实际上是常量,您可以像访问数组一样访问它,例如

          a aobj(2,3);    // initialize "constant array" b[]
          n = aobj.b(1);  // read b[1] (write impossible from here)
          

          如果你愿意使用一对类,你可以额外保护 privateB 免受成员函数的影响。这可以通过继承a来完成;但我想我更喜欢John Harrison's comp.lang.c++ post using a const class.

          【讨论】:

          • 这是一个有趣的方法!谢谢!
          【解决方案10】:

          有趣的是,在 C# 中,关键字 const 可以转换为 C++ 的静态 const,而不是 readonly,它只能在构造函数和初始化中设置,即使是非常量,例如:

          readonly DateTime a = DateTime.Now;
          

          我同意,如果您有一个 const 预定义数组,您不妨将其设为静态。 此时你可以使用这个有趣的语法:

          //in header file
          class a{
              static const int SIZE;
              static const char array[][10];
          };
          //in cpp file:
          const int a::SIZE = 5;
          const char array[SIZE][10] = {"hello", "cruel","world","goodbye", "!"};
          

          但是,我没有找到绕过常量“10”的方法。原因很清楚,它需要它知道如何执行对数组的访问。一种可能的替代方法是使用#define,但我不喜欢该方法,我在标题末尾添加了#undef,并在 CPP 中添加注释以防发生更改。

          【讨论】:

            猜你喜欢
            • 2023-03-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-01-17
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多