C++成员初始化

初始化列表

与其他函数不同,构造函数除了有名字,参数列表和函数体之外,还可以有初始化列表,初始化列表以冒号开头,后跟一系列以逗号分隔的初始化字段。

为什么使用初始化列表

初始化类的成员有两种方式:
1.使用初始化列表
2.在构造函数体内进行赋值操作。
使用初始化列表主要是基于性能问题,对于内置类型,如int, float等,使用初始化类表和在构造函数体内初始化差别不是很大,但是对于类类型来说,最好使用初始化列表,为什么呢?由上面的测试可知, 使用初始化列表少了一次调用默认构造函数的过程,这对于数据密集型的类来说,是非常高效的

常用的初始化方式:

class Student
{
public:
	Student(string n, int a)
	{
		name = n;
		age = a;
	}
private:
	string name;
	int    age;
};

这样可以达到预期效果,但却不是最佳做法。因为在构造函数中,是对name进行赋值,不是初始化,而string对象会先调用它的默认构造函数,再调用string类的赋值构造函数。对于age,因为int是内置类型,应该是赋值的时候获得了初值。

  • 要对成员进行初始化,而不是赋值,可以采用初始化列表(member initialization list)改写为如下:
class Student
{
public:
	Student(string n, int a) :name(n), age(a) {}
private:
	string name;
	int    age;
};

结果与上例相同,不过在初始化的时候调用的是string的拷贝构造函数,而上例会调用两次构造函数(默认构造函数,赋值函数),从性能上会有不小提升。

必须使用初始化列表的成员

  • 常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面

  • const数据成员,不能像常规的构造函数来初始化

#include<iostream>
using namespace std;

class A
{
public:
	A(int a, int b, int c)
	{
		ma = a;
		mb = b;
		mc = c;//error
	}
private:
	int ma;
	int mb;
	const int mc;//常量数据成员
};

  • const变量一定要在创建的时候初始化
const int a = 1;//yes

const int b;

b = 1;//no
  • 在构造函数中为什么不能使用赋值语句为const成员提供值

从概念上讲,调用构造函数时,对象在程序进入构造函数函数体之前被创建。也就是说,调用构造函数的时候,先创建对象,再进入函数体。对于A()构造函数来说,先为三个成员分配内存,然后再进入函数体使用常规的赋值方式将值存储到内存中。

  • const成员应该在分配内存的时候进行初始化。对此,c++提供了初始化列表语法来完成这项工作。
#include<iostream>
using namespace std;

class A
{
public:
	A(int a, int b, int c) :ma(a), mb(b), mc(c) {}//ok
private:
	int ma;
	int mb;
	const int mc;//常量数据成员
};

  • 引用类型,引用必须在定义的时候初始化,并且不能重新赋值。所以也要写在初始化列表里面
class Agency {};
class Agent
{
public:
	Agent(Agency& b) :belong(b) {}
private:
	Agency& belong;
};
  • 没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化。
class A
{
public:
	A(int a) :ma(a) {}
private:
	int ma;
};
class B
{
public:
	B(A& a1 )
	{
		a = a1;//error   类A不存在默认构造函数
	}
private:
   A a;
};

以上代码无法通过编译,因为B的构造函数中a = a1这一行实际上分成两步执行:

  1. 调用A的默认构造函数来初始化a1
    由于A没有默认的构造函数,所以1 无法执行,故而编译错误

使用初始化列表

class A
{
public:
	A(int a) :ma(a) {}
private:
	int ma;
};
class B
{
public:
	B(A& a1) :a(a1) {}//ok
private:
	A a;
};

C++98成员变量初始化

  • 在声明类的时候,对于静态常量类型,并且是枚举或者是整型的变量可以使用=在声明时初始化
int a = 7;
class A {
	static const int ma = 7;   // ok
	const int mb = 7;         //error:无static
	static int mc = 7;        //error:无const
	static const int md = a;  // error:初始化值不是常量表达式
	static const string me ="abc" //error:非整型
};

  • 对于不符合上述要求的静态变量可以在类外使用=进行初始化
  • 对于非静态类型可以在初始化列表进行初始化
  • 使用()对自定义类型进行初始化
  • 使用{}对元素集合统一初始化

C++11中的成员变量初始化

  • 允许非静态成员变量的初始化有多种形式:初始化列表; 使用等号=或花括号{}进行就地的初始化
  • 可以为同一成员变量既声明就地的列表初始化,又在初始化列表中进行初始化,只不过初始化列表总是看起来“后作用于”非静态成员。
  • 初始化列表的效果总是优先于就地初始化的。
class A
{
public:
private:
	int ma = 0;
	int mb = { 1 };
	int mc{ 2 };
};

等价于:

class A
{
public:
	A() :ma(0), mb(1), mc(2) {}
private:
	int ma;
	int mb;
	int mc;
};

列表初始化优点

  • 可以用列表初始化一个容器(vector,list,map,set…),不必调用成员函数添加元素
int main()
{
	vector<int> c{ 1, 3, 5 };
	map<int, float> d = { {1, 1.0f}, {2, 2.0f}, {3, 3.0f} };
}
  • 可以防止类型收窄,即对可能造成信息损失的类型转换,提示警告或者直接报错
int main()
{
	long double ld = 3.1415926;
	int a{ ld }, b{ ld };	//error,转换未执行,因为存在丢失信息的危险
	//int a(ld), b(ld);	//true,转换执行,且确实丢失了部分值
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43313035/article/details/89839461