【问题标题】:Calling another constructor when constructing an object with const members构造具有 const 成员的对象时调用另一个构造函数
【发布时间】:2012-08-15 09:02:10
【问题描述】:

我有一个包含const 成员的类,一个构造函数调用另一个填充了额外值的构造函数。通常我可以为此使用冒号初始化程序,但函数很复杂(printf/sprintf- like) 并要求我在堆栈上使用一个变量,因此我必须在构造函数的主体中执行此操作,并将 *this 分配给新对象。但是这当然是无效的,因为我的成员变量是const

class A
{
public:
    A(int b) : b(b), c(0), d(0) // required because const
    {
        int newC = 0;
        int newD = 0;
        myfunc(b, &newC, &newD);
        *this = A(b, newC, newD); // invalid because members are const

        // "cannot define the implicit default assignment operator for 'A', because non-static const member 'b' can't use default assignment operator"
        // or, sometimes,
        // "error: overload resolution selected implicitly-deleted copy assignment operator"
    };
    A(int b, int c, int d) : b(b), c(c), d(d) { };

    const int b;
    const int c;
    const int d;
};

A a(0);

(我没有明确删除赋值运算符。)我声明成员 const 是因为我希望它们是公共的,但不是可变的。

是否有一些规范的方法可以在不使用可怕的演员表和强制覆盖成员的constness 的情况下解决这个问题?这里最好的解决方案是什么?

【问题讨论】:

    标签: c++ constructor constants


    【解决方案1】:

    我更喜欢Kerrek SB's answer,但在您的情况下,您无法轻松地为每个成员创建单独的初始化函数。

    在这种情况下,另一种解决方案是将成员移动到基类,并使用具有非常量成员的辅助类初始化该基类。您的初始化代码已移至帮助程序类的构造函数,并且可以毫无问题地分配。

    class A_init
    {
      public:
        A_init(int b)
        {
          // do whatever you like with c and d:
          c = ...;
          d = ...;
        }
    
        int c; // Note: non-const
        int d; // Note: non-const
    };
    
    class A_base
    {
       public:
         A_base(int b, A_init init) : b(b), c(init.c), d(init.d) {}
         A_base(int b, int c, int d) : b(b), c(c), d(d) {}
    
         const int b;
         const int c;
         const int d;
    };
    
    class A : public A_base
    {
      public:
        A(int b) : A_base(b, A_init(b)) {}
        A(int b, int c, int d) : A_base(b, c, d) {}
    };
    

    如果想限制对A_init 的访问,可以切换到私人并声明A 为朋友。

    【讨论】:

      【解决方案2】:

      您可以添加参数类并使用 C++11 构造函数委托或基类:

      struct parameters {
          int b; int c; int d;
          parameters(int b): b(b), c(), d() {
              myfunc(b, &c, &d);
          }
      };
      
      // constructor delegation
      class A {
      public:
          A(int b): A(parameters(b)) { }
          A(parameters p): b(p.b), c(p.c), d(p.d) { }
      };
      
      // base/wrapper
      class ABase {
          ABase(parameters p): b(p.b), c(p.c), d(p.d) { }
      };
      
      class A: public ABase {
      public:
          A(int b): ABase(parameters(b)) { }
      };
      

      【讨论】:

        【解决方案3】:

        myfunc 的结果放在哪里,以便可以从不同的内存初始化器中设置和使用?在默认参数中怎么样?

        class A
        {
        private:
            struct InitData;
        public:
            A(int b, InitData data=InitData());
            A(int b, int c, int d) : b(b), c(c), d(d) { };
        
            const int b;
            const int c;
            const int d;
        };
        
        struct A::InitData
        {
            int setup(int b);
            int c;
            int d;
        };
        
        inline int A::InitData::setup(int b)
        {
            myfunc(b, &c, &d);
            return b;
        }
        
        inline A::A(int b_, InitData data)
            : b(data.setup(b_)),
              c(data.c),
              d(data.d)  {}
        
        A a(0);
        

        由于组成的类型是私有的并且没有转换,因此意外使用或滥用它的风险很小。

        【讨论】:

        • 我想补充一点,初始化是按照成员的声明的顺序完成的,而不是按照初始化列表中的顺序完成的。这里两者一致,所以没有问题。但是假设有人移动'const int c;'在 'const int b;' 之前,你会突然在 A::A 中遇到一个错误,因为 c(data.c) 会在 data.setup(b_) 被调用之前执行。
        【解决方案4】:

        制作一个辅助函数怎么样:

        class A
        {
            static int initializor(int b) { int n; myfunc(b, &n); return n; }
        public:
            explicit A(int b_) : b(b_), c(initializor(b_)) { }
            A(int b_, int c_)  : b(b_), c(c_)              { }
        
            // ... as before ...
        };
        

        【讨论】:

        • 我喜欢这个主意! — 棘手的部分是我实际上有不止一个变量,所以它更像myfunc(b, &c, &d, &e)。有没有办法对多个变量执行此操作?
        • @jtbandes:可能是一个 A make_A(int, int, int) 函数,它计算必要的条款并返回一个适当构造的 A
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-02-10
        • 1970-01-01
        • 1970-01-01
        • 2011-03-24
        • 1970-01-01
        • 2018-08-15
        • 2022-01-23
        相关资源
        最近更新 更多