介绍类的6个成员函数之前,我们先来看一下this指针
this指针
this 指针是成员函数里面的一个隐藏指针,指向了调用该函数的对象本身。
this指针特性:
(1)this指针的类型为:类类型* const
(2)this指针并不是对象本身的一部分,不影响sizeof结果
(3)this指针的作用域在类“非静态成员函数”内部
(4)this指针是”非静态类成员函数”的第一个默认隐含参数,编译器自动维护传递,类编写者不能显式传递
(5)只有在类的非静态成员函数中才可以使用this指针,其它任何函数都不可以
(6)this指针不能在初始化列表中使用
(7)当通过this指针访问数据成员时,this指针不能为空
class Test { public: void FunTest() { cout << "FunTest():" << this << endl; //若不进行注释程序会编译错误,this指针为空,不能使用this指针来访问数据成员 /*cout << this->_a << endl;*/ } private: int _a; }; int main() { Test* pt = NULL; pt->FunTest(); return 0; }
this指针是使用__thiscall调用约定的
(1)__thiscall只能够用在类的成员函数上
(2) 参数从右向左压栈
(3)如果参数个数确定,this指针通过ecx传递给被调用者;如果参数不确定(_cdecl),this指针在所有参数被压栈后压入堆栈
(4)对参数个数不定的,调用者清理堆栈,否则函数自己清理堆栈
类的6个默认的成员函数
构造函数:
(1)概念:构造函数是一个特殊的成员函数,名字和类名相同,无返回值类型,创建类对象时由编译器自动调用,在对象的生命周期内只调用一次,保证每个数据成员都有一个合适的初始值
class Date { public: //初始化列表 Date(int year,int month,int day) :_year(year),_month(month),_day(day) { cout << "Date(int):" << this << endl; } private: int _year; int _month; int _day; }; int main() { Date d(2018, 6, 14); return 0; }
(2)特性:1)函数名与类名相同 ,没有返回值 2) 新对象被创建时,由编译器自动调用,且在对象的声明周期内仅调用 一次 3)构造函数可以重载,实参决定了调用哪个构造函数 4)无参构造函数和带有缺省值的构造函数都认为是缺省的构造函数,并 且缺省的构造函数只能有一个 5)有初始化列表6) 如果没有显式定义时,编译器会合成一个默认的构造函数7) 构造函数不能用const修饰8)构造函数不能为虚函数
编译器在什么时候合成默认构造函数?
1)有一个A类,A类中包含缺省的构造函数
2)有一个B类,包含了A类的对象,并且B类没有显式定义自己的构造函数
在这种情况下系统必须为B类合成默认构造函数,目的就是为了构造B对象中包含的A对象
如下:
class Time { public: Time(int hour=8,int minute=2,int second=45) :_hour(hour),_minute(minute),_second(second) {} private: int _hour; int _minute; int _second; }; class Date { private: int _year; int _month; int _day; Time _t; }; int main() { Date d;//会调用系统合成的构造函数 return 0; }
(3)对象初始化
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表, 每个”成员变量”后面跟一个放在括号中的初始值或表达式
注意:
1)每个成员在初始化列表中只能出现一次
因为初始化的时候需要划分空间,所以只能出现一次
2)初始化列表仅用于初始化类的数据成员,并不指定这些数据成员的初始化顺序,数据成员在类中定义顺序就是在参数列表中的初始化顺序,尽量避免使用成员初始化成员,成员的初始化顺序好和成员的定义 顺序保持一致
3)类中包含以下成员,一定要放在初始化列表位置进行初始化:引用成员变量 const成员变量 类类型成员(该类有非缺省的构造函数)
类中包含类类型成员一定要放在初始化列表位置进行初始化:即A类中有非缺省的构造函数,B类中有构造函数,数据成员包含A类对象。
(4)构造函数作用
1)构造&初始化对象
2)类型转换 对于单个参数构造函数,可以将其接受参数转化成类类型对象。用 explicit修饰构造函数,抑制由构造函数定义的隐式转换,explicit 关键字类内部的构建声明上,在类的定义体外部的定义上不再重复,如:
class Date { public: //构造函数用explicit修饰可以避免类型转换问题 Date(int year) :_year(year) { cout << "Date(int)" << this << endl; } private: int _year; int _month; int _day; }; int main() { Date d1(2018); d1 = 2019;//用2019来构造一个临时对象,赋给d1 return 0; }
拷贝构造函数
class Date { public: Date(int year,int month,int day) :_year(year),_month(month),_day(day) { cout << "Date(int):" << this << endl; }; //拷贝构造函数 Date(const Date& d) :_year(d._year) ,_month(d._month) ,_day(d._day) { cout << "Date(const Date&):" << this << endl; }; private: int _year; int _month; int _day; }; int main() { Date d1(2018, 6, 14); Date d2(d1); return 0; }
(1)概念
只有单个形参,而且该形参是对本类类型对象的引用(常用const修饰),这 样的构造函数称为拷贝构造函数。拷贝构造函数是特殊的构造函数,创建 对象时使用已存在的同类对象来进行初始化,由编译器自动调用
(2)特征
1)是构造函数的重载,构造函数的性质拷贝构造函数均满足
2)参数必须使用类类型对象引用传递(为什么?)
若用值传递传参时,就会调用拷贝构造函数来构造临时变量,形成递归
3)如果没有显式定义,系统会自动合成一个默认的拷贝构造函数。默认 的拷贝构造函数会依次拷贝类的数据成员完成初始化
析构函数
(1)概念
析构函数:与构造函数功能相反,在对象被销毁时,由编译器自动调用,完成类 的一些资源清理和汕尾工作
(2)特性
1)析构函数在类名(即构造函数名)加上字符~
2)析构函数无参数无返回值
3) 一个类有且只有一个析构函数。若未显示定义,系统会自动生成缺省的析构函数
4)对象生命周期结束时,C++编译系统系统自动调用析构函数
注意:析构函数体内并不是删除对象,而是做一些清理工作
赋值操作符重载
操作符重载也是一个函数,具有返回值和形参表,它的形参数目与操作符的操作数数目相同,使用运算符重载可以提高代码的可读性
不可以重载的运算符:.(成员选择符) .*(成员对象选择符) ::(域解析操作符) ?:(条件操作符)
如下日期类的赋值操作符重载:
Date& operator=(const Date& d) { //排除自己给自己赋值,如果两个地址不一样才进行赋值 if (this != &d) { _year = d._year; _month = d._month; _day = d._day; } return *this; }
取地址操作符重载和const修饰的取地址操作符重载:
class Test { public: Test* operator&() { return this; } const Test* operator&()const { return this; } };
综合应用
class Test { public: //构造函数 Test() { cout << "Test()" << this << endl; } //拷贝构造函数 Test(const Test& t) { cout << "Test(const Test&):" << this << endl; } //赋值操作符重载 Test& operator = (const Test& t) { cout << this << "=" << &t << endl; if (&t != this) { ;//进行赋值操作 } return *this; } //析构函数 ~Test() { cout << "~Test():" << this << endl; } }; Test FunTest(Test t) { Test tt1; Test temp(t); tt1 = temp; return tt1; } void TestFunc() { Test t1; Test t2; t2 = FunTest(t1); } int main() { TestFunc(); return 0; }
此程序的调用过程为: