Categories
不学无术

C++11 继承构造函数、委派构造函数

C++11标准中引入了两种构造函数的活用方法,可以适当地减轻一些情景下的码字负担。

1.继承构造函数

以往情况下,在派生类中调用基类的构造函数,一般需要在派生类中显式调用:

struct A
{
    A(int i) {}
};
struct B
{
    B(int i) : A(i) {}
};

当构造函数的花式(签名)很多的时候,继承类写起来就比较辛苦了。
在C++11标准中,可以通过using声明来简化这种操作(using声明在以前的标准中已经可以用在非构造函数里)

struct A
{
    A(int i) {}
    A(double d, int i) {}
    A(float f, double d, int i) {}
    //...
};
struct B
{
    using A::A;    //继承构造函数
    //...
};

需要注意的是,采用这种using声明有两个规矩:

  • 如果基类的构造函数是private或是派生类是从基类中虚继承的,不能用这种方法;
  • 一旦用了继承构造函数,编译器就不会为派生类生成默认构造函数(Visual Studio报error C2280),除非基类自己没有定义任何构造函数而触发了默认构造函数生成;

2.委派构造函数

委派构造函数常用在减少初始化过程的代码冗余问题,比如下面这个经典例子(我就这么干过):

class Info
{
public:
    Info() { InitRest(); }
    Info(int i) : type(i) { InitRest(); }
    Info(char c) : name(c) { InitRest();}
private:
    void InitRest ()
    {
        // 初始化其他东西
    }
    int type {1};
    char name {'a'};
    //...
};

使用一般方法,我们无法在自己构造函数里又调用自己的构造函数(突然想起了python的import this,哈哈),纵使函数签名不同也会报编译错误。
C++11中,可以这么写:

class Info
{
public:
    Info() { InitRest(); }
    Info(int i) : Info() { type = i; }
    Info(char c) : Info() { name = c; }
private:
    void InitRest ()
    {
        // 初始化其他东西
    }
    int type {1};
    char name {'a'};
    //...
};

但是要注意,委派构造函数不能和初始化列表并用,不然依旧编译错误。
比如我们不能搞Info(int i) : Info(), type(i) { } 。
不过有解决办法,比如可以声明一个私有的构造函数,它包括“完整”的参数输入,然后公有的其他构造函数调用它:

class Info
{
public:
    Info() : Info(1, 'a') { } // 或采用链状委派: Info(1) 或 Info('a')
    Info(int i) : Info(i, 'a') { }
    Info(char c) : Info(1, c) { }
private:
    Info(int i, char e) : type(i), name(c) { }
    int type {1};
    char name {'a'};
    //...
};

关于函数执行顺序,目标函数执行总是先于委派构造函数的。

参考文献

《深入理解C++11 (C++11新特性解析与应用)》

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.