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; ?>
】
相关推荐
- Python与Docker容器化部署实战_镜像构建
- Win11怎么开启HDR模式_Windows 11
- Win11怎么设置指纹解锁 Win11笔记本录入指
- Win11怎么关闭用户账户控制UAC_Window
- Win10怎样卸载iTunes_Win10卸载iT
- Python项目回滚策略_发布安全说明【指导】
- Windows蓝屏错误0x00000018怎么处理
- Python网络异常模拟_测试说明【指导】
- Python多线程使用规范_线程安全解析【教程】
- Python解释执行模型_字节码流程说明【指导】
- Windows10电脑怎么连接蓝牙设备_Win10
- Windows10怎样连接蓝牙设备_Windows
- Win11怎么关闭定位服务_保护Win11位置隐私
- 如何使用Golang反射创建map对象_动态生成键
- Win11怎么设置右键刷新选项_Windows11
- php8.4新语法match怎么用_php8.4m
- Win11怎么开启智能存储_Windows11存储
- Windows 10怎么把任务栏放在屏幕上方_Wi
- 如何在 Go 后端安全获取并验证前端存储的 JWT
- Windows 10怎么录屏_Windows 10
- Windows驱动无法加载错误解决方法_驱动签名验
- WindowsUSB驱动安装异常怎么办_USB驱动
- Win11任务栏怎么固定应用 Win11将软件图标
- Win11怎么压缩文件 Win11自带压缩解压功能
- Win11怎么设置屏保时间_调整Win11屏幕保护
- Windows10怎么用“讲述人”读屏辅助 Win
- LINUX怎么查看进程_LINUX ps命令查看运
- Windows 11如何查看系统激活密钥_Wind
- VSC怎么配置PHP的Xdebug_远程调试设置步
- 如何使用Golang实现微服务事件驱动_使用消息总
- Windows如何使用注册表查找和删除项?(reg
- windows如何禁用驱动程序强制签名_windo
- 跨文件调用类方法怎么用_php作用域操作符与自动加
- PowerShell怎么创建复杂的XML结构
- Windows10如何重置此电脑_Windows1
- Python对象比较与排序_魔术方法解析【教程】
- Win10电脑C盘红了怎么清理_Windows10
- php下载安装后swoole扩展怎么安装_异步框架
- mac怎么打开终端_MAC终端Terminal使用
- Python文本编码与解码_跨平台解析说明【指导】
- Win11怎么关闭键盘按键音_Win11禁用打字声
- XML的“混合内容”是什么 怎么用DTD或XSD定
- Windows怎样拦截WPS弹窗广告_Window
- php条件判断怎么写_ifelse和switchc
- php打包exe后无法写入文件_权限问题解决方法【
- 如何关闭Win10自动更新更新_Win10系统自动
- C#如何序列化对象为XML XmlSerializ
- Windows怎样关闭开始菜单广告_Windows
- Win11应用商店下载慢怎么办 Win11更改DN
- Python随机数生成_random模块说明【指导


QQ客服