八股文又来了, 看吧 看一遍你就会了 时间长忘了?那就再看一遍孩子
一、核心概述
private、protected、public 是 c++ 封装特性的核心载体,其本质是在编译期限制类成员(变量/函数)在不同作用域下的可见性和访问权限——简单来说,就是规定“哪些地方能使用类的某个成员,哪些地方不能”。掌握这三个关键字的规则,是写出符合工程化规范、高内聚低耦合 c++ 代码的基础。
二、基础定义 & 核心访问规则
2.1 核心规则表(必记)
| 关键字 | 官方定义 | 大白话解释 | 类内访问 | 类外访问(通过对象) | 派生类(子类)访问 |
|---|---|---|---|---|---|
public | 公有的,对外暴露的接口,无访问限制 | 谁都能访问:类自己能用、外部代码能用、子类也能用 | ✅ 允许 | ✅ 允许 | ✅ 允许 |
private | 私有的,仅类内部可见,封装的核心体现 | 只有类自己能用:外部代码不能用、子类也不能用(友元除外) | ✅ 允许 | ❌ 禁止 | ❌ 禁止 |
protected | 受保护的,介于public和private之间,为派生类预留的访问权限 | 类自己能用、子类能用,但外部代码不能用 | ✅ 允许 | ❌ 禁止 | ✅ 允许 |
2.2 默认访问控制(易错点)
- 用
class定义类时,默认访问控制是private(未写关键字的成员均为 private); - 用
struct定义类时,默认访问控制是public(c++ 对 c 的兼容设计,struct 仅默认权限与 class 不同)。
2.3 基础验证代码
#include <iostream>
using namespace std;
class myclass {
// 未写关键字,class默认private
int default_val = 0;
public:
int pub_val = 10; // 公有成员
void pub_func() {
// 类内:能访问所有成员(public/private/protected)
cout << "类内访问 private_val: " << pri_val << endl;
cout << "类内访问 protected_val: " << pro_val << endl;
cout << "类内访问 default_val: " << default_val << endl;
}
private:
int pri_val = 20; // 私有成员
void pri_func() { cout << "私有函数" << endl; }
protected:
int pro_val = 30; // 受保护成员
void pro_func() { cout << "受保护函数" << endl; }
};
// 派生类(子类)
class derived : public myclass {
public:
void derived_func() {
// 派生类:能访问基类的public/protected,不能访问private
cout << "派生类访问 pub_val: " << pub_val << endl; // ✅ 允许
cout << "派生类访问 pro_val: " << pro_val << endl; // ✅ 允许
// cout << "派生类访问 pri_val: " << pri_val << endl; // ❌ 禁止:private不可访问
pro_func(); // ✅ 允许调用protected函数
// pri_func(); // ❌ 禁止调用private函数
}
};
int main() {
myclass obj;
// 类外:仅能访问public成员
cout << "类外访问 pub_val: " << obj.pub_val << endl; // ✅ 允许
// cout << "类外访问 pro_val: " << obj.pro_val << endl; // ❌ 禁止:protected不可访问
// cout << "类外访问 pri_val: " << obj.pri_val << endl; // ❌ 禁止:private不可访问
obj.pub_func(); // ✅ 允许调用public函数
// obj.pro_func(); // ❌ 禁止调用protected函数
// obj.pri_func(); // ❌ 禁止调用private函数
derived d_obj;
d_obj.derived_func(); // ✅ 派生类内部逻辑正常执行
return 0;
}2.4 补充:同一类不同对象的访问规则
访问控制是“类级别”的限制,而非“对象级别”——同一个类的不同对象,可互相访问对方的 private/protected 成员(编译期认为属于“同一类的内部”):
#include <iostream>
using namespace std;
class myclass {
private:
int m_val = 10;
public:
// 访问同类型另一个对象的private成员
void accessanotherobj(myclass& other) {
cout << "访问另一个对象的private成员:" << other.m_val << endl; // ✅ 允许
other.m_val = 20; // ✅ 可修改
}
int getval() const { return m_val; }
};
int main() {
myclass a, b;
a.accessanotherobj(b); // 输出:访问另一个对象的private成员:10
cout << "修改后b的val:" << b.getval() << endl; // 输出:20
return 0;
}三、继承中的访问控制(面试必考)
派生类继承基类时,继承方式(public/protected/private) 会修改“基类成员在派生类中的访问权限”,核心铁律:
派生类对基类成员的访问权限 = 「基类成员自身的访问权限」和「继承方式」中更严格的那个。
3.1 继承权限影响表
| 基类成员访问权限 | public继承(最常用) | protected继承 | private继承 |
|---|---|---|---|
| public | public(不变) | protected | private |
| protected | protected(不变) | protected | private |
| private | 不可访问(不变) | 不可访问 | 不可访问 |
3.2 单层继承代码示例
#include <iostream>
using namespace std;
// 基类
class base {
public:
int pub_val = 1;
protected:
int pro_val = 2;
private:
int pri_val = 3;
};
// 1. public继承(开发中99%的场景,推荐)
class derivedpub : public base {
public:
void show() {
cout << "pub_val: " << pub_val << endl; // ✅ public → public
cout << "pro_val: " << pro_val << endl; // ✅ protected → protected
// cout << pri_val << endl; // ❌ 始终不可访问
}
};
// 2. protected继承
class derivedpro : protected base {
public:
void show() {
cout << "pub_val: " << pub_val << endl; // ✅ public → protected
cout << "pro_val: " << pro_val << endl; // ✅ protected → protected
}
};
// 3. private继承
class derivedpri : private base {
public:
void show() {
cout << "pub_val: " << pub_val << endl; // ✅ public → private
cout << "pro_val: " << pro_val << endl; // ✅ protected → private
}
};
int main() {
derivedpub d_pub;
cout << d_pub.pub_val << endl; // ✅ public继承后,基类public仍为public,类外可访问
// cout << d_pub.pro_val << endl; // ❌ protected,类外不可访问
derivedpro d_pro;
// cout << d_pro.pub_val << endl; // ❌ public继承后变为protected,类外不可访问
derivedpri d_pri;
// cout << d_pri.pub_val << endl; // ❌ public继承后变为private,类外不可访问
return 0;
}3.3 多层继承的权限链式影响
多层继承中,基类成员权限会随继承链“层层收紧”(仅会更严格,不会放宽):
#include <iostream>
using namespace std;
// 顶层基类
class a {
public:
int pub_a = 1;
protected:
int pro_a = 2;
};
// 第二层:b public继承a
class b : public a {
public:
void showb() {
cout << "b中访问a的pub_a:" << pub_a << endl; // ✅ public→public
cout << "b中访问a的pro_a:" << pro_a << endl; // ✅ protected→protected
}
};
// 第三层:c protected继承b
class c : protected b {
public:
void showc() {
// a的pub_a:a→b(public)→c(protected)→ 最终c中是protected
cout << "c中访问a的pub_a:" << pub_a << endl; // ✅ 允许(c内可访问protected)
// a的pro_a:a→b(protected)→c(protected)→ 最终c中是protected
cout << "c中访问a的pro_a:" << pro_a << endl; // ✅ 允许
}
};
// 第四层:d private继承c
class d : private c {
public:
void showd() {
// a的pub_a:经过三层继承后变为private
cout << "d中访问a的pub_a:" << pub_a << endl; // ✅ d内可访问private
}
};
int main() {
c c;
// cout << c.pub_a << endl; // ❌ c中pub_a是protected,类外不可访问
d d;
// cout << d.pub_a << endl; // ❌ d中pub_a是private,类外不可访问
return 0;
}四、特殊场景的访问控制细节
4.1 静态成员的访问控制
static 仅改变成员的“存储方式”(属于类而非对象),访问控制规则与普通成员完全一致:
#include <iostream>
using namespace std;
class myclass {
private:
static int s_pri_val; // 静态私有成员
protected:
static int s_pro_val; // 静态受保护成员
public:
static int s_pub_val; // 静态公有成员
static void showstatic() {
// 类内可访问所有静态成员
cout << "静态私有:" << s_pri_val << endl;
cout << "静态受保护:" << s_pro_val << endl;
}
};
// 静态成员必须类外初始化
int myclass::s_pri_val = 10;
int myclass::s_pro_val = 20;
int myclass::s_pub_val = 30;
// 派生类
class derived : public myclass {
public:
static void showderivedstatic() {
// 派生类可访问基类的static protected/public
cout << "派生类访问静态受保护:" << s_pro_val << endl; // ✅
cout << "派生类访问静态公有:" << s_pub_val << endl; // ✅
// cout << s_pri_val << endl; // ❌ 静态私有仍不可访问
}
};
int main() {
// 类外仅能访问静态公有成员
cout << myclass::s_pub_val << endl; // ✅ 30
// cout << myclass::s_pro_val << endl; // ❌ 静态受保护,类外不可访问
// cout << myclass::s_pri_val << endl; // ❌ 静态私有,类外不可访问
derived::showderivedstatic(); // ✅ 派生类内可访问静态受保护
return 0;
}4.2 protected 的深层细节(易踩坑)
protected 允许“子类访问自己的基类成员”,但禁止子类访问其他子类对象的基类 protected 成员:
#include <iostream>
using namespace std;
class base {
protected:
int m_val = 10;
};
class deriveda : public base {
public:
// 错误场景:deriveda试图访问derivedb对象的protected成员
void accessderivedb(derivedb& b) {
// cout << b.m_val << endl; // ❌ 禁止:不能访问其他子类对象的protected
}
// 正确场景:访问自己的protected成员
void accessself() {
cout << m_val << endl; // ✅ 允许
}
};
class derivedb : public base {}; // 另一个子类
int main() {
deriveda a;
derivedb b;
a.accessself(); // ✅ 输出10
// a.accessderivedb(b); // ❌ 编译报错
return 0;
}五、友元:突破访问控制的唯一机制
friend 是 c++ 中唯一能突破 private/protected 访问限制的语法,核心是“显式授权”——只有被类主动声明为友元的函数/类,才能访问其私有/受保护成员。友元按授权粒度从粗到细分为以下三种形式:
5.1 友元函数(单个函数授权)
友元函数是最基础的友元形式,授权单个普通函数访问类的私有/受保护成员,适用于“仅需一个外部函数访问类内部数据”的场景(如运算符重载、简单的外部工具函数)。
#include <iostream>
using namespace std;
class myclass {
private:
int pri_val = 100;
// 声明友元函数:仅授权showprivate函数访问当前类的私有成员
friend void showprivate(myclass& obj);
};
// 友元函数:可以直接访问myclass的private成员(无需通过public接口)
void showprivate(myclass& obj) {
cout << "访问private成员: " << obj.pri_val << endl; // ✅ 允许
}
int main() {
myclass obj;
showprivate(obj); // 输出:访问private成员: 100
return 0;
}核心总结:
- 授权粒度:仅单个函数,影响范围最小;
- 声明位置:友元函数的声明可写在类的
public/private/protected任意区域,效果完全一致; - 核心优势:无需为单个函数开放过多权限,兼顾封装性和实用性。
5.2 友元类(批量授权,整个类的所有成员函数)
友元类是“批量授权”形式,授权另一个类的所有成员函数(无论 public/private/protected)访问当前类的私有/受保护成员,适用于“两个类高度耦合且需要深度协作”的场景(如容器类和其迭代器类)。
#include <iostream>
using namespace std;
class a {
// 声明b为友元类:b的所有成员函数都能访问a的私有/受保护成员
friend class b;
private:
int m_val = 100;
};
class b {
public:
// b的公有成员函数:访问a的私有成员
void accessa(a& a) {
cout << a.m_val << endl; // ✅ 允许
}
private:
// b的私有成员函数:同样能访问a的私有成员(友元类的所有成员函数都有权限)
void privateaccessa(a& a) {
a.m_val = 200; // ✅ 允许修改
}
};
int main() {
a a;
b b;
b.accessa(a); // 输出:100
return 0;
}核心总结:
- 授权粒度:整个类的所有成员函数,授权范围最广;
- 风险提示:友元类会大幅降低封装性,仅在确有必要时使用(优先考虑友元函数/友元成员函数);
- 访问规则:友元类的私有成员函数也能访问授权类的私有成员,权限无差别。
5.3 友元成员函数(精细授权,仅单个成员函数)
友元成员函数是“精准授权”形式,仅授权另一个类的某个特定成员函数访问当前类的私有/受保护成员,是兼顾“协作需求”和“封装性”的最优友元形式。
#include <iostream>
using namespace std;
// 前置声明:必须先声明a,才能在b的成员函数参数中使用a的引用
class a;
class b {
public:
// 先声明需要授权的成员函数(仅该函数需要访问a的私有成员)
void onlythisfunccanaccessa(a& a);
};
class a {
// 仅授权b类的onlythisfunccanaccessa成员函数访问当前类的私有成员
friend void b::onlythisfunccanaccessa(a& a);
private:
int m_val = 500;
};
// 实现授权的成员函数:可直接访问a的私有成员
void b::onlythisfunccanaccessa(a& a) {
cout << a.m_val << endl; // ✅ 允许
}
int main() {
a a;
b b;
b.onlythisfunccanaccessa(a); // 输出:500
return 0;
}核心总结:
- 授权粒度:仅单个成员函数,是最精细的友元授权方式;
- 前置声明:必须先声明被授权的类(如a),再声明其成员函数,否则编译器无法识别;
- 最佳实践:工程开发中优先使用友元成员函数,最小化破坏封装性。
5.4 友元的核心特性(所有友元形式通用)
友元关系的核心规则不受授权形式影响,需牢记以下三点:
- 单向性:若a声明b为友元,仅b能访问a的私有成员,a不能访问b的私有成员(双向授权需双方互相声明);
- 不传递性:若a授权b,b授权c,c不能访问a的私有成员(友元关系无法链式传递);
- 不继承性:若a授权b,c继承b,c不能访问a的私有成员(友元关系无法被子类继承)。
六、实际开发中的应用场景 & 反模式
6.1 核心应用场景(最佳实践)
| 关键字 | 核心应用场景 | 示例 |
|---|---|---|
public | 对外提供的「接口」(稳定、不轻易修改) | 业务方法(如 calculate())、get/set函数(如 getname()/setname()) |
private | 类的「内部实现细节」(隐藏,避免外部篡改) | 成员变量(如 m_name)、内部辅助函数(如 checkvalid()) |
protected | 基类中需要给「派生类复用」,但不希望外部访问的成员 | 基类的通用属性(如 m_id)、通用方法(如 initdata()) |
6.2 常见反模式(严禁使用)
反模式1:成员变量设为 public
// 错误
class student {
public:
int m_age; // 外部可直接赋值 m_age = -5,数据完全不安全
};
// 正确
class student {
private:
int m_age;
public:
void setage(int age) {
if (age >= 0 && age <= 120) m_age = age; // 带校验的写接口
}
int getage() const { return m_age; } // 只读接口
};反模式2:为方便将 protected 改为 public
// 错误
class base {
public:
int m_id; // 本应给子类复用的成员,被外部随意修改
};
// 正确
class base {
protected:
int m_id;
public:
int getid() const { return m_id; } // 仅暴露只读接口
};反模式3:过度使用友元,破坏封装
// 错误:授权多个类/函数,封装形同虚设
class a {
friend class b;
friend class c;
friend void func1();
friend void func2();
private:
int m_data;
};
// 正确:仅授权必要的函数/类
class a {
friend void onlynecessaryfunc(a& a);
private:
int m_data;
};七、常见误区 & 避坑点
- 误区1:
protected是“派生类的 private”→ 派生类之间不能访问对方的protected成员; - 误区2:继承方式会修改基类自身的成员权限 → 仅修改“基类成员在派生类中的权限”,基类自身权限不变;
- 误区3:
static会改变访问控制 →static private仍仅类内可访问,static不影响权限; - 误区4:访问控制针对“对象”→ 同一类的不同对象可互相访问 private 成员(类级别限制);
- 误区5:
private绝对不可访问 → 友元是合法途径,指针强制转换是不规范黑科技,严禁使用; - 误区6:
struct没有访问控制 →struct只是默认权限为 public,同样支持private/protected。
八、面试高频考点 & 标准答案
8.1 基础考点
- 问:
class和struct的访问控制默认值有什么区别?
答:class默认访问控制是private,struct默认是public(c++ 对 c 的兼容设计,struct 保留 c 的特性); - 问:
protected和private的核心区别是什么?
答:核心区别在派生类的访问权限 ——protected允许派生类访问,private不允许;类外两者都不可访问; - 问:public 继承和 private 继承的核心区别?
答:public 继承保留基类public成员的公有属性(类外可访问),private 继承会把基类所有可访问成员变为 private(仅派生类内可访问);开发中优先用 public 继承; - 问:友元是否受访问控制限制?
答:不受,友元函数/友元类可直接访问类的private/protected成员,是访问控制的合法例外。
8.2 进阶考点(结合多态)
问:为什么私有虚函数能被派生类重写?
#include <iostream>
using namespace std;
class base {
private:
virtual void func() { cout << "base::func" << endl; }
public:
void callfunc() { func(); } // 公有接口调用私有虚函数
};
class derived : public base {
private:
void func() override { cout << "derived::func" << endl; }
};
int main() {
base* ptr = new derived();
ptr->callfunc(); // 输出:derived::func
delete ptr;
return 0;
}答:访问控制(private)和多态(虚函数)是两个独立的机制:
- 访问控制限制“谁能直接调用函数”;
- 虚函数重写限制“函数是否能被覆盖”;
派生类重写私有虚函数后,基类的公有接口仍可调用重写后的版本,这是 c++ 允许的合法设计。
九、核心总结
- 访问控制的本质是控制类成员的可见性,是编译期语法检查,核心为封装性服务;
- 基础规则:
public(类内/外/派生类)、private(仅类内+友元)、protected(类内+派生类); - 继承中:派生类对基类成员的权限 = 「基类成员权限」和「继承方式」中更严格的那个,private 成员始终不可访问;
- 特殊细节:同一类的不同对象可互相访问 private 成员,
protected仅允许子类访问自身的基类成员; - 友元是唯一突破访问控制的机制,遵循“单向、不传递、不继承”,优先用“友元成员函数”最小化授权;
- 开发最佳实践:
public做接口、private藏实现、protected给子类复用,严禁 public 成员变量和过度友元。
到此这篇关于c++ private、protected、public从入门到精通实例解析的文章就介绍到这了,更多相关c++ private、protected、public内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论