尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

C++类与对象(上):从struct到class,一文吃透核心概念

C++类与对象(上):从struct到class,一文吃透核心概念
📅 发布时间:2026/7/16 10:31:26

文章目录

    • 引言
  • 一、类的定义:从struct到class的升级
    • 1.1 类定义格式
    • 1.2 struct也能定义类?
  • 二、访问限定符:封装的第一道防线
  • 三、类域:不要把成员函数写成全局函数
  • 四、实例化:把设计图变成房子
    • 4.1 什么是实例化?
    • 4.2 对象大小:成员函数去哪了?
  • 4.3 内存对齐规则
  • 五、this指针:让对象知道“我是谁”
    • 5.1 this指针从哪来?
    • 5.2 this指针的特性
    • 5.3 经典面试题
  • 六、C语言 vs C++:实现Stack的对比

简介:掌握C++类与对象的精髓

本文面向具备C语言基础、正在向C++进阶的开发者,系统讲解C++类与对象这一核心概念。我们将从C语言的结构体出发,对比引入C++类所带来的根本性变革——不仅仅是语法升级,更是编程范式的跃迁。

你将深入理解:

  • 封装与访问限定符如何构建数据安全的第一道防线;
  • 类域如何优雅地组织代码,告别全局函数的混乱;
  • 实例化过程与对象内存布局的奥秘;
  • this指针的底层机制及其在面向对象设计中的关键作用。

通过清晰的代码对比、内存布局分析和经典面试题剖析,本文旨在帮助你不仅“会用”类与对象,更能“懂其所以然”,为后续学习更复杂的面向对象特性打下坚实基础。

引言

在C++编程的世界里,类与对象是面向对象编程(OOP)的基石,也是从C语言过程式思维向C++面向对象思维转变的关键一步。本文旨在为有一定C语言基础、正在学习C++的开发者提供一个清晰、系统的学习路径,通过对比C语言的结构体与C++的类,深入剖析访问限定符、类域、实例化、this指针等核心概念。

你将不仅理解C++类与对象的语法形式,更能掌握其背后的设计哲学:封装、数据隐藏和代码组织。文章通过大量代码示例和内存布局分析,帮助你从"知道怎么写"进阶到"明白为什么这么写",最终能够运用面向对象思想解决实际问题。无论你是准备面试还是希望提升代码设计能力,本文都将为你提供实用的知识和思考框架。

一、类的定义:从struct到class的升级

如果你写过C语言的结构体,那类对你来说就没什么神秘的。

1.1 类定义格式

在C++中,用class关键字定义一个类:

