您的位置:首页 > 编程语言 > C语言/C++

杂货边角(14):C++11在构造函数方面的新特性

2018-02-05 15:23 375 查看
C++中类是面向对象编程的基础,但是一旦类的属性域很多时,必然面临着构造函数的数量爆炸,并且各个构造函数可能只存在局部的细节差异,故而为了应对这种存在明显代码段重复的情况,违背了代码重复即原罪的原则。C++11针对构造函数存在的代码臃肿问题专门引入了继承构造函数机制和委托构造函数机制,前者是用来针对子类和父类中的构造函数的重复问题,后者是针对本类中多个构造函数间差异微小导致的代码重复问题。

目录:

Sec1 继承构造函数

Sec2 委派构造函数

Sec1. 继承构造函数

/*===================================================================//
**主题:继承构造函数
**核心关键词: using\ 构造函数继承
**===================================================================*/
class Father
{
private:
int type;
double d;
public:
Father(): type(1),d(1.0) {cout << "calling the  father no-param constuct func" << endl;}
Father(int _in) : type(_in),d(1.0) {cout << " calling the int-param construct func" << endl;}
Father(double _input) : type(1),d(_input) {cout << "calling the double-param construct func" << endl;}
Father(int _in, double _input) : type(_in),d(_input) {cout << "calling the int&double-params construct func" << endl; }

void func(int _in) { cout<< "Father: "<< _in <<endl;}
};

class Child : public Father
{
private:
float _child_param {1.0}; //属性域直接初始化
public:
Child() {cout << "calling the child non-param construct func" <<endl;}
Child(float _in_param) : _child_param(_in_param) {cout << "calling the child float-param construct func" << endl; }
};

int main() {
Child a;  //会先调用Father类的无参构造函数,再调用Child类的无参构造函数
Child b(1.1314); //也是首先调用Father类的无擦构造函数,然后再调用Child类的float参数构造函数
}




如果在子类的构造函数中需要调用父类的其他构造函数该如何处理,传统的处理方式是

/*===================================================================//
**主题:继承构造函数
**核心关键词: using\ 构造函数继承
**===================================================================*/
class Father
{
private:
int type;
double d;
public:
Father(): type(1),d(1.0) {cout << "calling the  father no-param constuct func" << endl;}
Father(int _in) : type(_in),d(1.0) {cout << " calling the father int-param construct func" << endl;}
Father(double _input) : type(1),d(_input) {cout << "calling the father double-param construct func" << endl;}
Father(int _in, double _input) : type(_in),d(_input) {cout << "calling the father int&double-params construct func" << endl; }

void func(int _in) { cout<< "Father: "<< _in <<endl;}
};

class Child : public Father
{
private:
float _child_param {1.0}; //属性域直接初始化
public:
Child() {cout << "calling the child non-param construct func" <<endl;}
Child(float _in_param) : _child_param(_in_param) {cout << "calling the child float-param construct func" << endl; }
Child(float _in1, int _in2, double _in3) : Father(_in2, _in3), _child_param(_in1) {cout <<"calling the child explicit construct func with all param type"<<endl;}
Child(float _in1, int _in2) : Father(_in2), _child_param(_in1) {cout << "calling the child float & int param type construct func"<<endl;}
Child(float _in1, double _in3) : Father(_in3), _child_param(_in1) {cout << " calling the child float & double param type construct func" << endl;}
};

int main() {
/*可以看到在已有的情况下,要想调用Father类其他类型的构造函数无法实现,
那么该如何实现调用Father类的其他构造函数,这便是继承构造函数的使用了*/
Child(1.1314, 2, 2.0); //这种通过在Child类中显式地梳理子类和父类的前后衔接关系的方式也是可以达到调用父类具体构造函数的效果,但是依旧显得过于冗长
Child(1.1314, 2);
Child(1.31, 2.99);
}




可以看到这种一一对应的方式是可以完成更细粒度的控制,但是这显然并没有解放程序员的压力,需要程序员去一一梳理构造函数的调用关系。有没有一种更好的方式?

class Father
{
private:
int type;
double d;
public:
Father(): type(1),d(1.0) {cout << "calling the  father no-param constuct func" << endl;}
Father(int _in) : type(_in),d(1.0) {cout << " calling the father int-param construct func" << endl;}
Father(double _input) : type(1),d(_input) {cout << "calling the father double-param construct func" << endl;}
Father(int _in, double _input) : type(_in),d(_input) {cout << "calling the father int&double-params construct func" << endl; }

void func(int _in) { cout<< "Father: "<< _in <<endl;}
};

class Child : public Father
{
private:
float _child_param {1.0}; //属性域直接初始化
public:
using Father::Father;
};

int main() {
Child(1, 2.0); //会直接调用父类中int&double型构造函数,虽然在子类中并没有显式定义,但是通过using继承机制,可以快速地完成代码迁移
}


可以看到如果子类只是继承了父类,并且构造函数中相比于父类并没有额外的工作,那么使用继承机制是很好的,但是如果想上面的例子中要同时在子类中实现的对float变量的初始化,则显然通过单纯地继承机制是无法完成的,即还是需要下面这种枚举类型的代码出现

Child(float _in1, int _in2, double _in3) : Father(_in2, _in3), _child_param(_in1) {cout <<"calling the child explicit construct func with all param type"<<endl;}

Child(float _in1, int _in2) : Father(_in2), _child_param(_in1) {cout << "calling the child float & int param type construct func"<<endl;}

Child(float _in1, double _in3) : Father(_in3), _child_param(_in1) {cout << " calling the child float & double param type construct func" << endl;}


可以看到这里面还是涉及到了大量的手动梳理,并且代码逻辑是重复的,这也是C++11智能性不足的一个地方。

Sec2. 委派构造函数

对于属性域较多的类而言,下面这种存在多个构造函数的场景是无法避免的

class Info {
public:
Info() : type(1), name('a') {InitRest();}
Info(int i) : type(i), name('a') {InitRest(); }
Info(char e) : type(1), name(e) {InitRest();}

private:
void InitRest() {/*其他具体初始化工作*/}
int type;
char name;
};

/*即便通过类变量声明时即初始化,也只是减轻成如下*/
class Info {
public:
Info() : {InitRest();}
Info(int i) : type(i) {InitRest(); }
Info(char e) : name(e) {InitRest();}

private:
void InitRest() {/*其他具体初始化工作*/}
int type {1};
char name {'a'};
};
//可以看到构造函数依旧需要考虑到所有可能性的枚举


而C++11中的委托构造函数机制,则可以将上述改代码写成逻辑更清晰的模式

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;
};


这样采用将通用版本的构造函数私有化private,并在public区域中定义各种不同的构造接口,这样便可以将重复的工作都集中在通用的基础版构造函数中。并且还可以做一些额外的操作,比如添加
try...catch...
异常诊断功能。

#include <iostream>

using namespace std;

class DCExcept {
public:
DCExcept(double d)
try : DCExcept(1, d) {
cout << "Run the body."<<endl;
//其他初始化
}
catch(...) {
cout << "caught exception."<<endl;
}

private:
/*私有化该构造函数,完成委托构造的基础版本*/
DCExcept(int i, double d) {
cout << "going to throw!" <<endl;
throw 0;
}
int type;
double data;
};

int main() {
DCExcept a(1.2);
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息