C++ 类与对象:封装特性的实现与实战应用

C++ 类与对象:封装特性的实现与实战应用

C++ 类与对象:封装特性的实现与实战应用

在这里插入图片描述

💡 学习目标:掌握类与对象的核心概念,理解封装的本质与价值,能够独立设计并实现具有封装特性的 C++ 类。
💡 学习重点:类的定义与对象实例化、访问权限控制、构造函数与析构函数的使用、封装的实战场景应用。

一、类与对象的核心概念

结论:类是 C++ 面向对象编程的核心载体,是对一类事物属性和行为的抽象描述;对象是类的具体实例,是内存中实际存在的实体。

1.1 类的组成

一个完整的 C++ 类通常包含两部分:

  • 成员变量:描述类的属性,如人的姓名、年龄,圆的半径等。
  • 成员函数:描述类的行为,如人的吃饭、跑步,圆的面积计算等。

1.2 类的定义格式

#include<iostream>#include<string>usingnamespace std;// 类的定义:以 class 关键字开头classPerson{// 访问权限修饰符,后续详细讲解private:// 成员变量:属性 string name;int age;public:// 成员函数:行为voidsetInfo(string n,int a);// 设置属性voidshowInfo();// 展示属性};

1.3 对象的实例化与使用

对象是类的具体实例,实例化对象的语法与定义普通变量类似,有两种常见方式:

// 方式1:栈上实例化对象 Person p1; p1.setInfo("张三",20); p1.showInfo();// 方式2:堆上实例化对象(需要用 new 关键字) Person *p2 =newPerson(); p2->setInfo("李四",25); p2->showInfo();// 堆上对象使用后需手动释放内存delete p2; p2 =nullptr;

二、封装的本质:访问权限控制

💡 封装是面向对象三大特性之一,核心是通过访问权限修饰符隐藏类的内部实现细节,只对外暴露必要的接口,提高代码的安全性和可维护性。

C++ 提供了三种访问权限修饰符,作用域从修饰符出现位置开始,到下一个修饰符或类结束为止。

2.1 三种访问权限

修饰符访问范围核心特点
public类内、类外、子类对外公开的接口,允许外部直接访问
protected类内、子类仅允许类自身和子类访问,类外不可见
private类内仅允许类自身访问,子类和类外均不可见

⚠️ 注意事项:类的默认访问权限是 private,结构体 struct 的默认访问权限是 public

2.2 访问权限的代码演示

#include<iostream>#include<string>usingnamespace std;classPerson{// 默认 private string addr ="北京市";private: string name;int age;public:voidsetInfo(string n,int a){// 类内可以访问 private 成员变量 name = n; age = a;}voidshowInfo(){ cout <<"姓名:"<< name <<" 年龄:"<< age <<" 地址:"<< addr << endl;}};intmain(){ Person p; p.setInfo("王五",30); p.showInfo();// 错误:name 是 private 成员,类外无法访问// p.name = "赵六";// 错误:addr 是默认 private 成员,类外无法访问// p.addr = "上海市";return0;}

三、构造函数与析构函数:对象的生命周期管理

💡 构造函数负责对象的初始化,析构函数负责对象的清理工作,二者均由编译器自动调用,无需手动触发。

3.1 构造函数

3.1.1 构造函数的特性
  • 函数名与类名完全相同,没有返回值类型,连 void 都不能写。
  • 对象实例化时自动调用,且只调用一次。
  • 可以重载,支持不同方式的初始化。
  • 如果没有手动定义构造函数,编译器会自动生成一个空参构造函数
3.1.2 构造函数的分类与实现
#include<iostream>#include<string>usingnamespace std;classPerson{private: string name;int age;public:// 1. 空参构造函数(默认构造函数)Person(){ cout <<"空参构造函数被调用"<< endl; name ="未知"; age =0;}// 2. 有参构造函数Person(string n,int a){ cout <<"有参构造函数被调用"<< endl; name = n; age = a;}// 3. 拷贝构造函数(用已有对象初始化新对象)Person(const Person &p){ cout <<"拷贝构造函数被调用"<< endl; name = p.name; age = p.age;}voidshowInfo(){ cout <<"姓名:"<< name <<" 年龄:"<< age << endl;}};// 构造函数的调用方式intmain(){// 方式1:括号法 Person p1;// 调用空参构造 Person p2("张三",20);// 调用有参构造 Person p3(p2);// 调用拷贝构造// 方式2:显示法 Person p4 =Person(); Person p5 =Person("李四",25);// 方式3:隐式转换法(不推荐) Person p6 ={"王五",30}; p1.showInfo(); p2.showInfo();return0;}

⚠️ 注意事项:使用括号法调用空参构造时,不能写 Person p1(),这会被编译器识别为函数声明,而非对象实例化。

3.2 析构函数

