什么是类
首先要知道cpp和c语言的区别,c语言是面向过程的,而cpp是面向对象的,面向对象的特性就有:封装,继承和多态,这里的封装,就是封装类;
究竟什么是类,比如现在在一个学校,需要管理学生的信息,学生的信息是由姓名,学号,性别,联系方式来定义一个学生的信息;将这些可以表示学生的特征封装起来,就是一个类;
我们在c语言中,会用struct定义一个结构体,这个结构体其实就是一个简单的“类”
比如定义一个学生类:
struct suudent_t
{
char name[20];
char sex[5];
int identity;
int telephone;
};
这就是一个简单的学生类,而我们用这里的student_t创建出来的变量,就是类类型创建的对象;
student_t stu1,stu2;
这里的stu1和stu2就是student_t创建的两个对象,类和对象的关系可以理解成:
类是对象属性的描述,对象是类的具体实现;
如何定义类
class关键字:
上面的学生类在cpp中可以定义成:
class student_t
{
string name;
string sex;
int identity;
int telephone;
void SetStudent(string s, string gender, int n1, int n2)
{
name = s;
sex = gender;
identity = n1;
telephone = n2;
}
}
这里可以直接在源文件里面定义,也可以在头文件中声明类,在源文件中定义类
如果要采用第二种方式定义类的话,就得在头文件student.h中写成:
#pragma once
class student_t
{
string name;
string sex;
int identity;
int telephone;
void SetStudent(string s, string gender, int n1, int n2);
///函数做简单声明
}
在源文件中
#include "student.h"
void student_t::SetStudent(string s, string gender, int n1, int n2)
{
name = s;
sex = gender;
identity = n1;
telephone = n2;
}
这里就多了个作用域,因为类也是个作用域;所以在类外定义函数的时候需要用::
访问限定符
在c语言或者是cpp中我们用struct定义一个类,都可以在类外自由的访问它的变量;但是如果用class定义就不一样了
发现这里就行不通,为什么用class定义的类无法在类外访问变量?
访问限定符:
1、private
2、public
3、protected
protected类的在这里暂时不做讨论;
用struct定义的类里面的成员变量默认都是public类型的,也就是可以在类外访问的,但是class定义的默认都是private类型的,它是私有成员变量,不允许在类外访问的,但是如果改成public就变成可以在类外访问的了;
对象在内存中的存储方式
对象在类中是如何进行存储的?对于每个类类型对象的成员变量和成员方法每个都要进行存储吗?
首先看成员变量的存储:
这里可以看到对象在存储成员变量的时候,同样采取内存对齐的方式存储;
那对于成员函数呢?
是不是还是要再开辟额外的空间为成员函数用呢?
其实不然,即使对象不同,其实调用的方法是同一个,只不过对象中的数据在变化;优化一下可以将重复的代码放入代码段,只保存其地址就可以优化很多;
那也就是说只要保存函数的入口地址,将它当作指针来看吗?
相当于Date类中是:
三个int和一个指针
sizeof(Date)应该是16吧
加入了一个函数,对象的大小并没有变化。
所以正确的存储方式是只保存了成员变量;
但是类同样是有区别的,存在空类;
难道空类创建的对象大小就是0吗?因为没有成员变量啊
但是,空类的大小是1;
这里A虽然是个空类,但是它有大小!
因为如果空类类型的对象大小是0,其创建的对象就没有了区别,无法做到区分;
我们都知道在函数中创建的局部变量是创建在栈上的,并且根据大小向高地址拓展,包括main函数中的变量,如果我们将空类类型对象的大小设置为0,那a1和a2的地址不就重合了吗,但是我们仔细看监视窗口发现,a1和a2它们的地址并不相同;
换句话说如果地址相同了,怎么做到区分呢。。。
this指针
重新回到上面的日期类问题,
这里为什么通过不同的对象调用相同的成员函数还能不出错呢?成员方法是怎么知道谁调用的它呢,成员函数的参数里面明明只有三个int类型参数,并没有指明是哪个对象。
但其实成员函数是有隐藏参数的,这个参数就是 this指针
为什么打印出来的这个this指针和d1,d2,d3的地址完全相同呢,而且在这个函数中并没有显式定义这个this指针,反倒可以打印出这个this指针,证明这个this指针是编译器自动生成的而且是默认传参的;
this指针的值和调用成员函数的对象地址相同,目的就是让成员函数知道是谁在调用它;
那this指针是和调用成员函数的对象地址相同,我可以不可以修改它的指向,让它指向另一个指向从而修改另一个对象的内容呢?
很显然是不可以的,this指针的类型是类类型 * const this;
这里稍作解释:
const int* p:表示p的指向可以改变,p指向空间中的内容不可以被改变;
int* const p:表示p的指向不可以被改变,p指向空间中的内容可以改变;
所以可以知道,this指针的指向是不可以改变的;并不是一个可修改的左值;而且在非必要情况下,没必要用this->成员变量;
必要情况:函数参数和成员变量重名了
那么this指针究竟通过什么方式传参呢?这么隐蔽的传参方式
看看反汇编:
这里可以看见在该函数调用约定_thiscall 下:函数参数是从右往左传参的
所以第一个参数是21,第二个参数是10,第三个参数是2023,第四个参数和前三个参数入栈方式都不一样,前三个参数都是mov进行参数压栈的,但是[d1](表示d1的地址),是通过rcx寄存器传递压栈的;
那么this指针可以为空吗?
可以为空,
这里在pd=nullptr之后,很明显d1的地址和this指针都为0了,是合法的;
但是不能在this指针为nullptr之后继续访问它的成员变量,对一个空指针继续访问,是访问未分配的地址,是内存访问出错;(nullptr->year这种变量没意义)