C++ (5)C++中的this指针与默认成员函数
(一)自引用指针
每个对象都有属于自己的数据成员,但是所有对象的成员函数却合用同一份.成员函数如何判断当前调用自己的是哪个对象?
定义一个类:
class Date{
public:
void Display(){
std::cout<<_year<<"-"<<_month<<"-"<<_day<<std::endl;
}
private:
int _year;
int _month;
int _day;
};
- 每个
成员函数
都有一个指针形参
,它的名字是固定
的,称为this指针,this指针是隐式的(构造函数
没有this隐式指针
)- 编译器会对
成员函数
进行处理,在对象
调用成员函数
时,对象地址作实参传递给成员函数
的第一个形参this指针this指针
是成员函数隐含指针形参
,是编译器自己处理的,我们不能在成员函数
的形参中添加this指针
的参数定义,也不能在调用时显示传递对象的地址
给this指针
(二)默认成员函数
2.1构造函数
日期的成员变量是私有的,我们如何初始化这些私有的成员变量?可以通过void SetDate(int year,int month,int day)
来完成初始化吗?
成员变量是私有的,要对它们进行初始化,必须有一个公有成员函数来进行.同时这个函数应该有且仅在定义对象时自动执行一次,这时调用的成员函数称为构造函数(constructor).构造函数是特殊的成员函数,其特征如下:
- 函数名与类名相同
- 有返回值,返回的是新构造的对象本身
- 对象构造(对象实例化)时系统自动调用对应的构造函数,在对象生命周期内仅调用一次
- 构造函数可以重载
- 构造函数可以在类中定义,也可以在类外定义
- 如果类定义中没有给出构造函数,则C++编译器自动产生一个缺省的构造函数,但只要我们定义了一个构造函数,系统就不会自动生成缺省的构造函数
- 无参的构造函数和全缺省值的构造函数都认为是缺省构造函数,并且缺省的构造函数只能有一个
- 构造函数不能用const修饰
- 构造函数不能是虚函数
- 有初始化列表
2.1.1无参构造函数&带参构造函数
- 无参构造函数
Date()
{}
- 带参构造函数
方式一
:
Date(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
}
方式二
初始化列表
:
成员变量按照声明顺序依次初始化,而非初始化列表出现的顺序
Date(int year,int month,int day):_year(year),_month(month),_day(day){}
若类中有以下成员,必须要用初始化成员列表:
- 引用类型的数据成员(引用创建时必须初始化)
- const修饰的数据成员(常量创建时必须初始化)
- 类类型成员(该类有非缺省的构造函数)【没有缺省构造函数的类成员变量】
来看一个例子
:
class Date{
public:
Date(){
std::cout<<"调用无参构造\n"<<std::endl;
}
Date(int year,int month,int day):_year(year),_month(month),_day(day){
std::cout<<"调用有参构造\n"<<std::endl;
}
private:
int _year;
int _month;
int _day;
};
int main(){
Date d1;//调用无参构造函数
Date d2(2020,12,17);//调用带参构造函数
Date d3();//注意这里没有调用d3的构造函数定义出d3
}
注意上面例子中
Date d1
与Date d3()
的区别:
执行Date d3()
并没有定义出一个d3
对象,所以构造函数不会被执行,调用成员函数也会出现语法错误.这里从语法上看是定义了一个名为d3,返回值类型为Date类的无参数函数.
2.1.2缺省构造函数
若缺省参数声明和定义分离,则可以在声明或定义中给默认参数
2.2拷贝构造函数
创建对象时使用同类对象来进行初始化,这时所用的构造函数称为拷贝构造函数(Copy Constructor),拷贝构造函数是特殊的构造函数.特征:
- 使用场景:1.用对象实例化对象,2.作为函数参数,3.对象作为函数返回值,若参数为类类型,则使用引用操作
- 拷贝构造函数其实是一个构造函数的重载
- 拷贝构造函数的参数必须使用引用传参,使用传值方式会引发无穷递归调用
- 若未显示定义,系统会默认缺省的拷贝构造函数.缺省的拷贝构造函数会依次拷贝类成员进行初始化
**思考:一个构造函数面试题目**
:
- 类的成员变量有两种初始化方式:初始化列表和函数体内直接赋值.使用初始化列表的方式更高效,为什么?
2.3析构函数
当一个对象的声明周期结束时,C++编译器会自动调用一个成员函数,这个特殊的成员函数即析构函数(destructor).析构函数是特殊的成员函数,其特征如下:
- 析构函数在类名前加上字符
~
- 析构函数无参数、无返回值
- 一个类有且仅有一个析构函数,若未显示定义,系统会自动生成缺省的析构函数
- 对象声明周期结束时,C++编译系统自动调用析构函数
- 注意析构函数体内并不是删除对象,而是做一些清理工作,即释放内存空间
析构函数最简单的例子:
class Date{
public:
~Date(){ //析构函数
}
...
private:
int _year;
int _month;
int _day;
};
2.4运算符重载
为了增加程序的可读性,C++支持运算符重载.进行运算符重载时,必须写一个运算符函数,其名字规定为operator
后跟随一个要重载的运算符,运算符重载函数一般可以定义为友元运算符重载函数和成员运算符重载函数.
- 运算符重载函数
operator@
返回类型通常与它操作的类的类型相同(可以返回任何类型,甚至是void类型)- 运算符重载不能改变运算符
操作数个数
、优先级
、结合特性
(x = a/b*c
)=
、[]
、()
不能定义为友元运算符重载函数- 5个不能重载的运算符
.
.*
::
sizeof
?:
**
不能重载,因为它不是C++运算符- 重载运算符的调用及编译器的处理
一个赋值运算符重载的简单例子
class Date{
public:
Date(){} //构造函数
Date(const Date& d);//拷贝构造函数
Date& operator=(const Date& d);//=成员函数方式重载 不能通过有元方式
private:
int _year;
int _month;
int _day;
};
Date::Date(const Date& d):_year(d._year),_month(d._month),_day(d._day){}
Date& Date::operator=(const Date& d){
if(this != &d){ //防止d=d的赋值
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;
}
return *this;
}
int main(){
Date d1; //构造函数
Date d2 = d1; //拷贝构造函数
Date d3; //构造函数
d3 = d1; //赋值运算符重载函数
return 0;
}