核心内容摘要
基于S7-200 PLC与MCGS组态的隧道智能照明控制系统:梯形图程序、接线图与组态设计
类的访问权限public、private 与 protected 详解在C面向对象编程中类的三大访问权限public、private、protected是实现封装特性的核心也是连接类的定义与对象使用的关键纽带。
上一篇博客《C 类与对象类的定义与对象的创建》中我们简单提及了访问权限的作用但很多初学者仅停留在“知道三种权限”的层面分不清它们的访问范围、核心用途也不懂在实际编程中如何合理选择导致写出的代码既不符合封装原则也存在数据安全隐患。
本文将专门拆解public、private、protected三种访问权限从核心定义、访问范围、实战用法到继承场景下的差异、常见误区逐一讲解帮你彻底吃透访问权限的本质掌握“成员变量私有化、成员函数公有化”的封装原则学会在类的设计中合理分配访问权限为后续学习继承、多态打下坚实基础。
核心前提回顾C类的核心作用是封装——将对象的属性成员变量和行为成员函数打包在一起隐藏内部实现细节只暴露外部需要的接口以此保证数据安全性和代码可维护性。
而访问权限修饰符正是实现“隐藏细节、暴露接口”的核心工具它决定了类的成员成员变量、成员函数能在哪些地方被访问。
核心认知访问权限的本质的是什么很多初学者会误以为“访问权限是限制类的成员能否被使用”其实这是片面的。
访问权限的本质是限制“访问范围”——即类的成员成员变量、成员函数能在“类内部”“类外部”“子类内部”这三个场景中的哪些场景被访问和操作。
简单来说访问权限就像“类的访问规则”有的成员比如核心属性只能在类内部操作不允许外部直接访问——对应private有的成员比如外部需要调用的接口需要在类外部、类内部都能访问——对应public有的成员比如供子类继承使用的属性/方法需要在类内部、子类内部能访问不允许外部访问——对应protected。
关键注意访问权限修饰符的作用范围是“从当前修饰符开始到下一个修饰符结束”若未指定访问权限修饰符C默认是private这是初学者最容易踩的坑之一。
逐一看透三种访问权限详解核心重点下面逐一拆解public、private、protected三种访问权限重点掌握“访问范围”和“核心用途”这是区分三种权限的关键也是笔试面试中高频考察的知识点。
private私有权限核心封装最常用1核心定义private修饰的类成员成员变量、成员函数仅能在当前类的内部被访问类外部包括通过对象访问、子类内部均无法直接访问。
2访问范围
总结✅ 类内部可访问成员函数可直接操作private成员变量、调用private成员函数❌ 类外部不可访问无法通过对象直接访问private成员编译报错❌ 子类内部不可访问子类无法直接访问父类的private成员。
3核心用途必记private是实现封装的核心用于存储类的核心属性成员变量隐藏类的内部细节避免外部随意修改数据保证数据安全性。
通俗类比private成员就像“家里的保险柜”只有家里人类内部能打开、操作外人类外部、亲戚子类都不能直接触碰。
4实战示例定义一个Student类将核心属性学号、姓名、成绩设为private演示private的访问限制#includeiostream#includestringusingnamespacestd;classStudent{// 私有成员核心属性仅类内部可访问private:string m_id;// 学号string m_name;// 姓名doublem_score;// 成绩// 私有成员函数仅类内部可调用voidcheckScore(){if(m_score
{coutm_name成绩不及格需要补考endl;}}// 公有成员外部接口后续讲解public:voidsetScore(doublescore){// 类内部可访问private成员变量m_scoreif(score0score
{m_scorescore;// 类内部可调用private成员函数checkScore()checkScore();}else{cout成绩输入错误请输入
之间的数值endl;}}};intmain(){Student stu;//
尝试访问private成员变量m_score错误// stu.m_score 85; // 编译报错m_score是private类外部不可访问//
尝试调用private成员函数checkScore()错误// stu.checkScore(); // 编译报错checkScore()是private类外部不可访问//
通过public接口间接操作private成员正确stu.setScore(
;// 正确setScore()是public可外部调用内部间接操作private成员return0;}5关键说明private成员变量不能被外部直接访问但可以通过public成员函数接口间接访问和修改——这正是封装的核心思想隐藏细节通过接口暴露功能private成员函数通常用于类内部的辅助逻辑比如示例中的checkScore()用于校验成绩不需要外部调用因此设为private避免暴露内部实现日常编程中绝大多数成员变量都应设为private这是保证数据安全性的关键比如示例中通过setScore()的逻辑校验避免输入非法成绩。
public公有权限外部接口最常用1核心定义public修饰的类成员成员变量、成员函数可在类内部、类外部、子类内部均能被访问没有访问限制。
2访问范围
总结✅ 类内部可访问✅ 类外部可访问可通过对象直接访问public成员✅ 子类内部可访问子类可直接访问父类的public成员。
3核心用途必记public用于定义类的外部接口即类对外暴露的功能成员函数供类外部、子类调用以此实现外部与类的交互。
通俗类比public成员就像“家里的大门”所有人类内部、外部、子类都能通过大门进入访问是类与外部交互的唯一通道。
4实战示例延续Student类补充public成员函数作为外部接口实现对private成员的间接操作和访问#includeiostream#includestringusingnamespacestd;classStudent{// 私有成员核心属性隐藏细节private:string m_id;string m_name;doublem_score;voidcheckScore(){if(m_score
{coutm_name成绩不及格需要补考endl;}}// 公有成员外部接口暴露功能public:// 接口1设置学生姓名间接修改private成员m_namevoidsetName(string name){m_namename;}// 接口2设置学生成绩间接修改private成员m_scorevoidsetScore(doublescore){if(score0score
{m_scorescore;checkScore();}else{cout成绩输入错误请输入
之间的数值endl;}}// 接口3获取学生信息间接访问private成员voidshowInfo(){cout学号m_id姓名m_name成绩m_scoreendl;}};intmain(){Student stu;// 类外部通过public接口间接操作private成员正确stu.setName(张
;stu.setScore(
;stu.showInfo();// 类外部通过public接口间接访问private成员// 类外部可直接访问public成员若有public成员变量// 注意通常不建议设置public成员变量避免外部直接修改return0;}5关键说明public的核心作用是“暴露接口”而非“暴露数据”——日常编程中尽量避免设置public成员变量否则会破坏封装导致外部可随意修改数据失去封装的意义所有需要外部调用的功能都应封装为public成员函数接口在接口内部实现对private成员的操作同时可添加数据校验、逻辑判断保证数据安全性public成员是类与外部交互的唯一通道接口的设计应简洁、清晰只暴露外部需要的功能隐藏内部实现细节比如示例中外部无需知道checkScore()的存在只需调用setScore()即可。
protected保护权限继承专用重点1核心定义protected修饰的类成员成员变量、成员函数可在当前类内部、子类内部被访问但类外部无法直接访问——这是protected与private、public的核心区别。
2访问范围
总结✅ 类内部可访问❌ 类外部不可访问无法通过对象直接访问protected成员✅ 子类内部可访问子类可直接访问父类的protected成员。
3核心用途必记protected是为继承场景设计的用于定义“需要被子类继承、但不允许外部访问”的成员。
它兼顾了封装性和继承性既隐藏了类的内部细节不允许外部访问又为子类提供了必要的访问权限子类可直接使用父类的protected成员。
通俗类比protected成员就像“家里的卧室”家里人类内部、自己的孩子子类能进入外人类外部不能进入——既保护了隐私又方便家人使用。
4实战示例结合继承定义父类Person将需要被子类继承的成员设为protected定义子类Student继承Person演示子类对父类protected成员的访问#includeiostream#includestringusingnamespacestd;// 父类PersonclassPerson{// 私有成员仅父类内部可访问子类不可访问private:string m_idCard;// 身份证号核心隐私不被子类、外部访问// 保护成员父类内部、子类内部可访问外部不可访问protected:string m_name;// 姓名需要被子类继承不允许外部直接访问intm_age;// 年龄需要被子类继承不允许外部直接访问// 公有成员外部接口public:voidsetAge(intage){if(age0age
{m_ageage;}else{cout年龄输入错误endl;}}};// 子类Student继承PersonclassStudent:publicPerson{// 子类私有成员private:doublem_score;// 成绩// 子类公有成员接口public:// 子类内部可直接访问父类的protected成员m_name、m_agevoidsetStudentInfo(string name,intage,doublescore){m_namename;// 访问父类protected成员m_name正确m_ageage;// 访问父类protected成员m_age正确m_scorescore;// 尝试访问父类private成员m_idCard错误// m_idCard 110101199001011234; // 编译报错子类不可访问父类private成员}voidshowStudentInfo(){// 子类内部可直接访问父类protected成员cout姓名m_name年龄m_age成绩m_scoreendl;}};intmain(){Student stu;// 类外部尝试访问父类protected成员m_name错误// stu.m_name 张三; // 编译报错protected成员外部不可访问// 类外部通过子类public接口间接操作父类protected成员正确stu.setStudentInfo(张三,18,
95.
;stu.showStudentInfo();// 类外部通过父类public接口间接操作父类protected成员正确stu.setAge(
;stu.showStudentInfo();return0;}5关键说明protected仅在继承场景下有意义若类不涉及继承protected与private的作用完全一致类外部不可访问类内部可访问子类可直接访问父类的protected成员但不能访问父类的private成员——这是protected与private的核心区别也是继承场景中选择protected的原因即使子类继承了父类的protected成员子类的外部依然无法直接访问该成员只能通过子类的public接口间接访问保证了封装性。
三者核心对比表格
总结必背为了方便大家快速区分、记忆三种访问权限整理了核心对比表格涵盖访问范围、核心用途、使用场景一目了然笔试面试直接套用访问权限访问范围核心用途常用场景private私有✅ 类内部❌ 类外部❌ 子类内部隐藏核心细节保证数据安全绝大多数成员变量、类内部辅助函数public公有✅ 类内部✅ 类外部✅ 子类内部暴露外部接口实现类与外部交互类的外部接口成员函数protected保护✅ 类内部❌ 类外部✅ 子类内部兼顾封装与继承供子类访问需要被子类继承的成员属性/函数补充口诀快速记忆私有private藏内部公有public对外露保护protected给子类封装继承两不误。
实战技巧如何合理分配访问权限核心规范很多初学者学会了三种访问权限的定义却不知道在实际编程中如何选择——比如什么时候用private什么时候用public什么时候用protected。
其实只要遵循以下3个核心规范就能写出符合封装原则、易维护的代码。
规范1成员变量优先设为private核心封装原则无论类是否涉及继承所有核心属性成员变量都应设为private不允许外部直接访问。
外部需要操作这些变量时通过public成员函数set/get接口间接实现同时在接口中添加数据校验保证数据安全性。
示例规范写法classPhone{private:string m_brand;// 成员变量私有化doublem_price;public:// set接口修改private成员添加校验voidsetPrice(doubleprice){if(price
{m_priceprice;}else{cout价格输入错误endl;}}// get接口获取private成员不允许修改doublegetPrice(){returnm_price;}};规范2外部需要调用的函数设为public接口暴露原则所有需要被类外部、子类外部调用的功能都封装为public成员函数作为类的对外接口。
接口的设计应遵循“单一职责”——一个函数只做一件事简洁清晰避免暴露内部实现细节。
反例不规范将内部辅助函数设为public暴露内部细节classStudent{private:doublem_score;public:voidsetScore(doublescore){m_scorescore;checkScore();// 内部辅助函数}// 错误checkScore()是内部辅助函数无需外部调用不应设为publicvoidcheckScore(){if(m_score
{cout不及格endl;}}};规范3仅在继承场景下使用protected继承专用原则若类不涉及继承无需使用protected直接用private即可若类涉及继承且某个成员需要被子类直接访问无需通过父类接口则将该成员设为protected。
注意不要为了“方便子类访问”将所有父类成员都设为protected——这会破坏父类的封装性只有真正需要被子类继承、使用的成员才设为protected。
常见误区三种权限的5个高频坑必避初学者在使用访问权限时很容易因概念混淆、语法细节出错这里
总结5个最常见的坑结合错误示例和正确写法帮你少走弯路。
误区1未指定访问权限默认是public最容易踩坑错误认知很多初学者误以为类中未指定访问权限修饰符时默认是public正确认知C中类的成员未指定访问权限时默认是private。
classStudent{// 未指定访问权限默认是privatevoidshowInfo(){// private成员函数外部无法访问cout学生信息endl;}};intmain(){Student stu;stu.showInfo();// 错误showInfo()是private外部不可访问return0;}正确写法需要外部访问的成员明确指定为publicclassStudent{public:// 明确指定为publicvoidshowInfo(){cout学生信息endl;}};intmain(){Student stu;stu.showInfo();// 正确return0;}误区2子类可以访问父类的private成员错误认知子类继承了父类就可以访问父类的所有成员正确认知子类只能访问父类的public和protected成员无法访问父类的private成员。
classPerson{private:string m_name;// 父类private成员public:voidsetName(string name){m_namename;}};classStudent:publicPerson{public:voidshowName(){// 错误子类无法访问父类private成员m_namecout姓名m_nameendl;}};// 正确写法将父类m_name设为protected或通过父类public接口访问classPerson{protected:// 修改为protectedstring m_name;};classStudent:publicPerson{public:voidshowName(){cout姓名m_nameendl;// 正确}};误区3设置public成员变量方便外部访问错误做法为了方便外部修改数据将成员变量设为public破坏封装正确做法成员变量私有化通过public的set/get接口间接访问和修改添加数据校验。
// 错误写法public成员变量外部可随意修改无数据安全保障classStudent{public:doublem_score;// public成员变量};intmain(){Student stu;stu.m_score150;// 非法值无校验直接修改成功return0;}// 正确写法成员变量私有化通过接口访问classStudent{private:doublem_score;public:voidsetScore(doublescore){if(score0score
{m_scorescore;}else{cout成绩输入错误endl;}}};误区4protected成员可以被类外部访问错误认知protected成员和public成员类似类外部可以访问正确认知protected成员类外部无法访问仅能在类内部、子类内部访问。
classPerson{protected:string m_name;// protected成员};intmain(){Person p;p.m_name张三;// 错误protected成员类外部不可访问return0;}误区5访问权限修饰符影响成员函数的功能实现错误认知访问权限修饰符会改变成员函数的功能比如private成员函数无法实现复杂逻辑正确认知访问权限修饰符只限制访问范围不影响成员函数的功能实现——private、public、protected成员函数的实现逻辑完全一致只是能被访问的场景不同。
综合实战完整类设计结合三种访问权限结合本文所有知识点设计一个完整的类体系父类Animal、子类Dog合理分配三种访问权限演示访问权限的实际应用代码可直接运行测试巩固所学内容。
#includeiostream#includestringusingnamespacestd;// 父类Animal动物classAnimal{// private核心隐私仅父类内部可访问private:string m_id;// 动物编号核心隐私不暴露、不继承// protected需要被子类继承不允许外部访问protected:string m_name;// 动物名称子类需要继承intm_age;// 动物年龄子类需要继承// public外部接口供外部、子类外部访问public:// 构造函数后续讲解此处用于初始化Animal(string name,intage){m_namename;m_ageage;m_idAto_string(rand()%
;// 随机生成编号}// 接口获取动物编号间接访问private成员m_idstringgetId(){returnm_id;}// 接口显示动物基础信息父类内部访问protected、private成员voidshowBaseInfo(){cout动物编号m_id名称m_name年龄m_ageendl;}};// 子类Dog狗继承AnimalclassDog:publicAnimal{// private子类核心属性仅子类内部可访问private:string m_breed;// 狗的品种// public子类外部接口public:// 子类构造函数后续讲解Dog(string name,intage,string breed):Animal(name,age){m_breedbreed;}// 子类内部访问父类protected成员m_name、m_agevoidshowDogInfo(){cout狗的信息endl;cout名称m_name年龄m_age品种m_breedendl;cout编号getId()endl;// 子类访问父类public接口}// 子类接口修改父类protected成员m_agevoidsetDogAge(intage){if(age0age
{m_ageage;// 直接访问父类protected成员正确}else{cout狗的年龄输入错误endl;}}};intmain(){// 创建父类对象Animalcat(猫咪,
;cat.showBaseInfo();// 访问父类public接口正确cout猫咪编号cat.getId()endl;// 访问父类public接口正确// cat.m_name 小花; // 错误m_name是protected外部不可访问// 创建子类对象Dogdog(旺财,2,中华田园犬);dog.showDogInfo();// 访问子类public接口正确dog.setDogAge(
;// 访问子类public接口间接修改父类protected成员正确dog.showDogInfo();// 子类对象访问父类public接口正确dog.showBaseInfo();return0;}代码说明父类Animalm_id设为private核心隐私m_name、m_age设为protected供子类继承showBaseInfo()、getId()设为public外部接口子类Dogm_breed设为private子类核心属性showDogInfo()、setDogAge()设为public外部接口子类内部可直接访问父类的protected成员m_name、m_age类外部只能通过public接口访问成员无法直接访问private、protected成员完全遵循封装原则继承场景下protected成员的作用体现子类无需通过父类接口可直接访问父类protected成员简化子类代码。
七、
总结类的访问权限public、private、protected是C封装特性的核心也是类设计的基础其本质是“限制成员的访问范围”实现“隐藏内部细节、暴露外部接口”的目的。
掌握访问权限的关键不在于死记硬背访问范围而在于理解“封装”的思想——合理分配访问权限既能保证数据安全性又能简化代码、提高可维护性。
后续学习继承、多态时protected的作用会更加突出学好本文内容能让你在后续的类体系设计中更加得心应手。