c++基础语法:构造函数初始化列表

所属分类: 软件编程 / C 语言 阅读数: 63
收藏 0 赞 0 分享

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

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

为了说明清楚,假设有这样一个类:

复制代码 代码如下:

class foo{
   private :
    int a, b;
};

1、foo(){}和foo(int i = 0){}都被认为是默认构造函数,因为后者是默认参数。两者不能同时出现。

2、构造函数列表的初始化方式不是按照列表的的顺序,而是按照变量声明的顺序。比如foo里面,a在b之前,那么会先构造a再构造b。所以无论 foo():a(b + 1), b(2){}还是foo():b(2),a(b+1){}都不会让a得到期望的值。如果先声明b再声明a则会更好。

3、构造函数列表能够对const成员初始化。比如foo里面有一个int const c;则foo(int x) : c(x){}可以让c值赋成x。不过需要注意的是,c必须在每个构造函数(如果有多个)都有值。

4、在继承里面,只有初始化列表可以构造父类的private成员。比如说

复制代码 代码如下:

class child : public foo{
}

foo里面的构造函数是这样写的:foo (int x) { a = x; }.
而在child里面写child(int x){ foo(x); }是通过不了编译的。只有把父类初始化改为foo(int x) : a(x){}而子类构造写作child (int x) : foo(x){}才可以。

C++ 初始化类的成员,不但可以用构造函数(constructor)完成,而且可以用初始化类成员列表来完成。MFC大量用到此方法。例如有些初学者可能不大理解如下代码:
class A
{
  public:
    int member_var; //成员变量
    A();            //构造函数
}
A::A():member_var(0)
{
}

他们觉得这个构造函数的定义应该只能这样写:
A::A()
{
   member_var=1;
}
其实两种方法都可。但是有些情况下,只能用第一种,而且通常情况下用第一种也会效率高些。

其实,第一种方法是真正的初始化(initialization ),而在构造函数内实现的“=”操作其实是赋值(assign)。这两种方法的一切区别从这儿开始。区别大概如下:

1.我们知道普通变量编译器都会默认的替你初始化。他们既能初始化,也能被赋值的,而常量(const)按照其意思只能被初始化,不能赋值。否则与变量就无区别了。所以常量成员(const member)只能用成员初始化列表来完成他们的“初始化”,而不能在构造函数内为他们“赋值”。

2.我们知道类的对象的初始化其实就是调用他的构造函数完成,如果没有写构造函数,编译器会为你默认生成一个。如果你自定义了带参数的构造函数,那么编译器将不生成默认构造函数。这样这个类的对象的初始化必须有参数。如果这样的类的对象来做另外某个类的成员,那么为了初始化这个成员,你必须为这个类的对象的构造函数传递一个参数。同样,如果你在包含它的这个类的构造函数里用“=”,其实是为这个对象“赋值”而非“初始化”它。所以一个类里的所有构造函数都是有参数的,那么这样的类如果做为别的类的成员变量,你必须显式的初始化它,你也是只能通过成员初始化列表来完成初始化。 例如:

复制代码 代码如下:

class B
{
......
}

class A
{
  public:
  B member_b;
  A();
}
A::A():B(...) //你必须显式初始化它,因为他的所有构造函数
              //都是有参数的,之后才能被赋值。
{
  B=...; //因为如上所写,已经初始化了,才能被赋值,否则错误。
}


初始化顺序:
复制代码 代码如下:

class test

{

       const int a;

       std:string str;

       object o;

       test():str(“df”),o(null) ,a(0)

{

}    

};


黄色的既是初始化列表,他们会在构造函数正式调用前被调用,且他们的初始化顺序并不是根据 初始化列表中出现的顺序,而是他们声明的顺序来初始化。如上:

初始化顺序是: a, str, o;

一般用于初始化 常量类型,静态类型的数据,或者不能独立存在的数据

更多精彩内容其他人还在看

C++广播通信实例

这篇文章主要介绍了C++实现广播通信的方法,实例讲述了C++ socket广播通信的原理与实现方法,需要的朋友可以参考下
收藏 0 赞 0 分享

C++计算ICMP头的校验和实例

这篇文章主要介绍了C++计算ICMP头的校验和的方法,代码简单实用,对于校验ICMP报文来说有不错的实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C++设置超时时间的简单实现方法

这篇文章主要介绍了C++设置超时时间的简单实现方法,涉及系统函数setsockopt对套接口的操作,具有一定的实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C++实现ping程序实例

这篇文章主要介绍了C++实现ping程序实例,涉及C++对于ICMP数据包的发送与回显处理,具有一定的实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C++之boost::array的用法

这篇文章主要介绍了C++之boost::array的用法,以实例的形式简单讲述了静态数组的容器boost::array的使用技巧,具有一定的参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C++之Boost::array用法简介

这篇文章主要介绍了C++之Boost::array用法简介,较为详细的分析了Boost::array中的常见用法,并用实例的形式予以总结归纳,需要的朋友可以参考下
收藏 0 赞 0 分享

VC文件目录常见操作实例汇总

这篇文章主要介绍了VC文件目录常见操作实例汇总,总结了VC针对文件目录的各种常用操作,非常具有实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享

VC打印word,excel文本文件的方法

这篇文章主要介绍了VC打印word,excel文本文件的方法,是VC操作文本文件中非常实用的技巧,需要的朋友可以参考下
收藏 0 赞 0 分享

VC++获得当前进程运行目录的方法

这篇文章主要介绍了VC++获得当前进程运行目录的方法,可通过系统函数实现该功能,是非常实用的技巧,需要的朋友可以参考下
收藏 0 赞 0 分享

VC中SendMessage和PostMessage的区别

这篇文章主要介绍了VC中SendMessage和PostMessage的区别,较为全面的分析了SendMessage和PostMessage运行原理及用法上的不同之处,非常具有实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享
查看更多