引言:C++中引入了类的定义,与C语言不同的是,C++的类中不仅可以封装有类的成员变量,还能封装成员函数,且引入了成员限定符的概念,将类进行封装使得访问安全且明确。
一.类的定义
1.格式
- 类关键字为class****, 关键字后跟 类名,大括号{}里的为类的主体,右}结束后还有一个分号不能省略。
- 类的变量称作类的成员变量;类的函数称作类的方法或者成员函数。
- 定义在类里的函数默认为inline。
我们想到,C语言结构体的概念酷似类。但结构体中只能定义变量,而类中不仅可以定义变量,还能定义函数。(在C++中struct也被封装为了一个类,类中成员都是默认公有)
声明和定义都在类中,建议实现简单功能函数,因为在类中定义的函数默认内联。
声明定义分离,建议类的声明在.h文件,定义则在.cpp中实现
cpp
student.h
#include<iostream>
using namespace std;
class Student
{
public:
void print(); //类内声明函数,类外实现
private:
int _age;
float _height;
}
cpp
student.cpp
#include"student.h"
void student::print()
{
cout << _name << " "<< _age << " " << _height << endl;
}
2.访问限定符
这是C++中封装的 用于控制类的成 员在何处可以被访问的关键字。
2.1 public 公有成员
适用于所有代码,包括类内部,类外部,以及子类 。 一般用于定义类的对外接口。
2.2 private 私有成员
只适用于本类 ,一般用于隐藏内部数据,以及实现细节,防止被修改或被查看。
2.3 protected 保护成员
适用于本类以及希望子类继承的 , 一般用于定义子类可以访问的接口以及数据。
- public修饰的成员在类外可以直接被访问,而protected和private修饰的成员在类外不能直接被访问
- 注意:访问限定符的作用范围是从出现到第二个运算符出现为止这个范围内。
- 类中定义成员如果没被访问限定符修饰,则默认为私有,struct默认为公有。
- 我们在开始接触时候,就记得一般成员变量限制为私有或保护,需要给外部使用的成员函数放在public内即可。
3.类作用域
作用:类的所有成员都在类的作用域中,但是要类外定义,想知道在哪个类中。就是要通过类名和 域运算符:: 来明确访问的是哪个类的成员。
cpp
class Stack
{
public:
// 成员函数
void Init(int n = 4);
private:
// 成员变量
int* array;
size_t capacity;
size_t top;
};
//声明和定义分离,需要指定类域
void Stack::Init(int n)
{
array = (int*)malloc(sizeof(int) * n);
if (nullptr == array)
{
perror("malloc申请空间失败");
return;
}
capacity = n;
top = 0;
}
二.类的实例化
1.什么是实例化
-
类是一个设计蓝图,只是声明,不占物理内存空间,以这个蓝图也就是类类型创建一个类对象,这个过程就叫做类的实例化,实例化的对象是占用物理内存的。一个类类型可以实例化多个对象。
cppclass Date { public: void Init(int year, int month, int day) { _year = year; _month = month; _day = day; } private: // 只是声明,没有开空间 int _year; int _month; int _day; }; int main() { Date d1; //这就是定义实例化的对象 Date d2; d1.Init(2024, 3, 31); //对定义的对象初始化 return 0; }
2.对象大小怎么计算
当是空类时候,C++中空类大小为一字节,是用来标识这个类的。
不是空类,则类大小就是成员变量之和,自然也需要遵循内存对其原则。
注意:类中的成员函数是否应该存储在类对象中呢,因为类对象需要调用函数指针,要是将函数指针存储在对象的内存空间中,对象要是多了,那岂不是会很浪费,所以只需要存储成员变量即可。
但实际上,函数指针本身就不需要存储,调用函数也就是在编译过程中call地址,然后通过代码区找到函数位置后调用。所以说在编译链接过程中就会去找到函数地址,无需存储函数指针。
内存对⻬规则 (详情见C语言章节结构体)
- 第⼀个成员在与结构体偏移量为0的地址处。
- 其余成员变量遵循对其数整数倍地址处存储!(对齐数是编译器默认对其数与该成员大小的较小值。)要是默认4,成员是char,则为1.
- 总大小为最大对其数的整数倍。
三.this指针
this指针实际上就是一个指向类对象的指针,是右值,只可以访问无法修改,this指向的对象成员是左值,可以被修改。
cpp
class Date
{
public:
//实际上首个参数是this指针
// void Init(Date* const this, int year, int month, int day)
void Init(int year, int month, int day)
{
_year = year;
this->_month = month;
this->_day = day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
d1.Init(&d1,2025,10,7) //实际上首个参数是隐藏的,就是隐藏的this指针,用来找到d1的成员变量
return 0;
}
------------编译器进行编译时,看到的成员函数实际上也和我们所看到的不一样,每个成员函数的第一个形参实际上是一个隐含的this指针,该指针用于接收调用函数的对象的地址,用this指针就可以很好地访问到该对象中的成员变量。
------------实际上,this只是一个负责代替对象,访问对象的成员以及类中成员函数的指针,可以初始化为空,但为空了则就只能访问成员函数了,要想访问成员变量必须解引用this,但空指针解引用肯定会非法访问导致崩溃。
------------将调用this与编译链接起来,我们要理解的是,this和call都是在编译时候执行,编译过程将对象地址压栈,压栈的首地址就是this首地址,是通过对this进行偏移来找到成员变量的
------------成员函数是通过编译时候call代码段的函数地址来做到执行函数的
