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

Ped*_*ino 11 c++ class member-initialization

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

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);
    }
};
Run Code Online (Sandbox Code Playgroud)

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

编辑:我之前应该说过,但我的实际问题有一个额外的复杂性:我需要在初始化m_class之前调用一个方法,以便设置环境.所以:

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);
    }
};
Run Code Online (Sandbox Code Playgroud)

是否有可能通过花哨的初始化列表技巧实现这一目标?

Joh*_*itb 25

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

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)) {

    }
};
Run Code Online (Sandbox Code Playgroud)

要在初始化m_class之前调用函数,可以在该成员之前放置一个struct并利用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)) {

    }
};
Run Code Online (Sandbox Code Playgroud)

这将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)) {

    }
};
Run Code Online (Sandbox Code Playgroud)

它使用逗号运算符.请注意,您可以do_something使用function-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 */ }
};
Run Code Online (Sandbox Code Playgroud)

do_something如果它抛出导致MyClass无法创建对象的异常,则下次再次调用该函数.希望这可以帮助 :)


Tod*_*ner 5

使用初始化列表语法:

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*/)
    { }
};
Run Code Online (Sandbox Code Playgroud)

工厂可能更干净:

MemberClass create_member(int x){
   if(xyz == 42)
     return MemberClass(12);
    // ...
}

//...
 MyClass(int xyz) : m_class(create_member(xyz))
Run Code Online (Sandbox Code Playgroud)

  • 您正在创建一个未命名的对象,然后使用copy-ctor初始化该成员,而不是直接初始化它. (2认同)

Jam*_*ran 5

 MyClass(int xyz) : m_class(xyz==42 ? 12 : 32) {}
Run Code Online (Sandbox Code Playgroud)

要回答你修改过的问题,这有点棘手.最简单的方法是制作m_class一个指针.如果您真的希望它作为数据成员,那么您必须具有创造性.创建一个新类(最好是在MyClass内部定义).它是否是需要被调用的函数.首先将其包含在数据成员的声明中(这将使其成为第一个实例).

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.
    }
};
Run Code Online (Sandbox Code Playgroud)