介绍类的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;
}
此程序的调用过程为: