构造函数列表初始化与赋值

构造函数列表初始化与赋值的区别

构造函数初始化的两种类型

  • 手动给成员赋值
  • 使用初始化列表

使用初始化列表的构造函数

类名::构造函数名(参数表): (成员初始化表){构造函数体}

构造函数中的初始化列表只需要在参数列表的后面加一个冒号(:),然后*将要初始化的成员按照*成员名(参数)的格式排列在后面,个成员之间用逗号隔开

class Test{
	public:
		int A;
		int B;
		Test(int a);
};

Test::Test(int a):A(a),B(10) 
//给成员变量 A、B 初始化,不一定要和参数列表写在一行
{ /* …… */ }

其中成员的初始化顺序不是按照初始化列表中的顺序来的,而是按照成员声明的顺序来的,例如:

/* Test类的声明同上 */
Test::Test(int a):B(10),A(a) 
// 虽然 B 在前面,但还是 A 先初始化
{/* …… */}

Test::Test(int a):B(a),A(B) //是错误的
//此处 A 的初始化依赖了 B,然而是 A 先初始化,这就导致 A 得到了 B 中还没初始化的错误内容
{/* …… */}

若类的数据成员是静态的const或者引用类型,必须使用初始化列表

const和static的区别

static:修饰的变量为静态变量,只会被初始化一次,该变量存储在内存中的静态区,地址不会改变。修饰全局变量时,每个函数对其的调用都是调用其生成的副本,修饰局部变量时每次调用都是上一次调用后的值。

const:修饰的变量只会被定义(可能也只能在定义的时候赋值)一次,定义之后无法对其进行赋值或修改(即不能充当左值)。

static和const修饰量的最大区别就是:static的值能修改,const不能(const修饰指针的情况另分)

const是静态局部变量,在超出其作用域后会被立即释放,在类的具体对象中,不同的类可以有不同的const变量

static是静态整体变量,在函数执行完毕后不会释放其空间,static变量是和类绑定的不同的对象的static变量是一样的


  • 静态(const)的数据成员只能初始化而不能赋值,同样引用类型也是只可以被初始化,那么只有用初始化列表。

  • 因为静态数据成员必须在定义时就被赋值,在构造函数体内进行赋值时,编译会报错。

  • 静态数据成员在类声明中声明,在包含类方法的文件中初始化。初始化时使用作用域运算符来指出静态成员所属的类。但如果静态成员是整形const或枚举型const,则可以在类声明中初始化。

  • static类型的静态变量好像不能使用列表初始化的方式进行初始化

  • C++ static、const和static const类型成员变量声明以及初始化

  • 如何初始化const和static数据成员

  • 一文带你了解static 和const

#include <iostream>  
#include <string>  
using namespace std;  
  
template<class t>  
class namedptr {  
public:  
    namedptr(const string& initname, t *initptr);  
private:  
    const string name; //静态数据成员的初始化必需用初始化列表  
    t * const ptr;  
};  
  
  
  
template<class t>  
namedptr<t>::namedptr(const string& initname, t *initptr)
  : name(initname), ptr(initptr)  {}  
  
//第二种方法是在构造函数体内赋值:  
//但含有静态数据类型,所以不可用
  
//template<class t>  
//namedptr<t>::namedptr(const string& initname, t *initptr)  
//{  
//  name = initname;  
//  ptr = initptr;  
//}  
  
  
int main()  
{  
    int a  = 10;  
    namedptr<int> Test("SHENZHEN",&a);  
}  

两种初始化方式的对比

首先 构造函数体内进行赋值会带来额外的开销,效率会低于初始化列表的方式

#ifdef A_H_
#define A_H_
#include <iostream>
usingnamespace std;
class A{
public:
	A(int a);
	static void print();//静态成员函数
private:
	static int aa;//静态数据成员的声明
	static const int count;//常量静态数据成员(可以在构造函数中初始化)
	const int bb;//常量数据成员
};
 
int A::aa=0;//静态成员的定义+初始化
const int A::count=25;//静态常量成员定义+初始化
 
A::A(int a):bb(a){//常量成员的初始化
aa+=1;
}
 
void A::print(){
cout<<"count="<<count<<endl;
cout<<"aa="<<aa<<endl;
}
 
#endif
 
void main(){
	A a(10);
	A::print();//通过类访问静态成员函数
	a.print();//通过对象访问静态成员函数
}

在上面的例子中

  • 第一种使用了初始化列表的方式。 只是调用了一次缺省的构造函数,并不会调用赋值函数。会减少不必要的开支,当类相当复杂时,就会看出使用初始化列表的好处。
  • 第二种使用了构造函数体内赋值的方式。初始化数据成员时会两次对string的成员函数的调用:一次是缺省构造函数,另一次是赋值。