在代码开发中,C语言和C++都是基础语言,是很多web开发人员的入门级必学语言。但在C++98 中,类成员构造问题还存在一些问题,为此C++11提出了一些新特性。
C++98中如果一个类有多个构造函数且要实现类成员构造,这些构造函数通常要包含基本相同的类成员构造代码。在最坏的情况下,相同的类成员构造语句被拷贝粘贴在每一个构造函数中。
基于C++98中的类成员构造问题,C++11新特性中,程序员可以将公有的类成员构造代码集中在某一个构造函数里,这个函数被称为目标构造函数。其他构造函数通过调用目标构造函数来实现类成员构造,这些构造函数被称为委派构造函数。在该新特性提出之前,构造函数是不能显式被调用的,委派构造函数打破了这一限制。下面小编将和大家一起来看看C++11委派构造函数的使用方法。
1、委派构造函数的使用
为了详细了解C++11的新特性,我们可以通过下面这个例子来看看具体是怎么使用的:
程序源代码 (delegatingctor1.cpp)
class A{
public:
// A(int i)为 A()的委派构造函数
A(): A(0){}
// A(int i, int j)为 A(int i)的委派构造函数
A(int i): A(i, 0){}
// 委派构造链为 A()->A(int i)->A(int i, int j)
A(int i, int j) {
num1=i;
num2=j;
average=(num1+num2)/2;
}
private:
int num1;
int num2;
int average;
};
从上面这个例子中,可以看出,在构造函数 A()的初始化列表里,程序调用了 A(0), 这就是委派构造函数的语法。 我们称 A(int i)为 A()的目标构造函数,而 A()为 A(int i)的委派构造函数。同理,A(int i, int j)为 A(int i)的目标构造函数,而 A(int i) 为 A(int i, int j)的委派构造函数。在利用了委派构造函数后,整个程序变得更加的清楚和简洁。目标构造函数和委派构造函数跟其他普通的构造函数一样有相同的接口和语法,它们并没有特殊的处理和标签。通过这个例子,我们也可以看出C++11中一个委派构造函数可以是另一个委派构造函数的目标构造函数,委派构造函数和目标构造函数是相对而言的。目标构造函数是通过重载和类参数推导准则而选定的。
在C++函数委派过程中,当目标构造函数函数执行完毕后,委派构造函数继续执行它自己函数体内的其他语句。可以通过下面这个例子的方式实现:
程序源代码 (delegatingctor2.cpp)
#include <iostream>
using namespace std;
class A{
public:
A(): A(0){ cout << "In A()" << endl;}
A(int i): A(i, 0){cout << "In A(int i)" << endl;}
A(int i, int j){
num1=i;
num2=j;
average=(num1+num2)/2;
cout << "In A(int i, int j)" << endl;
}
private:
int num1;
int num2;
int average;
};
int main(){
A a;
return 0;
}
该例子的输出为:
In A(int i, int j)
In A(int i)
In A()
2、委派构造函数的异常处理
当目标构造函数抛出异常时,该异常会被委派构造函数中的 try 模块抓取到。并且在这种情况下,委派构造函数自己函数体内的代码就不会被执行了。
下面我们也通过例子来看看,构造函数 A(int i, int j)抛出一个异常,该异常依次被委派构造函数 A(int i)和 A()抓取到,且 A(int i)和 A()的函数体没有被执行。
程序源代码 (exception1.cpp)
#include <iostream>
using namespace std;
class A{
public:
A();
A(int i);
A(int i, int j);
private:
int num1;
int num2;
int average;
};
A:: A()try: A(0) {
// A()函数体不会被执行到
cout << "A() body"<< endl;
}
catch(...) {
cout << "A() catch"<< endl;
}
A::A(int i) try : A(i, 0){
// A(int i)函数体不会被执行到
cout << "A(int i) body"<< endl;
}
catch(...) {
cout << "A(int i) catch"<< endl;
}
A::A(int i, int j) try {
num1=i;
num2=j;
average=(num1+num2)/2;
cout << "A(int i, int j) body"<< endl;
// 抛出异常
throw 1;
}
catch(...) {
cout << "A(int i, int j) catch"<< endl;
}
int main(){
try{
A a;
cout << "main body"<< endl;
}
catch(...){
cout << "main catch"<< endl;
}
return 0;
}
该例的输出为:
A(int i, int j) body
A(int i, int j) catch
A(int i) catch
A() catch
main catch
当委派构造函数抛出异常时,系统会自动调用目标构造函数内已经构造完成的对象的析构函数。在下面的例子中,目标构造函数 A(int i, int j)完成了对象 a 的构造。它的委派构造函数 A(int i)在执行时抛出了一个异常,此时对象 a 马上被析构,且 A(int i)的委派构造函数 A()的函数体不再被编译器执行,这和上例中所描述的原则是一致的。
程序源代码 (exception2.cpp)
#include <iostream>
using namespace std;
class A{
public:
A();
A(int i);
A(int i, int j);
~A();
private:
int num1;
int num2;
int average;
};
A::A(): A(0){
// A()函数体不会被执行到
cout << "A()body" << endl;
}
A::A(int i) try : A(i, 0){
cout << "A(int i) body"<< endl;
// 抛出异常,对象 a 将被析构
throw 1;
}
catch(...) {
cout << "A(int i) catch"<< endl;
}
A::A(int i, int j){
num1=i;
num2=j;
average=(num1+num2)/2;
cout << "A(int i, int j) body"<< endl;
}
A::~A(){
cout << "~A() body"<< endl;
}
int main(){
A a;
return 0;
}
该例的输出为:
A(int i, int j) body
A(int i) body
~A() body
A(int i) catch
3、委派构造函数和泛型编程
其实,委派构造函数除了可以使程序员规避构造函数里重复的代码外,还可使构造函数的泛型编程变得更加容易,比如下面这个例子:
程序源代码 (generic.cpp)
#include <iostream>
using namespace std;
template<typename T> class A{
public:
A(int i): A(i, 0){}
A(double d): A(d, 0.0){}
// 函数模板
A(T i, T j) {
num1=i;
num2=j;
average=(num1+num2)/2;
cout << "average=" << average << endl;
}
private:
T num1;
T num2;
T average;
};
int main(){
A<int> a_int(1);
A<double> a_double(1.0);
}
该例的输出为:
average=0
average=0.5
上面这段代码中,目标构造函数为函数模板,它在被委派构造函数调用的时候才被实例化,非常方便,无需开发人员再写同类型的目标构造函数。
总结
通过上面的例子,相信大家对C++11委派构造函数具体使用都有了一定的了解和认识。利用C++11 的这个特性可以提高程序的可读性和可维护性,提高开发人员的开发效率。虽然委派构造函数的调用是可能会需要一些系统开销,但是大家还是可以尝试使用。
推荐学习:《C++面向对象编程》