一.类与对象概述
C++是一门面向对象的语言,因此在C++中引入了类这个概念,并通过类的实例化来得到对象。
类的:类是一个用户定义的类型,用于封装数据(属性)和操作这些数据的方法(成员函数)。
举个例子,一个人有身高、体重、年龄、姓名等属性,我们可以通过一些成员函数来设置或者更改这些属性,也可以通过实例化来修改,或者使用这些成员函数来做一些其它的功能。我们可以把这个类看作一个人,它的所有属性都可以放在类里,而成员函数则可以对这些属性做一些操作。
对象:对象是类的一个具体实例。每个对象都有自己的数据成员的副本,和类定义的方法。
我们可以通过实例化一个对象后来访问类的属性和方法。
二.类
1.1类的格式
类书写的基本格式。
class 类名{};
struct 类名{};
class和struct都是定义类的关键字,如果你学过C语言你可能会觉得类和结构体有相似之处,不过结构体中不能声明函数,除此之外还有更多不一样的地方,下面我们来一一介绍。
1.2类的书写
下面我们来写一个人类。
class Person
{
public:
Person(){}
Person(int age,int high,int weight){}
~Person(){}
void setage(int age);
private:
int m_age;
double m_high;
double m_weight;
protected:
string name;
};
类的内部可以声明函数,也可以实现函数,也可以在类外实现函数,不过要加上范围解析运算符::
例如:
void Person::setage(int age)
{
m_age=age;
}
类内部的public、private、protected是访问限定符,可以决定一个成员函数或成员变量是否可以在类外被访问,public限定符可以在类外的任何地方访问,而private和protected我们暂时可以认为它们是一样的,由它们限定的成员函数或成员变量在类外不可以访问。protected需要在我们学习到继承后就可以区分它和private了。
它们的作用域是从一个访问限定符到下一个访问限定符出现,或者到类结束。一个类中可以出现多个相同的访问限定符。
那么如果什么访问限定符都不加,class的默认访问权限是private,struct的默认访问权限是public。
如果你想要在类内声明函数,而在类外实现函数的话,就要在函数名的前面加上::来限定函数所属的类,有了类名限定后,在一个程序里需要使用到多种数据结构时,我们就可以将尾插、尾删等操作直接定义为push、pop而不用烦恼怎么取名字了。
而类内部的与类名相同的一些函数有什么作用呢?本篇文章我们暂不介绍,下一篇文章会专门介绍。
三.实例化
1.概念
概念:⽤类类型在物理内存中创建对象的过程,称为类实例化出对象。
如果不实例化对象,我们在类外是无法访问成员变量和成员方法的。下面我们通过代码来看看。
通过图片我们可以看到如果不实例化出一个对象的话,按平时我们调用函数的方法,我们是无法访问类内函数或变量的。而在实例化出一个对象后,我们就可以正常访问类内的公有内容了。
类就像是一张图纸,我们盖房子时需要参照它来建造房子,图纸只能存储数据,不可以住人,实例化则是把房子建造了出来可以让人住进去,在建造的过程中还可以根据实际对图纸的一些数据实时更改。
2.对象大小
int、double等都是C/C++的内置类型,它们都有大小,而类作为一种自定义类型,也应该有大小,下面我们来看一下一个类是如何计算大小的。
类是以内存对齐作为计算规则的,我使用的编译器是VS2022,默认对其数是8。
计算机的内存在逻辑上被视为一个连续的地址空间,这段空间是被分成若干个字节的。
下面我们来了解一下内存对齐规则
让我们先来了解一下结构体偏移量:结构体偏移量是指在内存中,结构体成员相对于结构体起始地址的字节偏移量。每个成员在结构体中的位置决定了它的偏移量。偏移量通常是从结构体的起始位置开始计算的,以字节为单位。通常偏移量以0为开始。它表示从结构体开始到该成员的字节距离。
规则1:第⼀个成员在与结构体偏移量为0的地址处。
规则2:其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
规则3:对⻬数 = 编译器默认的⼀个对⻬数 与 该成员⼤⼩的较⼩值。
规则4:结构体总⼤⼩为:最⼤对⻬数(所有变量类型最⼤者与默认对⻬参数取最⼩)的整数倍。
下面我们通过图片来具体学习怎么使用对齐规则和计算类的大小。
class Person
{
public:
double s;
int age;
};
int main()
{
Person a;
cout << sizeof(a);
cout << sizeof(Person);
}
在VS2022上,此程序的运行结果是两个16。下面我通过画图来带你了解一下为什么是16。
当然内存肯定不是这样分布的,图中只是为了方便。
Person类的第一个成员变量为double,占8个字节,而VS默认对齐数为8,根据规则3得到对齐数为8,我们在内存中从0开始向后数8个格子,就得到了下一个变量的偏移量。根据规则3我们可以得到对齐数为4,在向后数4个格子,最后得到的实际字节数为12,但是根据规则4结构体的大小必须为最大对齐数的整数倍,程序中double为8个字节,所以内存需要向它的整数倍对齐,所以应该为16。
这是你可能会觉得空间被浪费了,其实这样设计也是有原因的,cpu访问读取只能从内存的整数倍开始,像下面这个类,如果没有内存对齐,它就需要多次读取数据后在进行拼接,降低了效率,也可以说这样的设计是为了方便读取数据。
class Person
{
public:
char c;
int x;
};
同时需要注意一个空的类的大小并不是0,而是1,这是为了占位标识对象存在,如果空类的大小为0,怎么能证明它存在呢。
如果类内没有成员变量只有函数的话,其大小也为1。
class student{};
class Person
{
public:
void getage(){}
};
四.this指针
定义:this
指针是一个隐式参数,指向调用成员函数的对象。
class Person
{
public:
void PersonInit(string name, int age)
{
//this=nullptr;这是错误的,this只有读的权限,这样会放大权限
this->m_age = age;
this->m_name = name;
}
Person& getage() { return *this; }
string m_name;
int m_age;
};
在需要返回对象自身的引用时,也可以使用thi指针。