文章目录
环境
在运行测试代码时,使用了如下环境:
linux使用的是ubuntu 18,在ubuntu上使用的是g++,版本如下:
root@learner:~# g++ --version
g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
必须使用成员初始化列表的情况
成员变量是引用类型
class A
{
public:
A(int i)
:ri(i){}
private:
int & ri;
};
int main()
{
int i = 100;
A a(i);
return 0;
}
如果数据成员变量是引用类型,则必须在初始化列表中进行初始化。
成员变量是const类型
class A
{
public:
A(int i)
:ci(i){}
private:
const int ci;
};
int main()
{
A a(100);
return 0;
}
有参基类的构造器
基类的有参构造器必须在初始化列表中调用。
class A
{
public:
A(int i)
:i_(i){}
private:
int i_;
};
class B : public A
{
public:
B(int x)
:A(x){} //调用父类的有参构造器
};
int main()
{
B b(100);
return 0;
}
有参类类型数据成员构造器
类数据成员是类对象,且需要调用其有参构造器,那么只能在初始化列表中调用。
class A
{
public:
A(int i)
:i_(i){}
private:
int i_;
};
class B
{
public:
B(int x)
:a(x){} //调用类数据成员的有参构造器
private:
A a;
};
int main()
{
B b(100);
return 0;
}
应注意的问题
参数列表初始化顺序,跟变量的声明顺序有关,跟初始化列表中的顺序无关。
class Stu
{
public:
Stu(string na)
:name(na),len(strlen(name.c_str())){}
void print()
{
cout<<"len = "<<len<<", name = "<<name<<"."<<endl;
}
private:
int len;
string name;
};
int main()
{
Stu s("Jasmine");
s.print();
return 0;
}
len = 30, name = Jasmine.
出错了,name的长度不是30。
初始化列表的顺序是,先初始化name,再初始化len。实际的初始化顺序是,先初始化len,但在初始化是name还没有初始化,所以求出的长度是不对的;再初始化name。
class Stu
{
public:
Stu(string na)
:len(strlen(na.c_str())),name(na){} //正确的方式
void print()
{
cout<<"len = "<<len<<", name = "<<name<<"."<<endl;
}
private:
int len; //先初始化 len
string name; //再初始化 name
};
int main()
{
Stu s("Jasmine");
s.print();
return 0;
}
len = 7, name = Jasmine.
正确的方式
初始化列表快的原因
定义一个用于测试的类
class A
{
public:
A(){
cout<<"default constructor: "<<this<<endl;
data_ = 0;
}
A(int i){
cout<<"construtor: "<<this<<endl;
}
A(const A& a)
{
cout<<this<<" : copy constructor form : "<<&a<<endl;
data_ = a.data_;
}
A& operator=(const A & a){
cout<<this<<" operator= "<<&a<<endl;
data_ = a.data_;
}
~A()
{
cout<<"deconstructor : "<<this<<endl;
}
private:
int data_;
};
不使用初始列表
class B
{
public:
B(int data){
a = data;
}
private:
A a;
};
int main()
{
B b(100);
return 0;
}
default constructor: 0x7ffecef57d94
construtor: 0x7ffecef57d64
0x7ffecef57d94 operator= 0x7ffecef57d64
deconstructor : 0x7ffecef57d64
deconstructor : 0x7ffecef57d94
data赋值给a对象时,先是发生调用构造进行一次隐式转换,将隐式转换的结果赋值给对象a,发生赋值运算符重载,临时对象消失时,会调用析构器。
使用初始化列表
class B
{
public:
B(int data)
:a(data){}
private:
A a;
};
int main()
{
B b(100);
return 0;
}
construtor: 0x7ffe0fba5494
deconstructor : 0x7ffe0fba5494
现在仅需要一次构造即可
总结
从上面的两个测试例子中,对于类类型的数据成员,使用初始化列表仅需一次有参构造;不使用初始化列表,则会发生一次默认构造、一次有参构造、一次赋值运算符重载、一次析构。所以使用初始化列表比不使用快很多。