十四、构造函数和初始化表
… …
4、拷贝构造函数
1)用一个已经存在的对象构造同类型的副本对象,会调用拷贝构造函数;
语法:
类名(const 类型& that){//完成成员的拷贝}
2)如果一个类没有定义拷贝构造函数,那么编译器会为其提供一个缺省的拷贝构造函数;
- 对于基本类型的成员变量,按字节复制;
- 对于类类型的成员变量,调用相应类拷贝构造函数来初始化;
class A{
};
class B{
int m_data;
A m_a;
};
B b;
B b2(b);
注意:
一般不需要自己定义拷贝构造函数,因为编译器提供的缺省拷贝构造函数已经很好用了。
3)拷贝构造函数调用时机
- 用已定义的对象作为同类型对象的构造实参;
- 以对象形式向函数传递参数;
- 从函数中返回对象;
class A{..};
void func(A a){
A a;
return a;
}
int main(){
A a2;
func(a2);
}
5、初始化表
1)用于指明类中成员变量的初始化方式
class 类名{
类名(形参表):成员变量1(初值),..{ //函数体 }
};
eg:
class A{
public:
A(void):m_i(0), m_j(0){
//m_j = 0;
//m_i = 0;
}
A(int i, int j):m_i(i), m_j(j){}
int m_i;
int m_j;
};
2)需要使用初始化表的场景
- 如果类中包含
"const"
或者"引用&"
成员变量,必须在初始化表中进行初始化; - 如果有类类型的成员变量,而该类又没有无参构造函数,则必须通过初始化表指明构造实参;
注意:
成员变量的初始化顺序由声明顺序所决定,而与初始化表的顺序无关。
练习:
重构时钟类,如果以日历作为构造实参,则表现为时钟功能,如果以无参方式则表现为计时器。
要求:使用初始化表
十五、this指针与常成员函数
1、this指针
1)类的构造函数和成员函数都有一个隐藏的该类 类型的指针参数,名为"this"
;
- 对于普通成员函数,this指针就是指向调用该函数的对象;
- 对于构造函数,this指针就是指向正在被构造的对象;
注意:
类中成员函数和构造函数访问类中其它成员,实际上都是通过this指针完成的。
2)需要显式使用this指针的场景
- 区分作用域;
- 从成员中返回调用对象的自身;
- 从类的内部销毁对象的自身;
2、常成员函数(常函数)
1)在一个成员函数参数表后面加上const关键字,表示这个函数为常成员函数。
返回类型 函数名(形参表)const{//函数体}
2)常成员函数中this指针是一个常指针,不能在常成员函数中修改成员变量的值;
注意:
被mutable(adj.易变的/ ‘mju:tәbl /)关键字修饰的成员变量可以在常函数中被修改。
3)非 常对象既可以调用非 常函数,也可以调用常函数;而常对象只能调用常函数,不能调用非 常函数。
4)函数名和形参表相同的成员函数,其常版本和非 常版本可以构成重载关系,常对象调用常版本,非 常对象调用非 常版本;
十六、析构函数(Destructor)
1、语法
class 类名{
public:
~类名(void){
//主要用于清理对象创建时分配的动态资源
}
};
1)函数名必须是”~类名”;
2)析构函数没有参数,也没有返回类型;
3)析构函数不能重载;
2、当对象被销毁时,该类的析构函数将被自动执行
1)栈对象当其离开作用域时,其析构函数被作用域右花括号"}"
所调用;
2)堆对象的析构函数被delete运算符调用;
3、如果一个类没有定义析构函数,那么编译器会为其提供一个缺省的析构函数:
- 对基本类型的成员变量什么也不做;
- 对类 类型的成员变量,会调用相应类型的析构函数,析构成员子对象;
4、对象的创建和销毁的过程
1)对象的创建
- 内存分配;
- 构造成员子对象;
- 执行构造函数代码;
2)对象的销毁
- 执行析构函数代码;
- 析构成员子对象;
- 释放内存;