委派构造函数
引言
委派构造函数是C++11中新增的特性,目的是为了减少书写构造函数的时间。委托构造函数可以使用当前类的其他构造函数来帮助当前构造函数初始化。换而言之,就是可以将当前构造函数的部分(或者全部)职责交给本类的另一个构造函数。
先看一个不使用委托构造函数的例子:
1 class A 2 { 3 public: 4 A() : _x(0), _y(0), _z(0) { init(); } 5 A(int x) : _x(x), _y(0), _z(0) { init(); } 6 A(int x, int y) : _x(x), _y(y), _z(0) { init(); } 7 A(int x, int y, int z) : _x(x), _y(y), _z(z) { init(); } 8 9 private: 10 void init() { /*其他初始化*/ } 11 12 int _x; 13 int _y; 14 int _z; 15 };
上面代码中,除了初始化列表不同,其他部分均是相似的。为了构造对象,被迫书写重复代码。
委托构造函数
为了消灭上述重复代码,我们引入委托构造函数。C++11中的委派构造函数是在构造函数的初始化列表位置调用目标构造函数,将构造任务交由目标构造函数。
代码如下:
1 class A 2 { 3 public: 4 A() : A(0, 0, 0) { } //委托构造函数 5 A(int x) : A(x, 0, 0) { } //委托构造函数 6 A(int x, int y) : A(x, y, 0) { } //委托构造函数 7 8 private: 9 A(int x, int y, int z) : _x(x), _y(y), _z(z) { /*init()函数可以删除,在此完成init的功能*/ } //目标构造函数 10 11 int _x; 12 int _y; 13 int _z; 14 };
在上述代码中,A()、A(int x)、A(int x, int y)我们称之为委托构造函数。在委托构造函数中调用的构造函数,称之为目标构造函数。
委托构造函数的构造流程
构造流程为:委托构造函数初始化列表->目标构造函数初始化列表->目标构造函数函数体->委托构造函数函数体
1 struct A 2 { 3 A() { cout << "A Constructor" << endl; } 4 int _x = 10; 5 }; 6 7 struct B 8 { 9 B(const A& a) : B(a, 10) { cout << "B Delegate Constructor" << endl; } 10 11 B(const A& a, int x) :_a(a), _x(x) { cout << "B Target Constructor" << endl; } 12 13 A _a; 14 int _x; 15 }; 16 17 int main() 18 { 19 B b(A{}); 20 cout << "End!" << endl; 21 return 0; 22 }
输出如下:
A Constructor
B Target Constructor
B Delegate Constructor
End!
注意事项
1、初始化列表和“委派”不能同时进行
在委派构造函数中,不能同时调用初始化列表和目标构造函数,例如:
1 class A 2 { 3 public: 4 A() : A(10), _x(20){ } //error,委托构造函数不能具有其他成员初始化表达式 5 6 private: 7 A(int x) : _x(x){ /*init()函数可以删除,在此完成init的功能*/ } //目标构造函数 8 9 int _x; 10 };
解决办法是将初始化操作放在函数体中,但随着也会引发其他问题:
1 class A 2 { 3 public: 4 A() : A(10) { _x = 20; } //将初始化放在函数体中 5 6 private: 7 A(int x) : _x(x) { _x += 10; } //可能导致 _x += 10 的执行结果被后续计算覆盖 8 9 int _x; 10 };
因为是先调用目标构造函数,如果在目标构造函数体中进行计算操作,在回到委托构造函数体中,在此进行复制会导致值被覆盖。
2、避免循环委托
c++语法没有限制在目标构造函数继续调用目标构造函数,即目标构造函数也可能是委托构造函数。这可能导致A构造函数委托B构造函数完成初始化,而B构造函数中又委托A构造函数完成初始化,从而导致循环委托。代码如下:
1 struct A 2 { 3 A(int x) : A(9.5) {} 4 A(double d) : A(2) {} 5 6 int _x; 7 double _d; 8 };
3、异常捕获
如果在委派构造函数中使用try的话,那么从目标构造函数中产生的异常,都可以在委派构造函数中被捕捉到。
1 class A 2 { 3 public: 4 A(int x) try : A(4.5) 5 { 6 cout << "Delegate Constructor" << endl; 7 } 8 catch (...) 9 { 10 cout << "Caught Exception" << endl; 11 } 12 13 A(double d) 14 { 15 cout << "throw 0" << endl; 16 throw 0; 17 } 18 };
输出:
throw 0 Caught Exception
应用-构造模板函数
委派构造的一个很实际的应用就是使用构造模板函数产生目标构造函数,代码如下:
1 class TDConstructed 2 { 3 template<typename T> 4 TDConstructed(T first, T last) : l(first, last) {} 5 6 public: 7 TDConstructed(vector<short>& v) : TDConstructed(v.begin(), v.end()) {} 8 TDConstructed(deque<int>& d) : TDConstructed(d.begin(), d.end()) {} 9 10 void disp() 11 { 12 for (auto& itr : m_list) 13 cout << itr << endl; 14 } 15 16 private: 17 list<int> m_list; 18 }; 19 20 int main() 21 { 22 vector<short> vec = { 1,2,3,4,5 }; 23 TDConstructed td(vec); 24 td.disp(); 25 return 0; 26 }
通过模板,使得TDConstructed 能兼容各种容器,非常便捷的完成初始化。
参考
深入理解C++11:C++11新特性解析与应用