委托构造函数(C++11)
1 委托构造函数
C++11标准支持了委托构造函数:某个类型的一个构造函数可以委托同类型的另一个构造函数对对象进行初始化。为了描述方便我们称前者为委托构造函数,后者为代理构造函数(英文直译为目标构造函数)。
1 2 3 4 5 6 7 8 9 10 11 12
   | class X { public:   X() : X(0, 0.) {}   X(int a) : X(a, 0.) {}   X(double b) : X(0, b) {}   X(int a, double b) : a_(a), b_(b) { CommonInit(); } private:   void CommonInit() {}   int a_;   double b_; };
 
  | 
 
执行顺序是先执行代理构造函数的初始化列表,接着执行代理构造函数的主体(也就是CommonInit函数),最后执行委托构造函数的主体。
注意事项
- 每个构造函数都可以委托另一个构造函数为代理。也就是说,可能存在一个构造函数,它既是委托构造函数也是代理构造函数
 
- 不要递归循环委托!这一点非常重要,因为循环委托不会被编译器报错,随之而来的是程序运行时发生未定义行为,最常见的结果是程序因栈内存用尽而崩溃
 
- 如果一个构造函数为委托构造函数,那么其初始化列表里就不能对数据成员和基类进行初始化
 
- 委托构造函数的执行顺序是先执行代理构造函数的初始化列表,然后执行代理构造函数的主体,最后执行委托构造函数的主体
 
- 如果在代理构造函数执行完成后,委托构造函数主体抛出了异常,则自动调用该类型的析构函数。
 
2 委托模板构造函数
委托模板构造函数是指一个构造函数将控制权委托到同类型的一个模板构造函数,简单地说,就是代理构造函数是一个函数模板。这样做的意义在于泛化了构造函数,减少冗余的代码的产生。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
   | #include <vector> #include <list> #include <deque>
  class X {   template<class T> X(T first, T last) : l_(first, last) { }   std::list<int> l_; public:   X(std::vector<short>&);   X(std::deque<int>&); }; X::X(std::vector<short>& v) : X(v.begin(), v.end()) { } X::X(std::deque<int>& v) : X(v.begin(), v.end()) { }
  int main() {   std::vector<short> a{ 1,2,3,4,5 };   std::deque<int> b{ 1,2,3,4,5 };   X x1(a);   X x2(b); }
 
  | 
 
3 捕获委托构造函数的异常
当使用Function-try-block去捕获委托构造函数异常时,其过程和捕获初始化列表异常如出一辙。如果一个异常在代理构造函数的初始化列表或者主体中被抛出,那么委托构造函数的主体将不再被执行,与之相对的,控制权会交到异常捕获的catch代码块中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
   | #include <iostream>
  class X { public:   X() try : X(0) {}   catch (int e)   {        std::cout << "catch: " << e << std::endl;        throw 3;   }   X(int a) try : X(a, 0.) {}   catch (int e)   {        std::cout << "catch: " << e << std::endl;        throw 2;   }   X(double b) : X(0, b) {}   X(int a, double b) : a_(a), b_(b) { throw 1; } private:   int a_;   double b_; };
  int main() {   try {        X x;   }   catch (int e) {        std::cout << "catch: " << e << std::endl;   } }
 
  | 
 
结果如下:
1 2 3
   | catch: 1 catch: 2 catch: 3
 
  | 
 
4 委托参数较少的构造函数
通常情况下,都是将参数较少的构造函数委托给参数较多的构造函数,因为这样做的自由度更高。但是,并不是完全否定从参数较多的构造函数委托参数较少的构造函数的意义。这种情况通常发生在构造函数的参数必须在函数体中使用的场景。