委托构造函数 (来自C++11)

转载 2016年05月31日 07:17:27

委托构造函数delegating constructor

Herb Sutter & Francis Glassborow

1        问题的背景

1.1      简介

C++没有提供让一个构造函数去委托另一个构造函数执行构造操作的机制。这意味着不能(或不提倡)使用缺省参数,类的维护者不得不编写并维护多个构造函数。这会导致源代码和目标代码的重复,降低了可维护性(由于可能引起不一致性),有时还会导致代码膨胀。

其它的OO语言,如Java,就提供了这种特性。C++有必要增加相应的特性。

1.2      现状

目前,C++书籍建议用如下代码来让多个构造函数委托执行相同的初始化操作:

class X {

void CommonInit();

Y y_;

Z z_;

public:

X();

X( int );

X( W );

};

X::X() : y_(42), z_(3.14) { CommonInit(); }

X::X( int i ) : y_(i), z_(3.14) { CommonInit(); }

X::X( W e ) : y_(53), z_( e ) { CommonInit(); }

这种做法存在以下问题:

l         构造函数体代码重复。在这个例子中,有一个构造函数可以使用缺省参数来取替。而另一个不行,至少在不改变调用语义的前提下不行。

l         数据成员初始化重复。目前也不能委托数据成员的变量初始化,除非对这个类进行重构(如,把数据成员分离到另一个类,然后用CommonInit()分配并持有它的指针)。不可能在没有语言支持的前提下达到这个效果,因为一旦我们进入一个非构造函数就已经太迟了,所有数据成员都已经构造完成。所以没有办法“真正”委托任何东西,包括数据成员的构造。

注意,有些初学者(错误地)以为委托构造函数的特性已经存在,原因是以下代码可以通过编译,虽然它不能实现他们的期望:

class X {

int i_;

public:

X();

X( int );

};

X::X() { DoSomethingObservableToThisObject(); }

X::X( int i ) : i_(i) { X(); } // 可以编译,但什么也不做!

2        建议

2.1      简单案例

我们建议让类X的某个构造函数(称为“委托构造函数delegating constructor”)可以在初始化列表中调用同类的另一个构造函数(称为“目标构造函数target constructor”)。即委托构造函数把对象的初始化委托给另一个构造函数,然后再取回控制并执行其它的操作。一个委托构造函数也可以是另一个委托构造函数的目标构造函数。

例如:

class X {

int i_;

public:

X( int i ) : i_(i) { }

X() : X(42) { } // i_ == 42

};

规则如下:

l         允许外联形式的定义(见下例)。

l         最多只能有一个目标构造函数。如果在初始化列表中有一个同类的构造函数,则该初始化列表中不能再有其它东西(即不允许有其它基类或数据成员的初始化)。目标构造函数采用通常的重载决议和模板参数推断来选择。

l         目标构造函数还可以再委托给另一个构造函数。如果存在递归循环(如,构造函数C1委托给另一个构造函数C2,而C2又委托给C1),则行为未定义。不要求编译器检查这种情况,因为构造函数可以定义在不同的编译单元中,检查这种递归循环通常要耗费大量的编译时间。当然编译器如果能够进行检查,也是被鼓励的。

l         委托构造函数体中的语句在目标构造函数完全执行后才被执行。目标构造函数体中的局部变量不在委托构造函数体中起作用。

l         对象的生命期从任意一个构造函数执行完毕开始(对于委托构造的情况,就是最终的目标构造函数执行完毕时)。[C++03] §3.8中所写的“构造函数调用结束”是指任意一个构造函数。这意味着从委托构造函数体中抛出异常将导致析构函数的自动执行。

例子一:

class X {

X( int, W& );

Y y_;

Z z_;

public:

X();

X( int );

X( W& );

};

X::X( int i, W& e ) : y_(i), z_(e) { /*Common Init*/ }

X::X() : X( 42, 3.14 ) { SomePostInitialization(); }

X::X( int i ) : X( i, 3.14 ) { OtherPostInitialization(); }

X::X( W& w ) : X( 53, w ) { /* no post-init */ }

 

X x( 21 ); // if the construction of y_ or z_ throws, X::~X is invoked

例子二:

class FullName {

string firstName_;

string middleName_;

string lastName_;

public:

FullName(string firstName, string middleName, string lastName);

FullName(string firstName, string lastName);

FullName(const FullName& name);

};

FullName::FullName(string firstName, string middleName, string lastName)

: firstName_(firstName), middleName_(middleName), lastName_(lastName)

{

// ...

}

// delegating copy constructor

FullName::FullName(const FullName& name)

: FullName(name.firstName_, name.middleName_, name.lastName_)

{

// ...

}

// delegating constructor

FullName::FullName(string firstName, string lastName)

: FullName(firstName, "", lastName)

{

// ...

}

例子三:

class ex {

ex(int =0, double = 0.0, float = 0.0, std::string = "");

ex(int, double, std::string);

ex(int, std::string);

private:

int j;

double d;

float f;

std::string s;

};

ex::ex(int jp, double dp, float fp, std::string sp)

: j(jp), d(dp), f(fp), s(sp)

{

std::string message("full ctor");

std::cout << message <<'/';

}

ex::ex(int jp, double dp, std::string sp)

: ex(jp, dp, 1.0, sp)

{

std::string message("float defaulted ctor");

std::cout << message << '/';

}

ex::ex(int jp, std::string sp)

: ex(jp, 0.0, sp)

{

std::string message("float & double defaulted ctor");

std::cout << message << '/n';

}

例子三中的最后一个构造函数将象如下方式执行:

