👋 欢迎阅读

📑 目录
-
[八、this 指针](#八、this 指针)
一、前置讲解
在进入正文前,先花 10 秒了解本篇会反复用到的核心概念,带着印象去读正文,学习效率更高。
🧠 前置知识一:面向过程与面向对象
前者关注过程 (步骤 + 函数),后者关注对象(谁来做 + 对象间交互)。
📦 前置知识二:类(class)
把成员变量 和成员函数 封装在一起,定义时只在逻辑上有结构、不分配物理内存。
🔐 前置知识三:访问限定符
public / protected / private 决定成员类外能否直接访问 ,是封装的语法基础。
🏗️ 前置知识四:实例化与对象模型
用类创建对象 才分配空间;对象里只存成员变量 ,成员函数放在公共代码段。
👉 前置知识五:this 指针
每个非静态成员函数 都隐含一个指向当前对象的指针,由编译器自动传。
二、面向过程与面向对象
C 语言是面向过程 的,关注 的是过程 :分析出求解问题的步骤,通过函数调用逐步解决问题。

C++ 是基于面向对象 的,关注 的是对象 :把一件事情拆分成不同的对象 ,靠对象之间的交互完成。

| 维度 | 面向过程 | 面向对象 |
|---|---|---|
| 关注点 | 过程(步骤) | 对象(谁来做) |
| 组织方式 | 函数 + 数据结构,两者分离 | 数据 和操作封装在一起 |
| 解决问题 | 拆成步骤,函数逐个调用 | 拆成对象 ,靠对象交互完成 |
| 典型语言 | C | C++、Java |
三、类的初步认识
C 语言结构体中只能定义变量 ,而在 C++ 中,结构体内不仅可以定义变量,也可以定义函数。
不过 C++ 不常直接用 struct------新事物一般要配新名字。之所以改变 struct 的功能,是因为 C++ 对类的理解也是一种结构体,因此才对 struct 做了升级。
3.1 类的定义格式
格式:class 类名
cpp
class className
{
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
class 为定义类的关键字 ,className 为类的名字 ,{} 中为类的主体 。注意类定义结束时后面的分号不能省略。
类中的内容称为类的成员:
| 成员类型 | 叫法 |
|---|---|
| 类中的变量 | 类的属性 ,或称成员变量 |
| 类中的函数 | 类的方法 ,或称成员函数 |
3.2 类的两种定义方式
| 方式 | 做法 | 注意 |
|---|---|---|
| 方式一:全部写在类体中 | 声明和定义都放在类体里 | 成员函数如果在类中定义 ,编译器可能会把它当成内联函数处理 |
| 方式二:声明与定义分离 | 声明放 .h,成员函数定义放 .cpp |
成员函数名前需要加 类名:: ------这其实就是类域 的使用,和之前的命名空间域是一个道理 |


一般情况下更期望采用第二种方式 ,因为它看起来分工更明确。
3.3 成员变量命名规则的建议
先看一段"很僵硬"的代码:
cpp
// 我们看看这个函数,是不是很僵硬?
class Date
{
public:
void Init(int year)
{
// 这里的 year 到底是成员变量,还是函数形参?
year = year;
}
private:
int year;
};
year = year; 自己给自己赋值,成员变量根本没被改------所以一般都建议给成员变量加个前缀区分:
cpp
// 建议写法一:成员变量加下划线前缀
class Date
{
public:
void Init(int year)
{
_year = year;
}
private:
int _year;
};
// 建议写法二:成员变量加 m 前缀
class Date
{
public:
void Init(int year)
{
myear = year;
}
private:
int myear;
};
| 命名风格 | 示例 | 好处 |
|---|---|---|
| 下划线前缀 | _year |
一眼区分成员变量 与形参 |
| m 前缀 | myear |
同上,常见于老代码库 |
| 不区分 | year |
形参与成员同名时会自赋值,是真实 bug |
四、类的访问限定符及封装
4.1 访问限定符
C++ 实现封装的方式是:用类将对象的属性与方法结合在一块 ,让对象更加完善,并通过访问权限选择性地将其接口提供给外部的用户使用。

【访问限定符说明】
| 序号 | 规则 |
|---|---|
| 1 | public 修饰的成员在类外可以直接被访问 |
| 2 | protected 和 private 修饰的成员在类外不能直接被访问(此处两者类似,区别后面再讲) |
| 3 | 访问权限作用域从该访问限定符出现的位置开始,直到下一个访问限定符出现时为止 |
| 4 | 如果后面没有访问限定符,则一直到 } 即类结束 |
| 5 | class 的默认访问权限为 private ,struct 为 public(因为 struct 要兼容 C) |
💡 注意:访问限定符只在编译时有用 。当数据映射到内存后,没有任何访问限定符上的区别。
4.2 封装
面向对象的三大特性是:封装、继承、多态 。在类和对象阶段,主要研究封装。
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
封装本质上是一种管理,让用户更方便使用类 。库提供的函数都是一种接口,我们不需要知道它怎么实现,只需要知道它有什么作用------就像买电视,你不需要知道它怎么运行,只需要知道怎么用。
| 关键词 | 含义 |
|---|---|
| 有机结合 | 数据和操作数据的方法放在同一个类里 |
| 隐藏细节 | 属性和实现细节对外不可见 (private / protected) |
| 公开接口 | 只把必要的成员函数 用 public 暴露出去 |
| 本质 | 一种管理 手段,让使用者更省心、更安全 |
五、类的作用域
类定义了一个新的作用域 ,类的所有成员都在类的作用域 中。在类体外定义成员时,需要使用 :: 作用域操作符 指明成员属于哪个类域 ------这一点在讲命名空间的时候也提过一次。
cpp
class Person
{
public:
void PrintPersonInfo();
private:
char _name[20];
char _gender[3];
int _age;
};
// 这里需要指定 PrintPersonInfo 是属于 Person 这个类域
void Person::PrintPersonInfo()
{
cout << _name << " " << _gender << " " << _age << endl;
}
💡 类外定义成员函数时,
类名::不只是"格式要求",而是告诉编译器这个PrintPersonInfo属于Person类域 ------少了它,编译器会把它当成一个普通的全局函数,而全局函数是访问不到私有成员_name的。
六、类的实例化
用类这个类型创建对象的过程,称为类的实例化。
| 要点 | 说明 |
|---|---|
| 类是描述 | 类是对对象进行描述的,是一个模型 ,限定了类有哪些成员,但并没有分配实际的内存空间来存储它(就像入学时填的学生信息表,表格本身描述学生,但不是学生) |
| 对象才占空间 | 一个类可以实例化出多个对象 ,实例化出的对象占用实际的物理空间,存储类成员变量 |
cpp
int main()
{
Person._age = 100; // 编译失败:error C2059: 语法错误:"."
Person study;
study._age = 100;
return 0;
}
运行结果:
编译失败:error C2059: 语法错误:"."
因为类没有分配实际的内存空间存储数据,所以不能直接给成员赋值 ;只有类的实例化对象才分配了实际内存,才能给成员赋值。
再打个比方:类实例化出对象,就像用建筑设计图建造出房子 。类就像设计图 ,只设计出需要什么东西,但并没有实体建筑存在;实例化出的对象才能实际存储数据、占用物理空间。

💡 这里有个容易混的点:"类不分配物理内存"不等于"类没有大小" 。
sizeof(类)是逻辑层面 的答案(比如成员变量按内存对齐算出 8 字节),空类的sizeof是 1 字节(占位) ;但类本身不会分配物理空间,只有实例化出的对象才真正占那几字节。
七、类对象模型
要回答的问题:如何计算类对象的大小? 类中既可以有成员变量,又可以有成员函数,那么一个类的对象中到底包含了什么?

从图里可以看清:对象只保存成员变量,成员函数存放在公共的代码段。因此:
| 结论 | 说明 |
|---|---|
| 类的大小 | 实际就是该类中成员变量之和 |
| 注意点 | 要算上内存对齐,对齐后才是真实大小 |
| 成员函数 | 不占对象内存,所有对象共享同一份代码 |
八、this 指针
8.1 this 指针的引出
cpp
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, d2;
d1.Init(2022, 1, 11);
d2.Init(2022, 1, 12);
d1.Print();
d2.Print();
return 0;
}
运行结果:
2022-1-11
2022-1-12
问题来了 :Date 类中有 Init 与 Print 两个成员函数,函数体里没有任何区分对象的代码 ,那么当 d1 调用 Init 时,该函数是怎么知道该设置 d1 而不是 d2 的呢?
C++ 通过引入 this 指针解决该问题:
C++ 编译器给每个"非静态成员函数"增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),函数体中所有"成员变量"的操作,都是通过该指针去访问。只不过这些操作对用户是透明的------用户不需要传递,编译器自动完成。
8.2 this 指针的特性
| 序号 | 特性 |
|---|---|
| 1 | this 指针的类型是 类名* const ------即 this 指针本身不能被赋值(不能让它指向别的对象) |
| 2 | 只能在"成员函数"内部使用 |
| 3 | this 指针本质上是"成员函数"的形参 :对象调用成员函数时,把对象地址作为实参 传给 this 形参。所以对象中不存储 this 指针 |
| 4 | this 指针是"成员函数"第一个隐含的指针形参 ,一般由编译器通过 ecx 寄存器自动传递,不需要用户传递(不同编译器、不同调用约定下的传递方式可能不同) |

