1.this指针
(一)对象存储模型
- 我们都知道一个类中即有成员变量,又有成员函数,那么对象模型的存储方式是怎样的呢?
- 我们可以先看一下C语言是如何做的?
struct Person
{
char _name[20];
char _gender[3];
int _age;
};
void SetPersonInfo(Person* p, const char* name, con st char* gender, int age)
{
strcpy(p->_name, name);
strcpy(p->_gender, gender);
p->_age = age;
}
void PrintPersonInfo(Person* p)
{
cout<<p->_name<<" "<<p->_gender<<" "<<p->_ag e<<endl;
}
- 从上述代码中,我们可以看到,函数如果要操作类对象,必须将类对象的地址给进来,否则无法对实参对象进行修改
- 在C++中也是按照C中这种类似的方式进行处理的,只不过成员函数这里的指针是隐藏的,指向了调用该函数的对象本身,这个隐藏的指针就是this指针
- 所以对于类的存储方式,每个对象只保存数据信息,代码和数据分开进行存储,调用相关函数时通过对象本身的this指针指向相关函数即可
(二)this指针
- this指针指向的是类实例化出的对象,所以this指针的类型是类类型* const
- this指针只是指向对象,并不是对象本身的一部分,所以不影响sizeof的结果
- this指针可以明确对象想要调用的函数,所以this指针作用域在类"成员函数”的内部
- this指针是“类非静态成员函数”的第一个默认的隐含的参数,编译器会自动进行传递,不需要编写者显示传递
- 只有类的菲静态成员函数可以使用this指针,其他任何函数都不可以使用
2.类的六个默认成员函数
(一)构造函数
- 概念
- 构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时,由编 译器自动调用,在对象的生命周期内只调用一次,保证每个数据成员都有 一个合适的初始值
class Date { public: Date(int year, int month, int day) { _year = year; _month = month; _day = day; } private: int _year; int _month; int _day; }; int main() { Date d(2017, 12, 25); return 0; }
- 特性
- 函数名与类名相同
- 没有返回值,原因是构造函数具有隐式的返回值,该值由系统内部调用
- 新对象被创建时,由编译器自动调用构造函数,而且在这个对象的生命周期内仅且调用一次
- 构造函数也可以进行函数重载,实参决定了调用哪个构造函数
- 无参构造函数和带有缺省值的构造函数都认为是缺省的构造函数,并 且缺省的构造函数只能有一个
- 有初始化列表(可以不用)
- 注意:每个成员只能在初始化列表中出现一次; 初始化列表仅适用于初始化类的数据成员,这些成员的初始化顺序没有前后之分,初始化顺序其实是和数据成员在类中的定义顺序有关;尽量不要使用成员初始化成员,而且初始化顺序最好是和成员的定义顺序保持一致
- 最好使用初始化列表进行初始化的成员有:1)引用成员变量 2)const成员变量 3)类类型成员(该类有非缺省的构造函数)
- 如果没有显式定义时,编译器会合成一个默认的构造函数
- 构造函数不能用const修饰 ,原因是构造函数有可能是要修改类的成员变量的,所以最好是非const类型最好
- 构造函数不能为虚函数,这个原因后面博客中又说道
- 作用
- 构造并且初始化对象
- 类型转化
对于单个参数构造函数,可以将其接受参数转化成类类型对象。用 explicit修饰构造函数,抑制由构造函数定义的隐式转换,explicit 关键字位于类内部的构造函数的声明上,在类的定义体外部的定义上不再重复
(二)拷贝构造函数
- 概念
- 只有单个形参,而且该形参是对本类类型对象的引用(常用const修饰),这 样的构造函数称为拷贝构造函数。
- 拷贝构造函数是特殊的构造函数,创建 对象时使用已存在的同类对象来进行初始化,由编译器自动调用
class Date { public: Date(int year, int month, int day) : _year(year) , _month(month) , _day(day) {} Date(const Date& d) : _year(d._year) , _month(d._month) , _day(day) {} private: int _year; int _month; int _day; }; int main() { Date d1(2017, 12, 25); Date d2(d1); }
- 特征
- 构造函数的重载,构造函数的性质拷贝构造函数均满足
- 参数必须使用类类型对象引用传递,原因是传值会无休止下去调用拷贝构造函数创建临时对象
- 如果没有显式定义,系统会自动合成一个默认的拷贝构造函数。默认 的拷贝构造函数会依次拷贝类的数据成员完成初始化
- 使用场景
- 对象实例化对象
- 作为函数参数
- 作为函数返回值
(三)析构函数
- 概念
- 析构函数:与构造函数功能相反,在对象被销毁时,由编译器自动调用,完成类 的一些资源清理和汕尾工作
class Array { public: Array(int capacity = 10) , _array(NULL) , _capacity(capacity) , _size(0) { _array = (int*)malloc(sizeof(int)*_capacity); } ~Array() { if(_array) { free(_array); _capacity = _size = 0; } } private: int* _array; size_t _size; size_t _capacity; };
- 特性
- 析构函数在类名(即构造函数名)加上字符~
- 析构函数无参数无返回值
- 一个类有且只有一个析构函数。若未显示定义,系统会自动生成缺省 的析构函数,所以析构函数不能进行重载
- 对象生命周期结束时,C++编译系统系统自动调用析构函数 注意析构函数体内并不是删除对象,而是做一些清理工作