C++类构造函数初始化列表(构造函数后面的冒号)

来源:互联网 发布:非洲网络制式 编辑:程序博客网 时间:2024/06/06 01:36

http://www.eifr.com/article.php?id=293&act=print

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

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


c++构造函数后面的冒号
初始化列表,跟在{}里面的初始化没有什么不同,但在非静态const类型以及引用型成员变量必须在初始化列表里面初始化,不能在{}里面初始化.A(b)就是用父类的构造函数进行一部分初始化.
--------------

很多的人对中构造函数寝初始化很多的困惑,对冒号后初始化不是太明白,总搞不清楚它们之间的区别,我想把我对这个问题的理解和看法和大家讨论讨论。

   在程序中定义变量并初始化的机制中,有两种形式,一个是我们传统的初始化的形式,即赋值运算符赋值,还有一种是括号赋值,如: 
   int a=10; 
   char b='r';\\赋值运算符赋值 
  int a(10);\ 
   char b('r');\\括号赋值 
以上定义并初始化的形式是正确的,可以通过编译,但括号赋值只能在变量定义并初始化中,不能用在变量定义后再赋值,这是和赋值运算符赋值的不同之处,如: 
   (1) 
   int a;  \\先定义一个变量 
   ...... 
   a=10;  \\根据需要赋值

   (2) 
   int b;   \\先定义一个变量 
   ...... 
   b(10);  \\和(1)一样根据需要赋值 
(1)是可以用通过编译,定义一个变量a但并没有初始化,在需要变量a的时候,通过赋值运算符把10赋给a,而在(2)中,是通过括号把10赋值给b,但编译系统认为 
这是一个函数的调用,函数名为b,10为实际参数,所以编译错误。因此,括号赋值只用在定义变量并初始化中。 
    现在我们来看构造函数中冒号初始化和函数初始化的问题,类构造函数的作用是创建一个类的对象时,调用它来构造这个类对象的数据成员,一要给出此数据成员分配内存空间,二是要给函数数据成员初始化,构造数据成员是按数据成员在类中声明的顺序进行构造。

    冒号初始化与函数体初始化的区别在于:

   冒号初始化是给数据成员分配内存空间时就进行初始化,就是说分配一个数据成员只要冒号后有此数据成员的赋值表达式(此表达式必须是括号赋值表达式),那么分配了内存空间后在进入函数体之前给数据成员赋值,就是说初始化这个数据成员此时函数体还未执行。

    对于在函数中初始化,是在所有的数据成员被分配内存空间后才进行的。

   这样是有好处的,有的数据成员需要在构造函数调入之后函数体执行之前就进行初始化如引用数据成员,常量数据成员对象数据成员,看下面的一段程序: 
   class student 
   {public :

     student () 
      . 
      . 
      . 
     protected: 
     const int a; 
     int &b; 
   }

   student ::student (int i,int j) 
  { 
     a=i; 
     b=j; 
   }

在Student类中有两个数据成员,一个是常量数据成员,一个是引用数据成员,并且在构造函数中初始化了这两个数据成员,但是这并不能通过编译,因为常量初始化时必须赋值,它的值是不能再改变的,与常量一样引用初始化也需要赋值,定义了引用后,它就和引用的目标维系在了一起,也是不能再被赋值的。所以C ++":"后初始化的机制,使引用和常量数据成员变为可能的,Student类的构造函数应为: 
    student ::student(int i,int j):a(i),b(j){}

在下面的程序: 
    class teach 
   {  
    public : 
      teach(char *p="name",int a=0) 
           .         . 
           . 
    protected: 
      char name[30]; 
      int age; 
   } 
   teach ::teach(char*p,int a) 
   { 
    strcopy(name ,p); 
    age=a;  
         cout>>name>>endl; 
   } 
   class student 
  { 
    public: 
     student (char *p="name"); 
           . 
           . 
           . 
    protected; 
     char name[30]; 
     teach teacher; 
   }; 
   student::student(char *p) 
   { 
     strcopy(name,p); 
     cont>>name>>endl; 
   } 
在上面的程序中通不过编译,编译系统会告诉你teacher这个类对象缺默认构造函数,因为在teach 类中没有定义默认的构造函数。那么带参数的构造函数怎么进行构造呢?通过我们前面提到的冒号赋值。那它的构造函数应该是: 
    
   student::student(char *p,char *pl,int ag):teacher(pl,ag) 
   { 
     strcopy(name,p); 
     cont>>name>>endl; 
   } 
就是说在没有默认构造函数的时候,如果一个类对象是另一个类的数据成员,那么初始化这个数据成员,就应该放到冒号后面。这样可以带参数。在类的定义中,如: 
     protected; 
     char name[30]; 
     teach teacher 
类对象是不能带参数的,因为它只是声明。

    所以在C++中就增加了这种机制,这是面向对象编程所必须的。


0 0