一、this指针
关于this指针的一个精典回答:
当你进入一个房子后,
你可以看见桌子、椅子、地板等,
但是房子你是看不到全貌了。
对于一个类的实例来说,
你可以看到它的成员函数、成员变量,
但是实例本身呢?
this是一个指针,它时时刻刻指向你这个实例本身。
(一)this 指针:隐含在非静态成员函数中的特殊指针,它是当前正在调用此成员函数的对象的指针。
(二)this指针的作用:成员函数对成员变量的引用实际是通过this指针访问的,也就是说,成员变量 this->成员变量;另外,如果成员函数需要访问当前对象,也可以通过this指针,*this就是当前对象。
(三)this 指针的特性:1)this 指针的类型:类类型 * const
2)this 指针并不是对象本身的一部分,不影响 sizeof 的结果
3)this 指针的作用域在类“成员函数”的内部
4)this 指针是“类成员函数”的第一个默认隐含参数,编译器自动维护传递,类编写者不 能显示传递
5)只有在类的非静态成员函数中才可以使用 this 指针,其它任何函数都不可以
二、六个默认的成员函数
类的六个默认成员函数为构造函数,拷贝构造函数、析构函数、赋值操作符重载、取地址操作符重载、const修饰的取地址操作符重载。
(一)构造函数
//日期类的构造函数
Date(int year,int month,int day)
:_year(year)
, _month(month)
, _day(day)
{
cout << "Date(int):" << this << endl;
}
一)构造函数的定义:函数名与类名相同,没有返回值类型,创建类类型对象时,由编译器自动调用,在对象的什么周期内只调用一次。
二)构造函数的特性:1)函数名与类名相同
2)没有返回值
3)新对象被创建时,由编译器自动调用,且在对象的生命周期内仅调用一次
4)构造函数可以重载,实参决定了调用那个构造函数
5)无参构造函数和带有缺省值的构造函数都认为是缺省的构造函数,并且缺省的构造函 数只能有一个
6) 有初始化列表(可以不用)
7)没有显式定义时,编译器会合成一个默认的构造函数
8)构造函数不能用 const 修饰
9)构造函数不能为虚函数
三)构造函数的作用:构造和初始化对象、类型转换
四)隐式类型转换
什么样的构造函数有类型转换的作用? 类的构造函数一定是单参的,如果不是单参其他的的参数要有缺省值
如何禁止隐式类型转换? 在构造函数前加上explicit
五)对象的初始化
class Date
{
public:
Date(int year, int month, int day)
:_year(year);
,_month(month)
,_day(day) //上述三行就是对象的初始化
{}
private:
int _year;
int _month;
int _day;
};
从上述日期类中不难看出,对象的初始化在初始化列表,以一个冒号开始,以逗号分隔数据成员列表,每个成员变量后面跟一个括号放初始值或表达式。
必须在初始化列表初始化的有三类,引用成员变量、const 类类型常量、类里面包含了另外一种类类型的对象(举例:两个类 A类 B类 B类包含A类对象,A类有不是缺省的构造函数,把A类这个对象放在B类的初始化列表初始化)
this指针可以在初始化列表中使用吗?
答:函数体内进行的是赋值,而初始化列表进行的是初始化,这说明对象并不完整,所以不能通过this访问。
编译器什么时候合成构造函数?
如果编译器感觉自己需要时再去合成默认的构造函数,有一下三种情况。
1)如果A类中包含了B类的对象,前提:B类有缺省的构造函数,A类没有显式定义自己的构造函数,编译器 就会为A类合成默认的构造函数
2)如果B类没有缺省参数,A类的构造函数就需要显式的给出构造函数
3)创建对象时需不需要给对象一个初始值,如果需要初始值就需要给出构造函数,一般情况下都要给出构造 函数
注:显式的意思,这个构造函数用户有没有自己定义。
六)缺省参数的概念:在声明或定义函数时为函数的参数指定的一个默认值。
特性:1)带缺省值的参数必须放在参数列表最后面
2)一般放在声明中(声明、定义只能出现一次)
3)缺省值必须是常量或全局变量
4)C语言不支持
(二)拷贝构造函数
//日期类的拷贝构造函数
Date(const Date& d)
:_year(d._year)
, _month(d._month)
, _day(d._day)
{
cout << this->_year << endl;
cout << "Date(const Date&):" << this << endl;
}
一)拷贝构造函数的定义:只有单个形参,该形参是对本类类型对象的引用(常用const修饰)
拷贝构造函数形参为 const 类类型的引用(举例:日期类的拷贝构造函数 Date(const Date& d);)的原因?
答:一般外部实参不需要改变d所对应的内容,如果外部实参需要改变d对应的实体,则不需要给出const。
二)拷贝构造函数的特征:1)特殊的构造函数,是构造函数的重载
2)必须使用类类型对象引用传递
3)如果没有显式定义,系统会自动合成(只实现了拷贝构造函数的功能)。
默认的拷贝构造函数会一次拷贝类的数据成员完成初始化
4)拷贝函数不能重载
为什么必须使用类类型对象引用传递?
拷贝构造函数的调用场景(什么时候调用)?
1)当用同类已存在的对象作为参数,创建对象时,由编译器自动调用(例日期类,Date d1(2018);
Date d2(d1);)
2)作为函数返回值
3)对象实例化对象(例日期类,Date d1(2018);Date d2(d1);)
拷贝构造函数什么时候合成?
(三)析构函数
//析构函数
~Date()
{}
一)析构函数的概念:析构函数与构造函数的功能相反,在对象出函数作用域之前被销毁,由编译器自动调用,完成一些类的资源清理和汕尾工作。
二)析构函数的特性:1)析构函数的函数名与类名相同,且要在函数名前加上字符 ~
2)析构函数没有返回值
3)一个类只有一个析构函数。若没有显式定义,编译器会自动合成缺省的析构函数
4)对象生命周期结束时,编译器会自动调用析构函数
5)析构函数并不会删除对象,只是对对象做一些资源清理
三、运算符重载
(一)运算符重载的概念:重载函数是具有特殊函数名的函数,关键字operator,后面接需要定义的操作符符号。
为什么要有运算符重载?运算符重载有什么意义?
答:看一下这两个例子我想就能够比较清楚的理解为什么要有运算符的重载了。
例一,比如你定义一个类型“学生,现在如果你把两个“学生”类型的变量相加, 那系统怎么知道你要怎么加? 如果是数字的话,系统知道。那这个时候,你就要为“学生”这个类型重载加号运算符,然后在这个重载的方法里实现相加的意义,比如“学生”类型的年龄相加,或是其它东西相加,这个就你自己定义了。
例二,如果你定义了一个 “日期”类,想要把两个日期类相加,系统同样不清楚你要如何相加。这时候你就要为“日期”这个类重载加号运算符,在这个重载函数中实现两个日期类的相加。
综上,也就是说运算符重载是为了适应不同的数据类型,并提高代码的运行效率还有使用简单方便。
哪些运算符不能重载?
(二)重载运算符需要注意的事项:
(1)不能通过连接其他符号来创建新的操作符。例如,operator@。
(2)重载操作符必须有一个类类型或者枚举类型的操作数。
(3)用于内置类型的操作符,其含义不能改变。例如,内置的整形+,不能改变它的含义。
(4)因为有一个隐含形参 this,所以类的重载函数看起来比操作符数数目少1。this 限定为第一个形参。
(5)一般将算术操作符定义为非成员函数,将赋值运算符定义为成员函数,且操作符定义为非成员函数时, 一般将其定义为类的友元。
(6)== 和 != 操作符一定要成对重载使用。
(7)前置++/--必须返回被增量或者减量的引用,后置++/--操作符必须返回旧值,也就是返回值。
(8)输入操作符>>和输出操作符<<必须定义为类的友元函数。
(三)赋值操作符重载
//赋值操作符的重载
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
}
(四)取地址操作符重载
//取地址操作符的重载
Date* operator&()
{
return this;
}
(五)const修饰的取地址操作符重载
//const 修饰的取地址操作符的重载
const Date* operator &()const
{
return this;
}
四、静态成员
(一)静态成员的定义:在类的成员数据或成员函数前加上 static 修饰的类成员,称为类的静态成员。
(二)静态成员的特征:
1)静态成员为所有类对象所共享,不属于某个具体的实例。
2)静态成员可以用 类名: :静态成员 . 静态成员 /类名: :对象 . 静态成员 来访问。
3)类静态成员变量必须在类外定义,定义时不添加 static 关键字
4)类的静态成员函数没有默认的 this 指针,因此静态成员函数中不能使用任何非静态成员
5)静态成员和类的普通成员一样,有访问限定符3种访问级别,有返回值,能被const修饰
静态成员函数能够调用非静态成员函数吗?非静态成员函数可以调用类的静态成员函数吗?
答:静态成员函数不能调用非静态成员函数,因为静态成员函数没有this指针,而调用成员函数的时候,实际是this->成员函数。非静态成员函数可以调用静态成员函数。
静态成员函数与普通成员函数,普通函数的区别?
答:普通函数:属于全局函数,不受具体类和对象的限制,可以直接调用。
普通成员函数:C++ 普通成员函数本质上是一个包含指向具体对象this指针的普通函数,即c++类的普通成员函数都隐式包含一个指向当前对象的this指针。
在介绍静态成员函数之前我们再认识一下静态成员变量,类体中的数据成员的声明前加上static关键字,该数据成员就成为了该类的静态数据成员。静态成员变量的性质:一,static型变量只被初始化一次,下次执行初始化语句会直接跳过;二,static 说明一个类的成员为静态成员,经过static修饰的成员变量属于这个类,不再仅仅属于具体的对象。
静态成员函数: 类体中的成员函数的声明前加上static关键字,该成员函数就成为了该类的静态成员函数。静态成员函数的性质,一,不可以调用类的非静态成员;二,静态成员函数不含this指针。 静态成员函数属于这个类,不再仅仅属于具体的对象。因此类的静态成员函数和类的普通成员函数的区别是:
静态成员函数不包含指向具体对象的this指针;普通成员函数包含一个指向具体对象的this指针。
五、const类型成员
(一)const类型成员的定义:类中用 const 关键字修饰的数据成员或者成员函数就称为const类型成员。
(二)const的使用场景:
1)const修饰形参,一般和引用同时使用
2)const修饰返回值
3)const修饰类的数据成员,必须在构造函数的初始化列表中初始化
4)const修饰类的成员函数,实际上修饰的是隐含的this指针,表示类中不介意对类的任
何成员进行修改
5)在const修饰的成员函数中要对类的某个数据成员进行修改,该数据成员定义声明必 须加mutable关键字
注意事项:1)const对象(const成员函数内)只能调用const修饰的成员函数,不能调用非const成员函数
2)非const对象(非const成员函数内)既可以调用const成员函数,也可以调用非const成员函数
const不能修饰static修饰的成员函数的原因?
答:static修饰的成员函数没有this指针。
C语言中的const与C++中的const的区别?
答:C语言中的const只能修饰变量,且被const修饰的变量还是变量。
C++中const修饰的变量已经是常量,且C++中被const修饰的变量不仅是变量还具有宏的特性。
上述运用到的相关代码:
#include<iostream>
using namespace std;
class Time
{
public:
//时间类的构造函数
Time(int hour = 0, int minute = 0, int second = 0)
:_hour(hour)
, _minute(minute)
, _second(second)
{}
//时间类的拷贝构造函数
Time(const Time& t)
:_hour(t._hour)
, _minute(t._minute)
, _second(t._second)
{}
private:
int _hour;
int _minute;
int _second;
};
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 << this->_year << endl;
cout << "Date(const Date&):" << this << endl;
}
//this指针可以在初始化列表中使用吗?
//函数体内进行的是赋值,而初始化列表进行的是初始化,说明对象不完整,所以不能通过this访问
//赋值操作符的重载
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
}
//取地址操作符的重载
Date* operator&()
{
return this;
}
//const 修饰的取地址操作符的重载
const Date* operator &()const
{
return this;
}
//析构函数
~Date()
{}
private:
int _year;
int _month;
int _day;
Time _t;
};
int main()
{
Date d1(2018, 6, 10);
Date d2(d1); //直接传对象 会接受类类型对象,
//如果对象非常大 通过值传递会产生临时对象
//效率低且浪费空间 因此传类类型的引用 Date(Date& d);称为拷贝构造函数
return 0;
}