C++11委派构造函数的使用方法

在代码开发中,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++面向对象编程

时间: 2024-08-26 14:44:49

C++11委派构造函数的使用方法的相关文章

C++11 委派构造函数特性怎么使用?

在代码开发中,C语言和C++都是基础语言,是很多web开发人员的入门级必学语言.但在C++98 中,类成员构造问题还存在一些问题,为此C++11提出了一些新特性. C++98中如果一个类有多个构造函数且要实现类成员构造,这些构造函数通常要包含基本相同的类成员构造代码.在最坏的情况下,相同的类成员构造语句被拷贝粘贴在每一个构造函数中. 基于C++98中的类成员构造问题,C++11新特性中,程序员可以将公有的类成员构造代码集中在某一个构造函数里,这个函数被称为目标构造函数.其他构造函数通过调用目标构

C++11初窥二: 继承构造函数和委派构造函数

分析了这两种用法,真想吐槽两句,这两个特性确实有实际需要,但客观来说,现有标准足够用,而且带来的代价也非常大,又给C++复杂的语法糖重重的抹了一笔!!! 一.继承构造函数 继承构造函数的引入原因:如果基类的构造函数很多,那么子类的构造函数想要实现同样多的构造接口,必须一一调用基类的构造函数,有点麻烦 于是乎:C++11引入继承构造函数 class _A { public: _A( int _InInt ) {;} _A( double _InDouble, int _InInt ) {;} _A

C++11模板句柄的实现:委派构造函数、default关键字分析

C++11,使用委派构造函数,并且快速初始化变量,default关键字重声明默认构造函数,回复pod状态.分析与推荐用法. 目前为止,VS2012和2013对异常声明的兼容还是停留在代码沟通的级别,没有进行编译类型检查,出现如下错误可忽略. warning C4290: 忽略 C++ 异常规范,但指示函数不是 __declspec(nothrow) 下为:VS2012不支持委托构造函数,建议使用cocos2d-x 3.2及版本的朋友更新VS至2013版. 1>d:\cpp_lab\testque

模板句柄实现,委派构造函数的调用,throw声明等

C++11,使用委派构造函数,并且快速初始化变量,default关键字重声明默认构造函数,回复pod状态.分析与推荐用法. 目前为止,VS2012和2013对异常声明的兼容还是停留在代码沟通的级别,没有进行编译类型检查,出现如下错误可忽略. warning C4290: 忽略 C++ 异常规范,但指示函数不是 __declspec(nothrow) 下为:VS2012不支持委托构造函数,建议使用cocos2d-x 3.2及版本的朋友更新VS至2013版. 1>d:\cpp_lab\testque

Java中的构造函数引用和方法引用

方法引用的一些背景如果你还不知道 Java 构造函数本身就是特殊的方法,那么阅读方法引用的基本示例将对读者有所帮助,通过了解这些内容,可以了解构造函数引用是什么.方法引用可以引用静态方法和实例方法,两者是通用的.方法引用是函数式接口的实例.虽然 Lambda 表达式允许你动态创建方法实现,但通常情况下,一个方法最终会调用 Lambda 表达式中的另一个方法来完成我们想要完成的工作.更直接的方法是使用方法引用.当你已经有一个方法来实现这个函数式接口时,这是非常有用的.让我们看一个使用静态方法及实例

JVM加载类的过程,双亲委派机制中的方法

JVM加载类的过程: 1)JVM中类的整个生命周期: 加载=>验证=>准备=>解析=>初始化=>使用=>卸载  1.1.加载 类的加载阶段,主要是获取定义此类的二进制字节流,并将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构,最后在Java堆中生成一个代表这个类的java.lang.Class对象作为方法区这些数据的访问入口.相对于类加载过程的其他阶段,加载阶段是开发期可控性最强的阶段.我们可以通过定制不通的类加载器,也就是ClassLoader来控制二进制

C++11新增for循环遍历方法

记录C++11新增for循环遍历方法 1.基于迭代器的for循环: for_each位于std命名空间下,我们可以看到其定义如下: inline _Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func) { // perform function for each element _DEBUG_RANGE(_First, _Last); _DEBUG_POINTER(_Func); _For_each(_Unchecked(_First), _Un

Spring学习笔记--initmethod和构造函数、setter方法的加载顺序

今天学习了一下spring中bean的初始化和销毁,突然想了解一下初始化方法跟构造函数及setter方法注入的执行顺序,记录在此,仅作为学习笔记. 当实例化一个bean时,可能需要执行一些初始化操作来确保该bean处于可用状态.同样地,当不再需要bean时,将其从容器中移除是,我们可以还需要按顺序 执行一些清除工作. package com.zp.chapter2; public class Auditorium { private String name; public void doBefo

定义在构造函数内部的方法,会在它的每一个实例上都克隆这个方法;定义在构造函数的prototype属性上的方法会让它的所有示例都共享这个方法,但是不会在每个实例的内部重新定义这个方法. 如果我们的应用需要创建很多新的对象,并且这些对象还有许多的方法,为了节省内存,我们建议把这些方法都定义在构造函数的prototype属性上。

定义在构造函数内部的方法,会在它的每一个实例上都克隆这个方法;定义在构造函数的prototype属性上的方法会让它的所有示例都共享这个方法,但是不会在每个实例的内部重新定义这个方法. 如果我们的应用需要创建很多新的对象,并且这些对象还有许多的方法,为了节省内存,我们建议把这些方法都定义在构造函数的prototype属性上.当然,在某些情况下,我们需要将某些方法定义在构造函数中,这种情况一般是因为我们需要访问构造函数内部的私有变量.