3.2.1 析构函数的特性
  • 函数名以 ~ 开头,后跟类名,没有返回值类型,也没有参数。
  • 对象销毁时自动调用,且只调用一次。
  • 不能重载,一个类只能有一个析构函数。
  • 如果没有手动定义析构函数,编译器会自动生成一个空析构函数。
3.2.2 析构函数的实战价值

析构函数常用于释放对象占用的动态资源,如堆内存、文件句柄等,避免内存泄漏。

#include<iostream>#include<string>usingnamespace std;classStudent{private: string *name;// 堆内存指针int age;public:// 有参构造:分配堆内存Student(string n,int a){ cout <<"有参构造函数被调用"<< endl; name =newstring(n);// 堆上分配字符串 age = a;}// 析构函数:释放堆内存~Student(){ cout <<"析构函数被调用"<< endl;if(name !=nullptr){delete name;// 释放堆内存 name =nullptr;// 防止野指针}}voidshowInfo(){ cout <<"姓名:"<<*name <<" 年龄:"<< age << endl;}};intmain(){// 栈对象:函数结束时自动销毁,触发析构函数 Student s1("小明",18); s1.showInfo();// 堆对象:手动 delete 时触发析构函数 Student *s2 =newStudent("小红",17); s2->showInfo();delete s2; s2 =nullptr;return0;}

四、封装的实战案例:设计一个圆形类

💡 需求:设计一个圆形类 Circle,包含半径属性,提供设置半径、计算面积、计算周长的功能,要求通过封装隐藏半径的直接访问,确保半径的合法性。

4.1 需求分析

  1. 成员变量:半径 radius,设置为 private,避免外部直接修改。
  2. 成员函数:
    • setRadius(double r):设置半径,需校验半径是否大于 0。
    • getRadius():获取半径,对外提供只读接口。
    • getArea():计算圆的面积,公式:S=πr2S = \pi r^2S=πr2。
    • getPerimeter():计算圆的周长,公式:C=2πrC = 2\pi rC=2πr。
  3. 构造函数:提供空参和有参构造,初始化半径。

4.2 完整代码实现

#include<iostream>#include<cmath>usingnamespace std;// 定义圆周率常量constdouble PI =acos(-1.0);classCircle{private:// 私有成员变量:半径double radius;public:// 空参构造:默认半径为 0Circle(){ radius =0.0; cout <<"空参构造函数被调用,默认半径 0"<< endl;}// 有参构造:初始化半径Circle(double r){setRadius(r);// 调用 setRadius 做合法性校验 cout <<"有参构造函数被调用,半径设置为 "<< radius << endl;}// 设置半径:校验合法性voidsetRadius(double r){if(r >=0){ radius = r;}else{ cout <<"⚠️ 半径不能为负数,默认设置为 0"<< endl; radius =0.0;}}// 获取半径doublegetRadius(){return radius;}// 计算面积doublegetArea(){return PI * radius * radius;}// 计算周长doublegetPerimeter(){return2* PI * radius;}};intmain(){// 测试空参构造 Circle c1; cout <<"圆1 半径:"<< c1.getRadius()<< endl; cout <<"圆1 面积:"<< c1.getArea()<< endl; cout <<"圆1 周长:"<< c1.getPerimeter()<< endl; cout <<"-------------------------"<< endl;// 测试有参构造:合法半径 Circle c2(5.0); cout <<"圆2 半径:"<< c2.getRadius()<< endl; cout <<"圆2 面积:"<< c2.getArea()<< endl; cout <<"圆2 周长:"<< c2.getPerimeter()<< endl; cout <<"-------------------------"<< endl;// 测试非法半径设置 Circle c3(-3.0); cout <<"圆3 半径:"<< c3.getRadius()<< endl;// 动态修改半径 c3.setRadius(4.0); cout <<"修改后圆3 半径:"<< c3.getRadius()<< endl; cout <<"修改后圆3 面积:"<< c3.getArea()<< endl;return0;}

4.3 运行结果

空参构造函数被调用,默认半径 0 圆1 半径:0 圆1 面积:0 圆1 周长:0 ------------------------- 有参构造函数被调用,半径设置为 5 圆2 半径:5 圆2 面积:78.5398 圆2 周长:31.4159 ------------------------- ⚠️ 半径不能为负数,默认设置为 0 有参构造函数被调用,半径设置为 0 圆3 半径:0 修改后圆3 半径:4 修改后圆3 面积:50.2655 

五、封装的优势与开发规范

封装的核心优势

  1. 数据安全:通过 private 限制成员变量的直接访问,避免非法数据的输入。
  2. 代码复用:类的成员函数可以被多个对象复用,减少重复代码。
  3. 易于维护:类的内部实现细节被隐藏,修改内部逻辑时,只要对外接口不变,就不会影响外部代码。
  4. 接口清晰:对外只暴露必要的成员函数,让使用者无需关注内部实现,降低学习和使用成本。

