又一个C ++对象初始化询问

时间:2012-03-28 15:55:31

标签: c++ initialization initializer-list

我有这个类有很多类成员,还有很多不同的构造函数。

到目前为止,我在每个构造函数中使用了构造函数初始化列表,以我想要的方式调整每个成员。

这非常繁琐,因为每次我向我的类添加一个新成员时,我必须访问每个构造函数并更新初始化列表以向该成员添加默认值。

所以,我想我会添加一个方法来初始化我需要的值。的问题!由于该方法是在初始化列表之后执行的,因此我在此初始化列表中放置的特定值会被我的方法覆盖。

快速例证:

class A
{
public:
  A();
  A( B b );
  A( int i );
  // A( .... ); plenty of them


private:
  int member1, m2, m3,m4;
  bool b1,b2, b3;
  // ....
  // every time I add a member I have to modify the initialization lists

  // solution: agregate member initialization in a member function: 
  void init_members();    
}

// init list constructors
A::A() : m1(false), m2(false), m3(false), m4(true) .... // looong list
{
}

A::A( B b) : m1(b.state()), m2(false), m3(false), ... // loong list
{
}

// problem, if I use init_members:
void A::init_members()
{
  m1 = false;
  m2 = false;
  m3 = false;
// ...
}

A::A( int i ) : m1( true)
{
  init_members(); // overrides m1 !!!
}

所以,我的问题是:我可以混合列表初始化器和方法初始化器,以便列表初始化器优先于方法初始化器吗?
在上面的示例中,我希望m1保留true作为最后一个构造函数。

注意:我知道在方法调用之后我可以移动初始化列表,但这意味着我会为成员分配两倍的值:一次在init_members()中,然后覆盖它在构造函数中。不够理想: - )

我希望有一些小技巧,如果你有库存。

3 个答案:

答案 0 :(得分:5)

在C ++ 11中,一个选项可以是构造函数委托,其中一个构造函数只是调用其他构造函数之一,以避免重复代码。

这就是它的样子:

class A {
public:
    A(int,int,double);
    A(int,int);
    A(double);
    A();
private:
    ...
};

A::A(int a,int b,double c) {
    // the real work to initialize the class
}

// A(int,int) delegates to A(int,int,double), passing along a
// default value for the double
A::A(int a,int b) : A(a,b,0.0) {}

A::A(double c) : A(1,2,c) {} // A(double) delegates to A(int,int,double)

A::A() : A(1.0) {} // A() delegates to A(double)

确保不创建任何循环。通常,您还需要一个构造函数来完成大部分实际工作,而其他构造函数只需编组它们想要传递给该构造函数的值。我们称之为“指定构造函数”。指定的构造函数应该是获取最多参数并且不使用任何默认值的构造函数。最终所有构造函数都应该直接或间接调用指定的构造函数。

注意模式:使用某些默认值的构造函数将这些默认值传递给使用较少默认值的构造函数,直到您到达没有默认值的函数。这与您尝试使用init_members()方法所做的相反。你有一个设置所有默认值的函数,然后你尝试覆盖它们中的一些。如果您不能使用C ++ 11功能,那么最好模拟指定的构造函数模式:init_members()将是您指定的初始化程序,它将没有任何默认值。您可以为每个构造函数使用初始化方法,该构造函数接受它给出的参数,并抛出一些默认值来调用另一个init_members重载。

然而,指定初始化程序/构造函数的一个问题是默认值遍布整个地方。除了委托之外,C ++ 11中的另一个选项是“类内初始化”,它允许将所有默认值收集在一起。

class A {
public:
    A(int,int,double);
    A(int,int);
    A(double);
    A();
private:
    int a = 1,b = 2; // in-class initialization gathers all the defaults together
    double c = 1.0;
};

鉴于上述情况,所有构造函数都会自动将成员值初始化为这些默认值,除非您将其显式初始化为该构造函数中的其他内容。

A::A(int a,int b,double c) : a(a), b(b), c(c) {}
A::A(int a,int b) : a(a), b(b) {} // member c is automatically initialized to 1.0
A::A(double c) : c(c) {} // members a and be are automatically initialized to 1 and 2
A::A() {}; // all members are initialized with their in-class values.

以下是使用init_members()

的示例
class A {
public:
    A(int a,int b,double c) { init_members(a,b,c); }
    A(int a,int b) { init_members(a,b); }
    A(double c) {init_members(c);}
    A() { init_members(); }
private:
    void init_members(int,int,double) { ... }
    void init_members(int a,int b) { init_members(a,b,1.0); }
    void init_members(double c) { init_members(1,2,c); }
    void init_members() { init_members(1.0); }
    ...
};

此方法值在调用init_members()之前初始化成员,因此成员初始化两次。我不确定是否有办法在C ++ 03中解决这个问题。

答案 1 :(得分:1)

对于这样的情况,我不使用Base / Member Initializer List(成员在构造函数中具有“garbage”或“default-constructor”值),并且我使用init_()函数(从构造函数体调用)。然后,构造函数调用init_()函数,并且只有一个维护点。

同样,我的clear()函数也会调用init_()函数,以获得“默认”值的单个维护点。

对于您的情况,它将是:

A::A(void)
//...no Base/Member-Initializer list...
{
  init_members();
}

A::clear(void)
{
  init_members();
}

...和覆盖:

A::A(int override_m1)
{
  init_members();
  m1 = override_m1;
}

答案 2 :(得分:0)

嗯,如果他们发现这样的课程,我不想知道你的同事给你的名字......想象一下如果他们必须修理或扩展这类怪物会发生什么。我新分配了一个任务来切换我们在代码库中的一些A(并且变得不可维护)到这样的事情:

class AKeyValueStorage {
    // would be some kind of shared storage if meant to be 
    // copyable and don't forget moving if your're on c++11!
    std::map<std::string, boost::any> mMembers;
public:
    template<class Key, class T>
    T const & Get(Key const & pKey) const
    {
        auto tTmp = mMembers.find(ToString(pKey));
        if (tTmp != mMembers.end()) {
            return boost::any_cast<T const &>(*tTmp); 
        }
        // throw if none, or return default
    }

    template<class Key, class T>
    void Set(Key const & pKey, T const & pValue) const
    {
        mMembers[ToString(pKey)] = pValue; // replace if found, insert if none
    }
};