一、类的定义
类的本质是定义出一个类型
C++中用class定义类。而且可以用类名表示类型。
类里面包括:
- 类的属性/成员变量:类中的变量
- 类的方法/成员函数:类中的函数
注!类里面中的成员函数默认为inline
类和struct的不同点在于,类里面可以定义函数,同时类名可以表示类型。而struct必须用typedef才能重新命名。
不过在C++中struct升级成了类(可以用struct定义类),同时兼容C中struct的用法。
但是还是推荐用class定义类。
class Person
{
public:
void Init(const char* name, int age, int tel)
{
strcpy(_name, name);
_age = age;
_tel = tel;
}
void Print()
{
cout << "姓名:" << _name << endl;
cout << "年龄:" << _age << endl;
cout << "电话:" << _tel << endl;
}
private:
char _name[10];
int _age; //习惯上成员变量会加上一个特殊标识,比如这里的"_"
int _tel;
};
int main()
{
Person p1; //类名就是类型
p1.Init("张三", 19, 1201);
p1.Print();
return 0;
}
如上图,简单的定义了一个类(将class换成struct也可以实现该效果)。
可以发现,有两个陌生的关键词public和private
由此引入了访问限定符的概念。
访问限定符包括:
- public:可以直接被访问
- private:不能被访问
- protected:不能被访问
访问限定符限定的是类外。限定类外能否直接访问类里面的成员。
访问限定符的作用域是从该访问限定符开始到下一个访问限定符为止,若没有下一个访问限定符则就到 } 即为止
注意的是,如果不加任何访问限定符,class定义的类是默认private,而struct定义的类是public
同时成员变量默认是不允许修改的,所以要用private来限制
在C++入门中有提到,在C++中域被划分为四个,其中就有一个类域。
在类外定义类中成员的时候 ,就需要使用 :: 来指明成员属于哪个类域(因为不同的域中可以定义相同的变量名)
如上,演示一下怎么指明类域
二、实例化
类实例化出对象就是用类在内存中创建对象的过程
注! 类里面的成员变量不会分配内存空间,用类实例化对象才会分配空间
一个类可以实例化出多个对象
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private://声明成员变量
int _year;
int _month;
int _day;
//注意哈,声明和定义的区别就是,前者没有分配空间,后者分配了空间
};
int main()
{
Date d1; //用Date类实例化出两个对象
Date d2;
d1.Init(2024, 9, 7);
d1.Print();
d2.Init(2024, 9, 8);
d2.Print();
return 0;
}
我们可以把类理解成一个设计图,设计图本身不能干什么,但是用设计图设计出来的东西却可以发挥大作用。类的实例化也是这样。
那如何计算类实例化对象的大小嘞?
对象只存储成员变量
因为用类实例化出的多个对象当中的每个对象里面的成员变量一般都是不一样的,就如上面的日期。但是调用的成员函数只有一个,如果每个对象都保存相同的成员函数就太冗余了,其次函数被编译后是一段指令,在对象中是没有办法保存的。
C++规定类实例化对象也要符合内存对齐的规则。
内存对齐规则:
- 第一个成员在结构体偏移量为0的地址处
- 接下来的成员变量对齐到对齐数的整数倍地址处
- 对齐数 = 编译器默认对齐数(VS对齐数为8)与该成员变量大小的较小值
- 最后要满足,结构体的总大小得是最大对齐数的整数倍
- 注:如果嵌套了结构体,该结构体对齐到自己最大对齐数的整数倍上
用个图例来解释一下
如果类对象没有成员变量则对象的大小为1
因为如果一个字节都不给,怎么表示这个对象存在过嘞
这里有个问题哈,为什么要内存对齐?
- 平台兼容性:不是所有的硬件平台都能访问任意内存地址上的数据。
- 硬件要求:许多处理器和硬件架构对特定数据类型的访问是有对齐的要求的。例如某些处理器可能要求访问4字节整数的地址必须是4的倍数。如果未对齐,可能会导致性能下降甚至错误。
提高性能:内存对齐可以显著提高CPU访问内存的速度。当数据对齐时,CPU可以一次性读取所需的数据,而无需进行额外的内存访问。如果数据未对齐,CPU可能需要两次或更多次的访问来获取完整的数据,从而降低性能
三、this指针
不知道你有没有这样的疑惑,为什么d1和d2都调用了同一个成员函数,但是这个成员函数却能够分清是d1还是d2。
为了给不同对象进行区分,C++用一个隐含的this指针解决了这个问题
在编译器编译后,类里面的成员函数默认会在形参的第一个位置加一个this指针,指明到底是哪个对象(我们在自己敲代码的时候,实参和形参位置上不能显示的加this,不过函数体内可以加,但是通常也是省略不加)
成员函数访问成员变量的本质就是通过this指针访问的
class Date
{
public:
//void Init(Date* const this, int year, int month, int day)
void Init(int year, int month, int day)
{
this->_year = year;//函数体内可以加this指针
_month = month;//但是通常不加
_day = day;
}
//void Print(Date* const this)
void Print()
{
cout <<this-> _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2;
//d1.Init(&d1, 2024, 9, 7); //实参和形参位置上是不能够加this指针的,编译时编译器会自己处理
d1.Init(2024, 9, 7);
d1.Print();
//d2.Init(&d1, 2024, 9, 8);
d2.Init(2024, 9, 8);
d2.Print();
return 0;
}