2009-06-18 51 views
10

我确定这是一个非常简单的问题。下面的代码显示了我想要做的事:有条件地初始化C++成员变量的正确方法?

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正在用一个空的构造(不存在)创建。这样做的正确方法是什么?我的猜测是使用指针并使用new实例化m_class,但我希望有一种更简单的方法。

编辑:我应该早些说过,但我的实际问题有一个额外的复杂:我需要在初始化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); 
    } 
}; 

是否有可能实现这个花哨的初始化列表技巧?

回答

24

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

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

    } 
}; 

这之前会调用do_something()初始化m_class之前。请注意,在构造函数初始化程序列表完成之前,不允许调用MyClass的非静态成员函数。该函数必须是其基类的成员,并且必须已经完成基类的工作。

另请注意,对于创建的每个独立对象,不仅对于创建的第一个对象,当然总是调用该函数。如果你想这样做,你可以在初始化程序的构造函数中创建一个静态变量:

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 */ } 
}; 

do_something功能将再次下一次调用,如果把它扔到导致该MyClass对象不能被创建例外。希望这有助于:)

+0

谢谢!我更新了我的问题 - 我实际上需要在创建m_class之前运行一个方法。那可能吗?也许我可以在“classInit”中做到这一点,但那不会很优雅。 – 2009-06-18 19:11:01

+0

了解了很多,谢谢! :-) – 2009-06-18 19:42:02

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*/) 
    { } 
}; 

与工厂大概清洁:

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

//... 
MyClass(int xyz) : m_class(create_member(xyz)) 
+2

您正在创建一个未命名的对象,然后使用copy-ctor初始化成员,而不是直接初始化它。 – 2009-06-18 18:58:55

+0

是的,打字太快,为我自己的好。就我个人而言,我仍然将逻辑移入MemberClass或工厂。 – 2009-06-18 19:00:05

5
MyClass(int xyz) : m_class(xyz==42 ? 12 : 32) {} 

要回答你的修订问题,即变得有点棘手。最简单的方法是将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. 
    } 
}; 
0

或者:

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); 
    } 
}; 

如果你不知何故仍然要保持相同的语法。会员创新虽然效率更高。

0

试试这个:

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

这使得它的默认值,并且您的默认构造函数。

0

要让初始化发生其他的事情发生后,你确实需要使用指针,这样的事情:

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.counterdelete m_pClass在析构函数中。