ex::ex(int jp, std::string sp)

: j(jp), d(0.0), f(1.0), s(sp)

{

{

std::string message("full ctor");

std::cout << message <<'/';

}

try {

std::string message("float defaulted ctor");

std::cout << message << '/';

} catch(…) { ~ex(); throw; }

try {

std::string message("float & double defaulted ctor");

std::cout << message << '/n';

} catch(…) { ~ex(); throw; }

}

(注意,如果是构造函数try块的情形,请参见后面的2.3节)。

注意,有时也需要委托给带有更少参数的构造函数。例如std::fstream就是一个很好的例子。在标准中std::fstream有两个构造函数:

basic_fstream();

explicit basic_fstream(const char* s, ios_base::openmode mode);

而后一个构造函数可以写成委托构造函数:

basic_fstream::basic_fstream( const char* s, ios_base::openmode mode)

: basic_fstream()

{

if(open(s, mode) == 0)

setstate(failbit);

}

2.2      模板构造函数

如果使用模板构造函数作为目标构造函数,那么推断的方法如常,也可以显式给出模板参数。例如:

class X {

template<class T> X( T first, T last ) : l_( first, last )

{ /*Common Init*/ }

list<int> l_;

public:

X( vector<short>& );

X( deque<char>& );

};

X::X( vector<short>& v ) : X( v.begin(), v.end() ) { }

// T 被推断为 vector<short>::iterator

X::X( const deque<char>& d )

: X<deque<char>::iterator>( d.begin(), d.end() ) { }

// T 无需推断

2.3      构造函数try

如果使用了构造函数try块,那么目标构造函数的执行就象普通的数据成员初始化一样;从初始化列表或目标构造函数体中抛出异常都意味着不会进入委托构造函数体,如果存在合适的处理句柄,异常就会被委托构造函数try块所捕获。例如:

class X {

X( Y&, int, double );

Y y_;

int i_;

public:

X( double, Y );

X( Y );

};

 

X::X( Y& y, int i, double d )

try : y_( y*d ), i_(i)

{ cout << “X::X(Y&,int,double) body” << endl; throw 1; }

catch(…)

{ cout << “X::X(Y&,int,double) catch” << endl; } // 隐式的重新抛出

 

X::X( double d, Y y )

try : X( y, 42, d )

{ cout << “X::X(double,Y) body” << endl; }

catch(…)

{ cout << “X::X(double,Y) catch” << endl; } //隐式的重新抛出

 

X::X( Y y )

try : X( 3.14, y )

{ cout << “X::X(Y) body” << endl; }

catch(…)

{ cout << “X::X(Y) catch” << endl; } //隐式的重新抛出

 

int main() {

X x( Y() );

}

 

// 输出结果

X::X(Y&,int,double) body

X::X(Y&,int,double) catch

X::X(double,Y) catch

X::X(Y) catch

在以上例子中,最后一个构造函数的执行就象以下伪代码一样:

X::X( Y y )

try {

try {

try : y_( y*3.14 ), i_(42)

{ cout << “X::X(Y&,int,double) body” << endl; throw 1; }

catch(…)

{ cout << “X::X(Y&,int,double) catch” << endl; throw; }

}

{

cout << “X::X(double,Y) body” << endl;

}

catch(…) {

try { cout << “X::X(double,Y) catch” << endl; throw; }

catch(…) { ~X(); throw; }

}

}

{

cout << “X::X(Y) body” << endl;

}

catch(…) {

try { cout << “X::X(Y) catch” << endl; throw; }

catch(…) { ~X(); throw; }

}

3        影响与实现

3.1      影响

这个语言特性与语言的其它部分配合很好,是初始化列表语法及语义的一个自然的扩展。对模板的影响也一样。对于使用构造函数的代码没有冲突。对已有代码没有影响。

3.2      实现

实现这一特性不存在已知的或可预见的困难。

相关文章推荐

C++11 FAQ中文版:委托构造函数(Delegating constructors)

二25 Year 2011陈 良乔C++11 FAQ 委托构造函数(Delegating constructors) 在C++98中,如果你想让两个构造函数完成相似的事情,可以实现两个完全相...

C++学习笔记19,C++11的委托构造函数(三)

C++11还支持委托构造函数。

去红血丝,怎样治疗红血丝—康美力祛红血丝套装

去红血丝,怎样治疗红血丝—康美力祛红血丝套装去红血丝 怎样治疗红血丝 如何治疗红血丝,目前治疗红血丝的方法很多,红血丝患者一定要根据自己的症状选择好产品,以免红血丝没祛掉,反毁容的后果,比如说激光或光...

C++11新特性应用--让你的程序更简洁、更漂亮

今天旧事重提,再写一点关于c++11的事儿,注意是说明一下使用C++11使你的程序更加的简洁,更加漂亮。说道简洁,我想最多想到的就是auto关键字的使用了。比如之前你这样定义一个迭代器:std::ve...

C++Primer第五版 第七章习题答案(41~50)

41、42知识点1:委托构造函数:一个委托构造函数使用它所属类的其他构造函数执行它自己的初始化过程。 class OH{ OH(string s, int a, int b):book(s),pri...

委托构造函数 转换构造函数

委托构造函数 c++11新标准,使我们可以定义所谓的委托构造函数(delegating  constructor),一个委托构造函数使用它所属的其他构造函数执行它自己的初始化过程。 class S...

c++11特性之std::thread--进阶

博客 [c++11特性之std::thread--初识](http://blog.csdn.net/wangshubo1989/article/details/49592517 "std::threa...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)