代码之家  ›  专栏  ›  技术社区  ›  Pedro d'Aquino

有条件地初始化C++成员变量的正确方法?

  •  10
  • Pedro d'Aquino  · 技术社区  · 17 年前

    我相信这是一个非常简单的问题。以下代码显示了我正在尝试执行的操作:

    class MemberClass {
    public:
        MemberClass(int abc){ }
    };
    
    class MyClass {
    public:
        MemberClass m_class;
        MyClass(int xyz) {
            if(xyz == 42)
                m_class = MemberClass(12);
            else
                m_class = MemberClass(32);
        }
    };
    

    这不会编译,因为 m_class 正在使用空构造函数(不存在)创建。正确的方法是什么?我猜是使用指针和实例化 M级 使用 new 但是我希望有一个更简单的方法。

    编辑: 我应该早点说,但是我的实际问题还有一个额外的复杂之处:我需要在初始化m_类之前调用一个方法,以便设置环境。所以:

    class MyClass {
    public:
        MemberClass m_class;
        MyClass(int xyz) {
            do_something(); // this must happen before m_class is created
            if(xyz == 42)
                m_class = MemberClass(12);
            else
                m_class = MemberClass(32);
        }
    };
    

    是否可以通过花哨的初始化列表技巧来实现这一点?

    6 回复  |  直到 17 年前
        1
  •  24
  •   Johannes Schaub - litb    17 年前

    使用条件运算符。如果表达式更大,请使用函数

    class MyClass {
    public:
        MemberClass m_class;
        MyClass(int xyz) : m_class(xyz == 42 ? 12 : 32) {
    
        }
    };
    
    class MyClass {
        static int classInit(int n) { ... }
    public:
        MemberClass m_class;
        MyClass(int xyz) : m_class(classInit(xyz)) {
    
        }
    };
    

    要在初始化m_类之前调用函数,可以在该成员之前放置一个结构并利用raii

    class MyClass {
        static int classInit(int n) { ... }
        struct EnvironmentInitializer {
            EnvironmentInitializer() {
                do_something();
            }
        } env_initializer;
    public:
        MemberClass m_class;
        MyClass(int xyz) : m_class(classInit(xyz)) {
    
        }
    };
    

    这将呼叫 do_something() 初始化前 m_class . 请注意,不允许调用的非静态成员函数 MyClass 在构造函数初始值设定项列表完成之前。函数必须是其基类的成员,并且必须完成基类的ctor才能使其工作。

    当然,还需要注意的是,对于创建的每个单独的对象,函数总是被调用,而不仅仅是对于创建的第一个对象。如果要这样做,可以在初始值设定项的构造函数中创建静态变量:

    class MyClass {
        static int classInit(int n) { ... }
        struct EnvironmentInitializer {
            EnvironmentInitializer() {
                static int only_once = (do_something(), 0);
            }
        } env_initializer;
    public:
        MemberClass m_class;
        MyClass(int xyz) : m_class(classInit(xyz)) {
    
        }
    };
    

    它使用逗号运算符。请注意,您可以捕获由 do_something 通过使用函数try块

    class MyClass {
        static int classInit(int n) { ... }
        struct EnvironmentInitializer {
            EnvironmentInitializer() {
                static int only_once = (do_something(), 0);
            }
        } env_initializer;
    public:
        MemberClass m_class;
        MyClass(int xyz) try : m_class(classInit(xyz)) {
    
        } catch(...) { /* handle exception */ }
    };
    

    这个 多西 如果函数抛出导致 类名 无法创建对象。希望这有帮助:)

        2
  •  5
  •   Todd Gardner    17 年前

    使用初始值设定项列表语法:

    class MyClass {
    public:
        MemberClass m_class;
        MyClass(int xyz) : m_class(xyz == 42 ? MemberClass(12) : MemberClass(32)
                                   /* see the comments, cleaner as xyz == 42 ? 12 : 32*/)
        { }
    };
    

    可能是工厂的清洁剂:

    MemberClass create_member(int x){
       if(xyz == 42)
         return MemberClass(12);
        // ...
    }
    
    //...
     MyClass(int xyz) : m_class(create_member(xyz))
    
        3
  •  5
  •   James Curran    17 年前
     MyClass(int xyz) : m_class(xyz==42 ? 12 : 32) {}
    

    要回答你修改过的问题,会有点棘手。最简单的方法是 m_class 指针。如果你真的想把它作为一个数据成员,那么你必须要有创造性。创建一个新类(最好是在MyClass内部定义它)。让它的ctor成为需要调用的函数。包括在内 第一 在数据成员的声明中(这将使其成为第一个实例)。

    class MyClass 
    {
         class initer { public: initer() {
                        // this must happen before m_class is created
                        do_something();                        
                        }
                       }
    
        initer     dummy;
    public:
    
        MemberClass m_class;
        MyClass(int xyz) : m_class(xyz==42? 12 : 43)
        {
            // dummy silently default ctor'ed before m_class.
        }
    };
    
        4
  •  0
  •   ralphtheninja    17 年前

    或:

    class MemberClass {
    public:
        MemberClass(int abc){ }
    };
    
    class MyClass {
    public:
        MemberClass* m_class;
        MyClass(int xyz) {
            if(xyz == 42)
                m_class = new MemberClass(12);
            else
                m_class = new MemberClass(32);
        }
    };
    

    如果你仍然想保持相同的语法。不过,成员初始化更有效。

        5
  •  0
  •   Kieveli    17 年前

    试试这个:

    class MemberClass
    {
    public:    
       MemberClass(int abc = 0){ }
    };
    

    这会给它一个默认值和您的默认构造函数。

        6
  •  0
  •   Aric TenEyck    17 年前

    要在其他事情发生后进行初始化,确实需要使用指针,如下所示:

    class MyClass {
    public:
        MemberClass * m_pClass;
        MyClass(int xyz) {
            do_something(); // this must happen before m_class is created
            if(xyz == 42)
                m_pClass = new MemberClass(12);
            else
                m_pClass = new MemberClass(32);
        }
    };
    

    唯一的区别是您需要访问成员变量 m_pClass->counter 而不是 m_class.counter delete m_pClass 在析构函数中。

    推荐文章