一、继承构造函数
继承构造函数的引入原因:如果基类的构造函数很多,那么子类的构造函数想要实现同样多的构造接口,必须一一调用基类的构造函数,有点麻烦。
于是乎:C++11引入继承构造函数,子类可以通过使用using声明来声明继承基类的构造函数。
#include <iostream> using namespace std; class _A { public: _A(){;} _A(int _InInt) {;} _A(double _InDouble, int _InInt) {;} _A(float _InFloat, int _InInt, const char* _Char) {;} }; class _B : public _A { public: using _A::_A; // 使用_A中的构造函数 // Somthing... virtual void _ExtraInterface() {;} }; int main() { _A a; //正确 _A b(5); //正确 _A c(1.0, 4); //正确 _A d(1.0, 3, "hello"); //正确 return 0; }
使用using _A::_A; 把基类中的构造函数都继承到派生类_B中, 这样最开始的那段代码就可以摆脱基类那么多构造函数接口了。
更为精巧的是:C++11标准继承构造函数被设计为跟派生类中的各种类默认函数(默认构造、析构、拷贝等)一样,是隐式的。这意味着如果继承构造函数不被相关代码使用,编译器不会为其产生真正的函数代码,这无疑比透传方案更加节省目标代码空间。
但是:继承构造函数只会初始化基类中的成员变量(毕竟只是从基类继承下来的), 对于派生类中的变量初始化就无能为力。可以配合另一个C++11中的新特性: 数据成员就地初始化,来解决这个问题。但是,这种做法导致子类的成员不能在构造函数的参数列表中体现出来。
使用注意点:
(1) C++11中的继承构造函数特性最有用的场合就是:派生类只是在基类的基础上添加了几个新的接口, 这个时候继承构造函数最能够展现威力。
(2) 子类继承了基类的构造函数,那么子类就不会自动生成默认构造函数。
所以,如果子类新增了自己的成员变量,还是乖乖回到以前的初始化列表中去吧。
二、委派构造函数
所谓委派构造,就是指委派函数将构造的任务委派给目标构造函数来完成这样一种类构造的方式。
托构造函数允许类中的一个构造函数通过初始化列表方式来调用同一个类中的另一个构造函数。可以减少冗余代码和重复代码,能提高代码的可读性。
class Info { public: Info() : Info(1, 'a') { } Info(int i) : Info(i, 'a') { } Info(char e): Info(1, e) { } private: Info(int i, char e): type(i), name(e) { /* 其他初始化 */ } int type; char name; // ... };
在委托构造的链状关系中,有一点必须注意:就是不能形成委托环。如下:
struct Rule2 { int i, c; Rule2(): Rule2(2) {} Rule2(int i): Rule2('c') {} Rule2(char c): Rule2(2) {} };
Rule2定义中,Rule2()、Rule2(int)和Rule2(char)都依赖于别的构造函数,形成环委托构造关系。这样的代码通常会导致编译错误(gcc好像不会报错)。