C++ 友元类有什么用 C++ friend关键字破坏封装性分析【概念】
技术百科
穿越時空
发布时间:2026-01-27
浏览: 次 友元类仅能访问被明确声明为友元的类的私有成员,不支持继承与传递;典型场景包括容器与迭代器、工厂与构造函数;滥用会破坏封装性并引发依赖问题。
友元类能访问私有成员,但仅限于明确声明的类
友元类不是“开放全部权限”,而是由被访问类主动授予特定访问权。比如 A 类声明 friend class B;,那只有 B 的成员函数(或友元函数)能在定义中直接访问 A 的 private 和 protected 成员——其他类哪怕继承自 B 也不自动获得该权限。
常见误用是以为友元关系可传递或继承:B 是 A 的友元,C 继承 B,不代表 C 能访问 A 的私有成员。C++ 明确不支持友元关系的继承与传递。
典型使用场景:容器与迭代器、工厂与对象构造
真正需要友元的地方,往往绕不开数据布局与接口隔离的权衡。比如标准库中 std::vector 和它的迭代器类(如 __normal_iterator)之间就存在友元关系,让迭代器能直接读取底层 _M_start、_M_finish 指针,而不暴露这些字段给所有用户。
- 容器类把迭代器声明为友元,避免为每个内部指针加 public getter(性能损耗 + 接口污染)
- 工厂类作为友元,可直接调用目标类的 private 构造函数,实现受控实例化(如单例、对象池)
- 两个紧密耦合的类(如
Node和Graph),若频
繁互相访问内部状态,又不想把逻辑全塞进一个类,友元比一堆 set/get 更轻量
friend 破坏封装性的实际影响被高估了
封装性受损的前提是“本不该知道的被知道了”。但友元不是全局开放,而是点对点授权。真正的问题往往出在设计阶段:是否真的需要这种耦合?有没有更松的替代方案?
容易踩的坑包括:
- 把友元当快捷方式滥用:本可用
public成员函数完成的操作,却用友元直接读写字段,导致后续字段重构时大量友元代码要同步改 - 友元类本身成了上帝类:一个类被十几个其他类声明为友元,说明它可能承担了太多职责,应考虑拆分
- 头文件依赖爆炸:友元声明要求友元类名可见,常迫使你在头文件里提前声明甚至包含对方头文件,增加编译依赖
替代 friend 的常见做法及其代价
不是所有“想访问私有成员”的需求都该用友元。更可控的方式包括:
- 提供精简的
public接口,比如A::get_raw_data()返回const std::vector,而非暴露原始指针& - 用
protected+ 继承:如果逻辑天然属于类族,让子类访问比让无关类访问更合理 - 借助 Pimpl 惯用法:把实现细节藏在指针后,接口类只暴露必要函数,连友元都不需要
- 使用
friend函数而非整个类:粒度更细,比如只把operator 声明为友元,而不是整个std::ostream类
友元本身不危险,危险的是跳过设计思考直接写 friend class XXX;。它解决的是“谁可以访问”,而真正的难点永远在“为什么需要这个访问”。
# 的是
# 也不
# 太多
# 成了
# 都不
# 而非
# 迭代
# 不支持
# public
# 对象
# 堆
# c++
# class
# 标准库
# 指针
# stream
# 子类
# 构造函数
# 接口
# 重构
# 为什么
# private
# node
# operator
# 封装
# 成员函数
# 头文件
# 继承
# const
# protected
# 封装性
# 用友
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- 如何在 Go 中比较自定义的数组类型(如 [20]
- PHP怎么接收URL中的锚点参数_获取#后面参数值
- 如何使用Golang实现路由分组管理_Golang
- php删除数据怎么清空表_truncate与del
- Python代码测试策略_质量保障解析【教程】
- php485在php5.6下能用吗_php485旧
- 如何在Golang中实现服务熔断与限流_Golan
- C++如何解析JSON数据?(nlohmann/j
- 如何使用Golang管理跨项目依赖_Golang多
- Go语言中CookieJar的持久化机制解析:内存
- Windows蓝屏错误0x0000002C怎么解决
- Python函数接口文档化_自动化说明【指导】
- Windows Defender扫描失败怎么办_安
- Mac如何将HEIC图片格式转为JPG_Mac批量
- 如何高效删除 NumPy 二维数组中所有元素相同的
- Go语言中正确反序列化多个同级XML元素为结构体切
- Win11文件扩展名怎么显示 Win11查看文件后
- Linux如何挂载新硬盘_Linux磁盘分区格式化
- windows系统如何安装cab更新补丁_wind
- Mac怎么设置鼠标滚动速度_Mac鼠标设置详细参数
- Win11怎么更改计算机名_Windows11系统
- php串口通信波特率怎么选_根据硬件手册设置正确波
- PythonPandas数据分析项目教程_时间序列
- Windows资源管理器总是卡顿或重启怎么办?(修
- C++如何使用Qt创建第一个GUI窗口?(入门教程
- LINUX怎么进行文本内容搜索_Linux gre
- 如何使用Golang包导出规则_控制函数和变量可见
- Python对象比较与排序_魔术方法解析【教程】
- Windows如何设置登录时的欢迎屏幕背景?(锁屏
- Win10怎么创建桌面快捷方式 Win10为应用创
- Go 中的 := 运算符:类型推导机制与使用边界详
- 如何用::实现工具类方法调用_php静态工具类设计
- c++中的可变参数模板(variadic temp
- Python列表推导式与字典推导式教程_简化代码高
- 如何在Golang中捕获HTTP服务器错误_Gol
- Win11无法识别耳机怎么办_解决Win11插耳机
- Mac如何修复应用程序权限问题_Mac磁盘工具修复
- 如何在包含多值的列中精准搜索指定演员?
- Windows笔记本无法进入睡眠模式怎么办?(电源
- 用Python构建微服务架构实践_FastAPI与
- 获取 PHP 文件最后修改时间的正确方法
- 如何使用Golang开发简单的聊天室消息存储_Go
- Win11搜索不到蓝牙耳机怎么办 Win11蓝牙驱
- Win11怎么关闭OneDrive同步_Win11
- Win10怎么卸载迅雷_Win10彻底卸载迅雷方法
- php报错怎么查看_定位PHP致命错误与警告的方法
- Win11怎么设置鼠标宏_Win11鼠标按键自定义
- Win11怎么关闭搜索历史_Win11清除设备上的
- Win11怎么开启自动HDR画质_Windows1
- 如何在Golang中使用内置函数_Golangle


QQ客服