C++学习笔记32——构造函数

1,基本概念

(1)只要创建类类型的新对象都要执行构造函数;
(2)构造函数的名字与类的名字相同,并且不能指定返回类型;
(3)构造函数不能声明为const,即便是const对象,也是用普通的构造函数来初始化;
(4)构造函数可以使用初始化列表,一般的函数不行。

2,初始化列表

(1)只有类的构造函数才能使用初始化列表;
(2)初始化列表只出现在构造函数定义的地方,而不出现在其声明的地方;
初始化列表的形式如下:
class Ini_Class
{
public:
	int i1;
	int i2;
	string str3;
	Ini_Class();
};

//在类外定义构造函数
Ini_Class::Ini_Class() :i1(520), i2(3132)
{
	cout << "I am here!" << endl;
}


或者直接在类内定义构造函数:
class Ini_Class
{
public:
	int i1;
	int i2;
        string str3;
	Ini_Class() :i1(520), i2(3132){cout << "I am here!" << endl;}
};


花括号之前,冒号之后的部分,即为构造函数的初始化列表。
初始化的次序是由类中成员定义的次序决定的,而不是由初始化列表中成员出现的次序决定的。

3,构造函数的执行步骤

(1)初始化阶段,
构造函数初始化列表中被显式提及的每个成员,都被初始化为给定值;没有被提及的成员则按照初始化变量的规则来初始化:类类型的成员调用默认构造函数来初始化,内置或复合类型(指针、数组)的成员的初始化依赖于对象的作用域(在什么地方定义这个类对象的):若处于局部作用域中,这些成员不被初始化,获得的值是随机的;若处于全局作用域中,则被初始化为0值。

(2)普通的计算阶段,
执行构造函数函数体里的语句。
让数据成员获得给定的值,既可以发生在初始化阶段,也可以发生在计算阶段。

以上面的例子来说就是,由于在初始化列表中提及了i1和i2,所以初始化阶段先分别将i1、i2初始化为520和3132,未被提及的str3则调用string类的默认构造函数完成初始化。完成之后再执行函数体里的语句,打印"I am here!".
综上:类类型的数据成员总是要初始化的,要么在初始化列表中显式初始化,要么就隐式调用其默认构造函数,如果不存在默认构造函数,则隐式调用默认构造函数失败。对于这种数据成员就必须在初始化列表中初始化。另外,对于const对象和引用类型的对象,只能初始化,所以const对象成员与引用类型的成员也必须放在初始化列表中初始化,而不能在函数体内赋值。

总结,必须使用初始化列表初始化的数据成员:
(1)数据成员是无默认构造函数的类对象;
(2)数据成员为const对象;
(3)数据成员为引用类型。

注意:const数据成员是不会被自动初始化的,不管是放在全局作用域中,还是局部作用域中。
其实,别说是类的数据成员,就是一个普通的内置类型的const对象,也不会被默认初始化的,哪怕是在全局作用域中。编译未显式初始化的全局变量会报错:
error C2734: “g_i_love_u”: 如果不是外部的,则必须初始化常量对象。

4,合成的默认构造函数

没有输入参数的构造函数即为默认构造函数,包括为所有形参提供默认实参的构造函数。
在定义一个对象时,如果不提供初始化式,则调用的就是默认构造函数。

合成的默认构造函数是指,编译器为没有定义任何构造函数的类创建的构造函数。其初始化的规则与初始化变量相同:类类型的成员调用默认构造函数来初始化,内置或复合类型(指针、数组)的成员的初始化依赖于对象的作用域(在什么地方定义这个类对象的):若处于局部作用域中,这些成员不被初始化,获得的值是随机的;若处于全局作用域中,则被初始化为0值。
不能使用合成的默认构造函数的情况:
(1)数据成员是无默认构造函数的类对象,即给类中有自己定义的构造函数,则编译器不再为其合成默认构造函数;
(2)数据成员为const对象;
(3)数据成员为引用类型。

例如有如下类的定义:
class const_Class
{
public:
	int i1;
	int i2;
	int *pi3;//指针OK
	int &i4; //引用err
	const int i5;//const变量 err
};


再定义该类的对象:
const_Class i_miss_u;//error C2280: “const_Class::const_Class(void)”: 尝试引用已删除的函数

推测是合成了默认构造函数后,发现成员中有const对象,又将默认构造函数删除了。

调用默认构造函数的两种方式:
My_Class myobj1;
My_Class myobj2 = My_Class();

5,隐式类类型的转换

如果程序中,在需要A类类型对象的地方,却给了其他类型B的对象,而A类类型又有一个接受B类型形参(只有这一个形参)的构造函数,则会发生隐式类型转换,调用该构造函数,生成一个临时的A类型对象。

这种转换有时并不是程序的设计者想要的。
为了阻止这种隐式的类型转换,可以把该构造函数声明为explicit。被声明为explicit的构造函数不能再用于隐式类型转换。
注意explicit只出现在构造函数声明 的地方,不能出现在它定义的地方。
一般情况下,单形参的构造函数都应该声明为explicit.

6,总结一些关键字出现的位置

inline:  在 成员函数声明和定义的地方出现皆可,效果相同,两个地方都出现也可以。
const:   必须在 成员函数声明和定义的地方都出现,因为可以利用构造函数是否为const来重载成员函数。
explicit:只能在 构造函数声明的地方出现,不能在定义的地方出现。
初始化列表:只能在 构造函数定义的地方出现,而不能在构造函数声明的地方出现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值