1

我有这个类,它有许多类成员和许多不同的构造函数。

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

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

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

快速示例:

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(),然后在构造函数中覆盖它。不够优化:-)

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

4

3 回答 3

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 中有没有办法解决这个问题。

于 2012-03-28T17:23:27.457 回答
1

对于这样的情况,我不使用 Base/Member Initializer List(成员在构造函数中的那个点具有“垃圾”或“默认构造函数”值),而是使用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;
}
于 2012-03-28T15:58:21.600 回答
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
    }
};
于 2012-03-28T16:34:07.217 回答