asm, c, c++ are my all
-- Core In Computer
posts - 139,  comments - 123,  trackbacks - 0

/********************************************\
|    欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉!     |
\********************************************/


C++对象模型(7) -  Member Initialization List

作者: Jerry Cat
时间: 2006/05/12
链接: 
http://www.cppblog.com/jerysun0818/archive/2006/05/12/6978.html


2.4 Member Initialization List
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

When you write a constructor, you have the option of initializing class members either through the
member initialization list or within the body of the constructor. Except in four cases, which one
you choose is not significant.

In this section, I first clarify when use of the initialization list is "significant" and then
explain what actually gets done with that list internally. I then look at a number of possible,
subtle pitfalls.

You must use the member initialization list in the following cases in order for your program to compile:

(1). When initializing a reference member
(2). When initializing a const member
(3). When invoking a base or member class constructor with a set of arguments
     低效的第四种情况
In the fourth case, the program compiles and executes correctly. But it does so inefficiently.
For example, given

class Word {
   String _name;
   int _cnt;
public:
   // not wrong, just naive ...
   Word() {
      _name = 0;
      _cnt = 0;
   }
};
this implementation of the Word constructor initializes _name once, then overrides the
initialization with an assignment, resulting in the creation and the destruction of a temporary
String object. Was this intentional? Unlikely. Does the compiler generate a warning? I'm not aware
of any that does. Here is the likely internal augmentation of this constructor:
// Pseudo C++ Code
Word::Word( /* this pointer goes here */ )
{
   _name.String::String();         // invoke default String constructor
   String temp = String( 0 );      // generate temporary
   _name.String::operator=( temp );// memberwise copy _name
   temp.String::~String();         // destroy temporary
   _cnt = 0;
}

Had the code been reviewed by the project and corrected, a significantly more efficient
implementation would have been coded:
// preferred implementation
Word::Word : _name( 0 )
{
   _cnt = 0;
}
This expands to something like this:

// Pseudo C++ Code
Word::Word( /* this pointer goes here */ )
{   // invoke String( int ) constructor
   _name.String::String( 0 );
   _cnt = 0;
}
This pitfall, by the way, is most likely to occur in template code of this form:

template < class type >
foo< type >::foo( type t )
{
   // may or may not be a good idea depending on the actual type of type
   _t = t;
}
This has led some programmers to insist rather aggressively that all member initialization be done
within the member initialization list, even the initialization of a well-behaved member such as _cnt:

// some insist on this coding style, 顺序有问题!
Word::Word() : _cnt( 0 ), _name( 0 )
{}

Actually, there is a subtlety to note here: The order in which the list entries are set down is
determined by the declaration order of the members within the class declaration, not the order
within the initialization list. In this case, _name is declared before _cnt in Word and so is placed first.

This apparent anomaly between initialization order and order within the initialization list can
lead to the following nasty pitfall:

class X {
   int i;
   int j;
public:
   // oops!  do you see the problem?
   X( int val ) : j( val ), i( j )
   {}
   ...
};

// preferred idiom, 解决咯
X::X( int val ) : j( val )
{
   i = j;
}

Here is an interesting question: Are the entries in the initialization list entered such that the
declaration order of the class is preserved? That is, given

// An interesting question is asked:
X::X( int val ) : j( val )
{
   i = j;
}
is the initialization of j inserted before or after the explicit user assignment of j to i? If
the declaration order is preserved, this code fails badly. The code is correct, however, 这才是
真正的原因 - because the initialization list entries are placed before explicit user code.
所以成员初始化不是一股脑儿都放到初始化列表里才是最优方案!

Another common question is whether you can invoke a member function to initialize a member, such as
// is the invocation of X::xfoo() ok?  问得好!
X::X( int val ) : i( xfoo( val )), j( val )
{}

where xfoo() is a member function of X. The answer is yes, but…. To answer the "but" first, I
reiterate my advice to initialize one member with another inside the constructor body, not in the
member initialization list. You don't know the dependencies xfoo() has regarding the state of the
X object to which it is bound. 还是那句话: 别将所有的成员初始化工作全放在构造函数的初始化列表里 -
By placing xfoo() within the constructor body, you can ensure there is no ambiguity about which
members are initialized at the point of its invocation.

The use of the member function is valid (apart from the issue of whether the members it accesses
have been initialized). This is because the this pointer associated with the object being
constructed is well formed and the expansion simply takes a form like the following:

// Pseudo C++ Code: constructor augmentation
X::X( /* this pointer, */ int val )//一般都将this指针缺省, 但它的确是存在的, 至少对编译器而言
{
   i = this->xfoo( val );
   j = val;
}

where xfoo() is a member function of X. The answer is yes, but…. To answer the "but" first, I
reiterate my advice to initialize one member with another inside the constructor body, not in the
member initialization list. You don't know the dependencies xfoo() has regarding the state of the
X object to which it is bound. By placing xfoo() within the constructor body, you can ensure
there is no ambiguity about which members are initialized at the point of its invocation.

The use of the member function is valid (apart from the issue of whether the members it accesses
have been initialized). This is because the this pointer associated with the object being
constructed is well formed and the expansion simply takes a form like the following:
// Pseudo C++ Code: constructor augmentation
X::X( /* this pointer, */ int val )
{
   i = this->xfoo( val );
   j = val;
}

In summary, the compiler iterates over and possibly reorders the initialization list to reflect
the declaration order of the members. It inserts the code within the body of the constructor
prior to any explicit user code. 成员初始化列表的内容"插"在构造函数的最前端.

posted on 2006-05-12 00:49 Jerry Cat 阅读(749) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理



<2006年5月>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

常用链接

留言簿(7)

随笔档案

最新随笔

搜索

  •  

最新评论

阅读排行榜

评论排行榜