定义
与其他函数不同,构造函数除了有名字,参数列表和函数体之外,还可以有初始化列表,初始化列表以冒号开头,后跟一系列以逗号分隔的初始化字段。
构造函数列表的初始化方式不是按照列表的的顺序,而是按照变量声明的顺序同时初始化显隐数据成员
从概念上来讲,构造函数的执行可以分成两个阶段,初始化阶段和计算阶段,初始化阶段先于计算阶段.
初始化阶段
所有类类型(class type)的成员都会在初始化阶段初始化,即使该成员没有出现在构造函数的初始化列表中.
计算阶段
一般用于执行构造函数体内的赋值操作
初始化列表的使用场景
场景1
以下三种情况下必须使用初始化成员列表
情况一、需要初始化的数据成员是对象的情况(这里包含了继承情况下,通过显示调用父类的构造函数对父类数据成员进行初始化);
情况二、需要初始化const修饰的类成员或初始化引用成员数据;
情况三、子类初始化父类的私有成员;在继承里面,只有初始化列表可以构造父类的private成员(通过显示调用父类的构造函数)
场景2
效率问题:初始化类的成员有两种方式,一是使用初始化列表,二是在构造函数体内进行赋值操作。
对于基本的类型,使用初始化列表效果不是非常明显。
但是对于类来说使用初始化列表少了一次调用默认构造函数的过程,这对于数据密集型的类来说,是非常高效的。如果不用成员初始化类表,那么类对自己的类成员分别进行的是一次隐式的默认构造函数的调用,和一次赋值操作符的调用,如果是类对象,这样做效率就得不到保障
class A
{
public:
//A() {};
A(int a=10) {};//两个都可以称为默认的构造函数
~A() {};
private:
int a;
};
class B
{
public:
B() {};//构造的时候调用一次隐形的构造函数,然后调用一次赋值操作符。
B(A &a ):a(a) {};//直接调用拷贝构造函数初始化a,省去了调用默认构造函数的过程。所以能使用初始化列表的时候尽量使用初始化列表.
~B() {};
private:
A a;
};
初始化列表的使用方法
#pragma once
#include <iostream>
using namespace std;
class ClassConst
{
public:
//方法一:通过数值对数据成员进行初始化
ClassConst():mAge(20), mKG(65), mID(0),addr(mAge)
{
mAge = 24;//先执行初始化列表在执行构造赋值。
//mKG = 66;const修饰的成员变量,构造函数也不能构造,需要使用初始化列表
}
//方法二:构造函数参数进行初始化
ClassConst(int age,int kg,int id) :mAge(age), mKG(kg), mID(id),addr(mAge) {}
void set() const
{
//mAge = 20;err 常函数不可以修改this->任何成员变量. mutable除外
mID = 2020623;
}
void show()
{
cout << "mAge=" << mAge << " mKG=" << mKG << " mID=" << mID << " addr=" << addr << endl;
}
private:
int mAge;//初始化列表与变量的声明顺序有关。
//如果数据成员为const型,那么该数据成员的值只能在初始化列表中被初始化或者定义直接初始化
const int mKG;//const修饰的变量,c++性质,编译器会优化(相当于宏替换),如果没有&和extern那么不会有内存分配(被优化)
mutable int mID;
int& addr;
};