C++ friend类怎么定义 C++友元类访问私有成员示例【关系】
技术百科
穿越時空
发布时间:2026-01-27
浏览: 次 友元类是单向授权,A声明B为friend后B可访问A的私有成员,但A不能访问B的;声明必须在A类定义体内,用friend class B;形式,前置声明class B;即可;友元关系不可继承,且不授予对private构造/析构函数的调用权。
friend 类声明必须写在类内部
友元类不是“互相友好”,而是单向授权:A 声明 B 为 friend,B 就能访问 A 的私有成员,但 A 不能因此访问 B 的私有成员。声明位置很关键——friend 关键字必须出现在被访问类(比如 ClassA)的定义体内,且通常放在 private 或 protected 区域里,但它本身不改变访问级别。
常见错误是把 friend class B; 写在类外面,或者写成 friend B;(漏掉 class 关键字,C++ 要求显式写出类型类别)。
-
friend class B;是合法声明;friend B;编译报错:'B' does not name a type - 前置声明足够:如果
B在A定义前未完全定义,只需在A上方加class B;即可 - 友元关系不可继承:即使
B是A的 friend,B的派生类也不能访问A的私有成员
友元类能直接访问 private 成员,无需 public 接口
一旦声明成立,友元类的**任何成员函数**(包括构造函数、静态函数、const 成员函数)都能像访问自己成员一样读写目标类的 private 和 protected 成员,不需要 getter/setter,也不受封装限制。
示例中,class Printer 是 class Data 的 friend,那么 Printer::print() 可以直接写 d.m_value(假设 m_value 是 Data 的 private 成员),而普通函数或非友元类只能通过 Data 提供的 public 方法间接访问。
- 友元函数同理,但只限于那个函数;友元类则授权整个类的所有成员函数
- 注意:友元不授予对
private构造函数/析构函数的调用权——除非你显式在友元类中调用,且该构造函数可访问(例如不是 delete 或 private 继承导致不可见) - 若
Data的私有成员是另一个类的 private 成员(如嵌套类),友元关系不自动穿透;Printer仍不能直接访问Data::Inner::m_x,除非Inner也声明Printer为 friend
友元破坏封装,但适合紧密耦合场景
典型适用场景是两个类逻辑上高度内聚,比如容器与迭代器(std::vector 和它的 iterator)、Pimpl 惯用法中的接口类与实现类、或序列化辅助类。这时候强行加 public 接口反而暴露不该暴露的细节,或引入不必要的性能开销(比如拷贝返回值)。
- 不要为图省事给业务逻辑类随便加 friend——它会让类的契约变得模糊,增加维护成本
- 友元声明不会影响二进制兼容性,但会扩大 ABI 影响范围:修改
Data的私有成员布局,可能迫使所有 friend 类重新编译 - 模板类做友元时需谨慎:友元可以是具体特化(
friend class Helper),也可以是全特化模板(; template),后者权限更大,也更难追踪friend class Helper;
编译器不检查友元访问是否合理
C++ 编译器只验证语法和可见性,不管语义是否“合理”。比如 Printer 是 Data 的 friend,它就能在 print() 里把 d.m_value = 42;,哪怕这违反了 Data 的不变量(invariant)。这种越权修改容易埋下 bug,调试时也难以定位——因为错误发生在友元类里,而问题根源其实在被访问类的约束被绕过。
- 没有运行时检查,也没有 warning 提示“你在滥用 friend”
- 单元测试要覆盖友元类对私有状态的修改路径,否则很容易漏掉非法状态
- Clang-Tidy 有
cppcoreguidelines-non-private-member-variables-in-classes等规则,但对
friend 访问无能为力;静态分析工具很难补这个缺口
真正麻烦的不是怎么写 friend,而是后续谁来保证 Printer 不会悄悄改坏 Data 的内部一致性——这得靠设计约定、代码审查和测试覆盖,编译器帮不上忙。
# 放在
# 就能
# 很难
# 你在
# 出现在
# 不需要
# 只需
# 写在
# 特化
# public
# 工具
# c++
# int
# class
# 构造函数
# 接口
# private
# delete
# bug
# 体内
# 封装
# 成员函数
# 析构函数
# 继承
# const
# protected
# print
相关栏目:
<?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大文件处理策略_内存优化说明【指导】
- Python性能剖析高级教程_cProfileLi
- Win11怎么更改默认打开方式_Win11关联文件
- 如何在 Go 中正确测试带 Cookie 的 HT
- php485在php5.6下能用吗_php485旧
- 网站内页做seo排名怎么做?
- Win11怎么更改管理员名字 Win11修改账户名
- 如何用列表一次性对 DataFrame 的指定列应
- 一文教你快速开通网站LOGO图
- Win11麦克风没声音怎么设置_Win11麦克风权
- c++中explicit(bool)的用法 c++
- Mac怎么给文件夹加密_Mac创建加密磁盘映像教程
- Python字符串操作教程_切片拼接与格式化详解
- Python函数接口稳定性_版本演进解析【指导】
- Ajax提交表单PHP怎么接收_处理Ajax发送的
- Win11怎么查看已连接wifi密码 Win11查
- php控制舵机角度怎么调_php发送pwm信号控制
- 如何在Golang中捕获结构体方法错误_Golan
- Python爬虫项目实战教程_Scrapy抓取与存
- LINUX如何查看文件类型_Linux中file命
- Win11如何连接Xbox手柄 Win11蓝牙连接
- c++的static关键字有什么用 静态变量和静态
- 如何优化Golang程序CPU性能_Golang
- 如何使用Golang操作指针变量_Golang解引
- Python多进程教程_multiprocessi
- Win11怎么开启远程桌面连接_Windows11
- 当网站SEO排名下降时,如何应对?
- Win11怎么设置默认浏览器Chrome_Wind
- c++如何使用std::bitset进行位图算法_
- MAC怎么在照片中添加水印_MAC自带编辑工具文字
- Win11怎么关闭OneDrive同步_Win11
- php中常量能用::访问吗_类常量与作用域操作符使
- XML的“混合内容”是什么 怎么用DTD或XSD定
- php在Linux怎么部署_LNMP环境搭建PHP
- Linux如何使用Curl发送请求_Linux下A
- 如何使用Golang指针与接口结合_实现方法调用和
- php怎么连接数据库_MySQL数据库连接的基础代
- PHP 中如何在函数内持久修改引用变量所指向的目标
- Windows怎样关闭Edge新标签页广告_Win
- WindowsUSB驱动安装异常怎么办_USB驱动
- Windows 11如何开启文件夹加密(EFS)_
- c++协程和线程的区别 c++异步编程模型对比【核
- mac怎么安装adb_MAC配置Android A
- Win10如何卸载WindowsDefender_
- Python装饰器设计思路_功能增强机制说明【指导
- Windows服务无法启动错误1067是什么_进程
- 如何在 Go 中正确反序列化 XML 多节点数组(
- Win11 C盘满了怎么清理 Win11磁盘清理和
- Win11玩游戏全屏闪退怎么办_Win11全屏优化
- Go语言中slice追加操作的底层共享机制详解


QQ客服