5.1 C++ 类的开发规范

  • 成员变量尽量设置为 private,通过 publicget/set 函数访问和修改。
  • 构造函数要保证成员变量的初始化,避免出现未初始化的“脏数据”。
  • 涉及动态内存分配的类,必须手动编写析构函数释放资源,防止内存泄漏。
  • 类的命名采用大驼峰命名法,如 PersonCircle;成员函数和成员变量采用小驼峰命名法,如 setInforadius

六、本章总结

✅ 类是属性和行为的抽象,对象是类的具体实例,二者是面向对象编程的基础。
✅ 封装的核心是通过 public/protected/private 控制访问权限,隐藏实现细节,暴露统一接口。
✅ 构造函数负责对象初始化,支持重载;析构函数负责资源清理,不能重载。
✅ 合理使用封装特性,可以显著提升代码的安全性、复用性和可维护性。

Read more

从零开始:在本地搭建一个带知识库的 AI 助手(Ollama + Open WebUI)

从零开始:在本地搭建一个带知识库的 AI 助手(Ollama + Open WebUI)

一文讲清楚:要选哪些工具、需要什么环境、整体架构长什么样,以及一步步实现到能用的程度。 一、为什么要在本地搭一个 AI 助手? 过去一年,大模型从“新奇玩意儿”迅速变成“日常生产力工具”。但如果你只用网页版 ChatGPT / 文心一言 / 通义千问,会碰到几个很现实的问题: * 数据隐私:公司内部文档、个人笔记、聊天记录,你敢全部塞到线上吗? * 网络依赖:在飞机上、高铁里,或者公司内网严格管控时,在线 AI 直接“失联”。 * 额度与费用:免费额度有限,稍微重度一点就要付费,而且你也不知道自己的数据会不会被拿去训练。 本地部署一套 “AI + 知识库” 的好处就非常直观: 1. 数据完全不出本地,满足隐私合规要求。 2. 断网也能用,随时随地调取你的“第二大脑”。 3. 可定制:可以给团队搭一个“

By Ne0inhk
Flutter for OpenHarmony: Flutter 三方库 sanitize_html 彻底杜绝 XSS 注入风险(鸿蒙 Web 内容安全净化)

Flutter for OpenHarmony: Flutter 三方库 sanitize_html 彻底杜绝 XSS 注入风险(鸿蒙 Web 内容安全净化)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在开发 OpenHarmony 应用时,如果我们需要在 UI 中渲染来自后端的 HTML 内容(例如文章正文、用户评论),或者使用 flutter_html 等库,一个致命的安全风险就是 XSS (跨站脚本攻击)。恶意代码可能会通过 <script> 标签或 onerror 属性在你的 App 内执行非法逻辑。 sanitize_html 是一个轻量级且极高效的 HTML 净化库。它采用白名单机制,能瞬间过滤掉所有不安全的标签和属性,确保你在鸿蒙 App 内渲染的每一行 Web 内容都是绝对安全的。 一、核心防御机制解析 sanitize_html 遵循“默认拒绝”

By Ne0inhk

前端保持和服务器时间同步的方法【使用vue3举例】

你只管努力!剩下的交给时间! 目录 * 引言: * 方法一: 轮询(定时请求服务器时间) * 优点: * 缺点: * 方法二:使用WebSocket * 优点: * 缺点: * 方法三:时间戳校正 * 优点: * 缺点: * 方法四: 使用NTP(网络时间协议) * 优点: * 缺点: * 方法五:使用SSE(Server-Sent Events) * 优点: * 缺点: * 总结: 引言: 保持前端与服务器时间同步是一个常见的需求,特别是在需要确保时间一致性的应用中,比如在线投票、实时聊天或游戏等。以下是一些方法来实现这一目标: 方法一: 轮询(定时请求服务器时间) 可以定时向服务器发送请求获取当前时间,以此来更新前端的时间显示。 <template><div><h1>当前时间:

By Ne0inhk
前端异常捕获与统一格式化:从 console.log(error) 到服务端上报

前端异常捕获与统一格式化:从 console.log(error) 到服务端上报

🧑 博主简介:ZEEKLOG博客专家,「历代文学网」(公益文学网,PC端可以访问:https://lidaiwenxue.com/#/?__c=1000,移动端可关注公众号 “ 心海云图 ” 微信小程序搜索“历代文学”)总架构师,首席架构师,也是联合创始人!16年工作经验,精通Java编程,高并发设计,分布式系统架构设计,Springboot和微服务,熟悉Linux,ESXI虚拟化以及云原生Docker和K8s,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。 🤝商务合作:请搜索或扫码关注微信公众号 “ 心海云图 ” 前端异常捕获与统一格式化:从 console.log(error) 到服务端上报 引言 在前端开发中,异常监控是保证应用稳定性的重要一环。当用户遇到页面白屏、功能不可用等问题时,如果能及时收集到详细的错误信息(包括堆栈、

By Ne0inhk