构造函数初始化列

这是篇引用百度文库的文章,对构造函数初始化列表写的很清楚。

 

C++成员初始化列表

下面有三篇文章 对初始化列表的讲解很清楚:

一,

C++为类中提供类成员的初始化列表

类对象的构造顺序是这样的:
1.
分配内存,调用构造函数时,隐式/显示的初始化各数据成员
2.
进入构造函数后在构造函数中执行一般计算

使用初始化列表有两个原因:

1.必须这样做:
       
如果我们有一个类成员,它本身是一个类或者是一个结构,而且这个成员它只有一个带参数的构造函数,而没有默认构造函数,这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数,如果没有初始化列表,那么他将无法完成第一步,就会报错。

class ABC
...{
public:
         ABC(
int x,int y,int z);
private:
         
int a;
         
int b;
         
int c;
};
class MyClass
...{
public:
        MyClass():abc(1,2,3)
...{}
private:
        ABC abc;
};

        因为ABC有了显示的带参数的构造函数,那么他是无法依靠编译器生成无参构造函数的,所以没有三个int型数据,就无法创建ABC的对象。
        ABC
类对象是MyClass的成员,想要初始化这个对象abc,那就只能用成员初始化列表,没有其他办法将参数传递给ABC类构造函数。
       
另一种情况是这样的:当类成员中含有一个const对象时,或者是一个引用时,他们也必须要通过成员初始化列表进行初始化,因为这两种对象要在声明后马上初始化,而在构造函数中,做的是对他们的赋值,这样是不被允许的。

2.效率要求这样做:
      
类对象的构造顺序显示,进入构造函数体后,进行的是计算,是对他们的赋值操作,显然,赋值和初始化是不同的,这样就体现出了效率差异,如果不用成员初始化类表,那么类对自己的类成员分别进行的是一次隐式的默认构造函数的调用,和一次复制操作符的调用,如果是类对象,这样做效率就得不到保障。

注意:构造函数需要初始化的数据成员,不论是否显示的出现在构造函数的成员初始化列表中,都会在该处完成初始化,并且初始化的顺序和其在声明时的顺序是一致的,与列表的先后顺序无关,所以要特别注意,保证两者顺序一致才能真正保证其效率。

二,

构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。例如:

Example::Example() : ival(0), dval(0.0) {}//ival dval是类的两个数据成员

上面的例子和下面不用初始化列表的构造函数看似没什么区别:

Example::Example()

{

    ival = 0;

    dval = 0.0;

}

的确,这两个构造函数的结果是一样的。但区别在于:上面的构造函数(使用初始化列表的构造函数)显示的初始化类的成员;而没使用初始化列表的构造函数是对类的成员赋值并没有进行显示的初始化

初始化和赋值对内置类型的成员没有什么大的区别,像上面的任一个构造函数都可以。但有的时候必须用带有初始化列表的构造函数

1       成员类型是没有默认构造函数的类。若没有提供显示初始化式,则编译器隐式使用成员类型的默认构造函数,若类没有默认构造函数,则编译器尝试使用默认构造函数将会失败。

2       const成员或引用类型的成员。因为const对象或引用类型只能初始化,不能对他们赋值。

如下:

class Test

{

public:

     Test();

     ~Test();

private:

     const int i;

};

这样的定义是编译不过的,错误:Error     1    error C2758: 'Test::i' : must be initialized in constructor base/member initializer list   

三,

构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。例如:

class CExample  {
public:
    
int a;
    
float b;
    
//构造函数初始化列表
    CExample(): a(0),b(8.8)
    
{}
    
//构造函数内部赋值
    CExample()
    
{
        a=
0;
        b=
8.8;
    }
};

上面的例子中两个构造函数的结果是一样的。上面的构造函数(使用初始化列表的构造函数)显式的初始化类的成员;而没使用初始化列表的构造函数是对类的成员赋值,并没有进行显式的初始化。

初始化和赋值对内置类型的成员没有什么大的区别,像上面的任一个构造函数都可以。对非内置类型成员变量,为了避免两次构造,推荐使用类构造函数初始化列表。但有的时候必须用带有初始化列表的构造函数:
1.
成员类型是没有默认构造函数的类。若没有提供显示初始化式,则编译器隐式使用成员类型的默认构造函数,若类没有默认构造函数,则编译器尝试使用默认构造函数将会失败。
2.
const成员引用类型的成员。因为const对象或引用类型只能初始化,不能对他们赋值。

初始化数据成员与对数据成员赋值的含义是什么?有什么区别?
首先把数据成员按类型分类并分情况说明:
1.
内置数据类型,复合类型(指针,引用)
    
在成员初始化列表和构造函数体内进行,在性能和结果上都是一样的
2.
用户定义类型(类类型)
    
结果上相同,但是性能上存在很大的差别。因为类类型的数据成员对象在进入函数体前已经构造完成,也就是说在成员初始化列表处进行构造对象的工作,调用构造函数,在进入函数体之后,进行的是对已经构造好的类对象的赋值,又调用个拷贝赋值操作符才能完成(如果并未提供,则使用编译器提供的默认按成员赋值行为)

Note:
初始化列表的成员初始化顺序:
    C++
初始化类成员时,是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序。
    Example:

class CMyClass  {
    CMyClass(
int x, int y);
    
int m_x;
    
int m_y;
};

CMyClass::CMyClass(
int x, int y) : m_y(y), m_x(m_y)
{
}

你可能以为上面的代码将会首先做m_y=y,然后做m_x=m_y,最后它们有相同的值。但是编译器先初始化m_x,然后是m_y,,因为它们是按这样的顺序声明的。结果是m_x将有一个不可预测的值。有两种方法避免它,一个是总是按照你希望它们被初始化的顺序声明成员,第二个是,如果你决定使用初始化列表,总是按照它们声明的顺序罗列这些成员。这将有助于消除混淆。

 

 

posted @ 2010-12-21 17:59  pjh123  阅读(260)  评论(0编辑  收藏  举报