💡 记住这一条就能解释很多面试题:this 是形参,不是成员 。所以它不占对象大小 ;也正因为它是形参,静态成员函数没有它(静态函数不属于任何对象)。
九、复盘(附答案)
💡 思考题
-
面向过程和面向对象 :两者的核心区别 是什么?C++ 兼容 C 语言,如何在 C++ 中同时使用两种编程范式?
-
类的初步认识 :C++ 中
struct和class的唯一区别 是什么?为什么 C++ 要保留struct而不是完全用class替代? -
访问限定符及封装 :
public、protected、private的访问范围 分别是什么?封装的意义是什么? -
类的实例化 :类实例化对象的内存分配时机 是什么?栈对象和堆对象的生命周期有何不同?
-
类对象模型 :空类的大小是多少 ?为什么?成员函数是否占用对象的内存空间?
-
this 指针 :
this指针的本质是什么 ?为什么this指针不能被修改 ?静态成员函数为什么没有this指针?
📝 答案
-
面向过程 以函数 为核心,数据与操作分离 ;面向对象 以对象 为核心,将数据和方法封装在一起。C++ 兼容 C,可以在 C++ 中继续写 C 风格代码,同时使用类、继承、多态等面向对象特性。
-
C++ 中
struct和class的唯一区别 是默认访问权限 :struct默认public,class默认private。保留struct是为了兼容 C 语言 ,同时也用于定义纯数据聚合体。 -
public类内外均可访问 ,protected类内和派生类可访问 ,private仅类内可访问 。封装 隐藏实现细节、暴露接口,提高代码安全性 和可维护性。 -
类定义时不分配内存 ,实例化(创建对象)时才分配 。栈对象 在作用域结束时自动销毁 ;堆对象 需要手动
delete释放,否则内存泄漏。 -
空类的大小为 1 字节 ,因为 C++ 标准要求任何对象必须有唯一地址 ,1 字节是占位 用的。成员函数不占用对象内存空间 ,它们存放在代码段,被所有对象共享。
-
this是指向当前对象的常量指针 (类名* const this),不能被修改 (不能改指向,但可以通过它改对象内容)。静态成员函数没有this指针 ,因为它们属于类而不是对象 ,没有"当前对象"可指,也就无法访问非静态成员。
结语
如果本文对你有帮助,欢迎:
👍 点赞 | ⭐ 收藏 | 👤 关注作者 | 💬 留言交流你的疑问或补充
你的每一次互动都是我继续更新的动力,我们下一篇见!🚀