classStack{public:// 成员函数voidInit(intn=4){_a=(int*)malloc(sizeof(int)*n);if(nullptr==_a){perror("malloc申请空间失败");return;}_capacity=n;_top=0;}voidPush(intx){// 扩容逻辑..._a[_top++]=x;}private:// 成员变量int*_a;size_t _capacity;size_t _top;};// 注意:这个分号不能省略!

这里有几个细节需要注意:

  • class是定义类的关键字,Stack是类名,大括号里是类体,末尾的分号一定不能少。
  • 类体中的变量叫成员变量(属性),类体中的函数叫成员函数(方法)。
  • 为了区分成员变量,通常会在成员变量名前加_或m,比如_a、_capacity。这不是语法强制要求,只是行业惯例。
  • 定义在类里面的成员函数默认是inline的。

1.2 struct也能定义类?

C++兼容C语言的所有语法,同时把struct也升级成了类。升级后的struct和class最大的区别就一个:

| struct的成员默认是public,class的成员默认是private。

// C语言的struct用法——需要typedeftypedefstructListNodeC{structListNodeC*next;intval;}LTNode;// C++的struct用法——不用typedef,可以直接当类型用structListNodeCPP{voidInit(intx){next=nullptr;val=x;}ListNodeCPP*next;intval;};

虽然struct也能用,但在实际开发中,推荐用class定义类,语义更清晰。

二、访问限定符:封装的第一道防线

C++提供了三种访问限定符来实现封装:

限定符访问范围使用场景
public类内外都能访问对外接口(成员函数)
private仅类内部可访问成员变量、内部辅助函数
protected类内部和子类可访问继承场景中使用

关键规则:

  • 访问权限从当前限定符开始,到下一个限定符或类结束为止。
  • class定义类时,默认访问权限是private;struct默认是public。
  • 通常成员变量设为private或protected,对外暴露的接口函数设为public。
classDate{public:voidInit(intyear,intmonth,intday){_year=year;_month=month;_day=day;}private:int_year;int_month;int_day;};

这样做的好处是:外部代码无法直接修改对象的内部数据,只能通过我们提供的public接口来操作,数据的安全性得到了保障。

三、类域:不要把成员函数写成全局函数

类定义了一个新的作用域,所有成员都在这个作用域内。如果要在类体外定义成员函数,需要用 “::” 作用域操作符指明它属于哪个类:

classStack{public:voidInit(intn=4);private:int*_a;size_t _capacity;size_t _top;};// 声明和定义分离,需要指定类域voidStack::Init(intn){_a=(int*)malloc(sizeof(int)*n);if(nullptr==_a){perror("malloc申请空间失败");return;}_capacity=n;_top=0;}

如果不写Stack::,编译器会把Init当作全局函数,找不到_a等成员变量就会报错。

四、实例化:把设计图变成房子

4.1 什么是实例化?

类是抽象的“设计图”,限定了一个类有哪些成员,但成员变量此时只是声明,并没有分配内存空间。用类在物理内存中创建对象的过程,就叫做实例化。实例化出来的对象,才真正占有内存空间,存储具体的成员变量数据。

classDate{public:voidInit(intyear,intmonth,intday){_year=year;_month=month;_day=day;}private:int_year;// 声明,没有开空间int_month;int_day;};intmain(){Date d1;// 实例化对象,分配内存Date d2;// 可以实例化多个对象d1.Init(2024,3,31);d2.Init(2024,7,5);return0;}

一个类可以实例化出多个对象,每个对象的成员变量各自独立,互不干扰

4.2 对象大小:成员函数去哪了?

关键问题:对象内存里存什么?

成员函数编译后是一段指令,存在代码段,不在对象里。如果每个对象都存一份函数指针,100个对象就要存100次同样的地址,太浪费了。

所以C++的设计是:对象中只存储成员变量,成员函数单独放在代码段。

空类呢?如果一个类没有任何成员变量,C++会给它分配1字节的内存空间。这不是为了存数据,而是为了在内存中标记这个对象“存在过”。

classA{int_i;char_ch;};// 8字节(考虑内存对齐)classB{};// 1字节classC{};// 1字节

| 空类的大小是1,是为了让不同的对象在内存中有不同的地址,避免零大小带来的问 题。

4.3 内存对齐规则

对象的大小计算遵循内存对齐规则(和C语言结构体一样):

  1. 第一个成员在偏移量为0的地址处。
  2. 其他成员要对齐到对齐数的整数倍地址。对齐数 = min(成员大小, 编译器默认对齐数)。VS中默认对齐数为8。
  3. 结构体总大小为最大对齐数的整数倍。
  4. 嵌套结构体时,需要对齐到自身最大对齐数的整数倍。
classA{char_ch;// 1字节int_i;// 4字节,从偏移量4开始(补3字节)};// 总大小:4 + 4 = 8字节

五、this指针:让对象知道“我是谁”

5.1 this指针从哪来?

有这样一个问题:Date类实例化出d1和d2两个对象,它们调用同一个Init函数时,函数怎么知道自己处理的是哪个对象的数据?

答案:是this指针。

编译器在编译时会默默给每个成员函数添加一个隐藏参数——当前对象的地址,这就是this指针。比如这个成员函数:

voidInit(intyear,intmonth,intday){_year=year;}

经过编译器处理后,实际是这样:

voidInit(Date*constthis,intyear,intmonth,intday){this->_year=year;}

5.2 this指针的特性

  • this指针是隐藏的,你不能在实参和形参的位置显式写出来,但可以在函数体里使用它。
  • this的类型是类名* const,即this的指向不能改变。
  • 成员函数中访问成员变量,本质都是通过this指针访问。
  • 在const成员函数中,this的类型是const 类名* const。
classDate{public:voidInit(intyear,intmonth,intday){// this = nullptr; // 错误!this不能被赋值this->_year=year;// 可以显式使用_month=month;// 编译器自动翻译为 this->_month}private:int_year;int_month;int_day;};

5.3 经典面试题

题目1:

classA{public:voidPrint(){cout<<"A::Print()"<<endl;}private:int_a;};intmain(){A*p=nullptr;p->Print();return0;}

结果:正常运行。 因为Print函数没有访问任何成员变量,编译器生成调用指令时,函数地址已经确定,不需要解引用p来访问数据,所以不会崩溃。

题目2:

classA{public:voidPrint(){cout<<"A::Print()"<<endl;cout<<_a<<endl;// 访问成员变量}private:int_a;};intmain(){A*p=nullptr;p->Print();return0;}

结果:运行崩溃。 因为Print内部访问了_a,这会被翻译为this->_a,而this是nullptr,解引用非法。

题目3: this指针存在哪个区域?
栈区。 this是一个隐含的形参,在调用成员函数时由编译器压栈传递,所以存在栈中(部分编译器的优化可能会把它放在寄存器中)。

六、C语言 vs C++:实现Stack的对比

我们把C语言和C++实现的Stack放在一起,直观感受一下封装带来的变化。
C语言实现(面向过程)

typedefstructStack{STDataType*a;inttop;intcapacity;}ST;voidSTInit(ST*ps);voidSTPush(ST*ps,STDataType x);intSTTop(ST*ps);voidSTPop(ST*ps);intSTEmpty(ST*ps);

痛点:

  • 数据和操作函数是分离的,维护成本高。
  • 用户可以直接修改top等成员变量,绕过函数,破坏栈的状态。
  • 每个函数都必须手动传递结构体指针。

C++实现(面向对象)

classStack{public:voidInit(intn=4);voidPush(STDataType x);voidPop();intTop();boolEmpty();voidDestroy();private:STDataType*_a;size_t _capacity;size_t _top;};intmain(){Stack s;s.Init();s.Push(1);s.Push(2);while(!s.Empty()){cout<<s.Top()<<endl;s.Pop();}s.Destroy();return0;}

C++带来的变化:

  1. 数据和操作数据的函数封装在一起,逻辑更清晰。
  2. 成员变量设为private,外部无法直接访问,必须通过公开接口操作,数据安全性大大提高。
  3. 成员函数不需要手动传对象地址,this指针隐式传递,代码更简洁。
  4. 缺省参数让代码更灵活。
  5. 类名直接作为类型名使用,不用再写typedef。

虽然C++版本在底层逻辑上和C版本没有本质区别,但封装带来的代码组织方式的变化,让代码更安全、更易于维护和扩展。这就是面向对象的核心优势之一。

总结:
这篇文章我们梳理了C++类和对象的基础知识:

  1. 类的定义:用class定义类,成员变量和成员函数组织在一起。struct也可以用,但默认访问权限是public。
  2. 访问限定符:public对外公开,private内部私有,protected留给继承。
  3. 类域:成员函数可以在类外定义,需要加类名::指明归属。
  4. 实例化:类是设计图,对象是房子。对象中只存储成员变量,成员函数存在代码段。空类占1字节。
  5. 内存对齐:对象的大小遵循内存对齐规则,不是成员变量的简单相加。
  6. this指针:每个成员函数隐藏的第一个参数,指向当前调用的对象。nullptr调用成员函数时,如果访问成员变量就会崩溃。
  7. 封装的优势:通过C和C++的Stac k对比,直观感受了封装带来的安全性和可维护性提升。

下一篇文章会讲构造函数、析构函数、拷贝构造函数这些默认成员函数,敬请期待。

本文总结:类把数据和行为绑定在一起,访问限定符控制外部访问权限,this指针让对象知道“自己是谁”。理解这些,你就迈入了面向对象编程的大门。

相关新闻

  • 数字孪生+AI视觉:C#上位机落地产线仿真与缺陷预测,拒绝PPT架构
  • 【嵌入式系统】从物理地址到别名:深入解析寄存器映射的C语言实现
  • Mediapipe实时人体姿态估计与手部追踪实战指南

最新新闻

  • Linux动态链接库PLT/GOT机制解析与优化实践
  • Unity开发者必备的Android开发指南与优化技巧
  • 2026 仓山黄金回收门店盘点,易奢福 6 家门店覆盖全商圈 - 肉松卷
  • 腾讯混元大模型:轻量化技术与移动端部署实践
  • 2026年西安新城区迪奥包包回收,易奢福如何让戴妃包马鞍包卖出理想价? - ys韩
  • 金价高位!济宁正规黄金回收渠道测评对比出炉,6 家实体门店资质、价格、服务一览 - 不晚生活号

日新闻

  • Toon Boom Harmony 高效工作流:从节点视图到镜头标记的实战技巧
  • 真力时售后维修电话,为您提供专业腕表保养与故障修复服务权威公示(2026年7月最新) - 亨得利官方服务中心
  • 【C++】类和对象--构造函数进阶(初始化列表与explicit)

周新闻

  • IX9104 PCIe5.0 高速交换芯片@ACP#完整规格 + 应用场景总结
  • Unity游戏集成Coze智能体:实现NPC智能对话与知识库联动
  • SAP EPIC 建行回单查询:从标准类CL_EPIC_EXAMPLE_CN_CCB_GHTD到Z类的5处关键修改

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号