C++中友元(friend)高级应用和使用示例

下面列出几个 高级友元应用场景 与典型设计模式,并配以示例,帮助大家在实际项目中灵活运用 friend 机制。


1. ADL 友元注入(“注入式友元”)

场景:为某个类型定义非成员操作符(如算术、流插入等),希望通过 Argument-Dependent Lookup(ADL)让调用者只需 #include 类型的头文件即可生效。
做法:在类内部直接定义 friend 函数,而不在命名空间外重复声明。

namespace math {// 将 operator* 注入到 math 命名空间,ADL 可自动发现
class Vec {double x,y;
public:Vec(double _x, double _y): x(_x), y(_y) {}// 注入式友元friend Vec operator*(double s, const Vec& v) {return Vec(s * v.x, s * v.y);}friend Vec operator*(const Vec& v, double s) {return Vec(s * v.x, s * v.y);}friend std::ostream& operator<<(std::ostream& os, const Vec& v) {return os << '(' << v.x << ',' << v.y << ')';}
};} // namespace math// 使用方只需 #include "vec.hpp"
int main() {using namespace math;Vec v(1,2);std::cout << 3 * v + v * 4 << "\n";  // ADL 自动找到 operator*
}

2. Pimpl(编译期隐藏实现细节)

场景:使用 Pimpl(Pointer to Implementation)模式,将实现细节完全隐藏于 .cpp,降低编译依赖。
做法:让 Impl 类成为接口类的友元,直接操作私有指针与成员。

// widget.hpp
class Widget {
public:Widget();~Widget();void draw();
private:struct Impl;Impl* pImpl;friend struct Impl;  // Impl 可直接访问 pImpl
};// widget.cpp
struct Widget::Impl {int w,h;void drawImpl() { /* 绘制逻辑 */ }
};Widget::Widget(): pImpl(new Impl{640,480}) {}
Widget::~Widget(){ delete pImpl; }
void Widget::draw() { pImpl->drawImpl(); }

3. CRTP 与静态多态“挂钩点”

场景:在基类中提供默认行为,同时允许派生类定制实现(类似“模板回调”)。
做法:通过 friend 授权基类模板访问派生类私有成员。

template<typename Derived>
class Serializer {
public:std::string serialize() const {// 访问 Derived::toString()return static_cast<const Derived*>(this)->toString();}
};class Person : public Serializer<Person> {std::string name;int age;// 授权 Serializer<Person> 访问私有成员friend class Serializer<Person>;std::string toString() const {return name + ":" + std::to_string(age);}
public:Person(std::string n,int a): name(std::move(n)), age(a) {}
};

4. 单元测试访问私有成员

场景:在不破坏封装的前提下,让测试框架访问类私有、受保护成员。
做法:在被测类内声明测试类/测试函数为友元。

class MyClass {
private:int secret();friend class MyClassTest;   // GoogleTest 测试套件
};int MyClass::secret() { return 42; }// MyClass_test.cpp
#include "MyClass.hpp"
#include <gtest/gtest.h>
class MyClassTest : public ::testing::Test { /* … */ };TEST_F(MyClassTest, Secret) {MyClass obj;EXPECT_EQ(obj.secret(), 42);  // 直接访问私有函数
}

5. 表达式模板与延迟求值

场景:在数值计算库(如 Eigen、Blaze)中,构建 AST 节点并延迟计算,避免中间拷贝。
做法:各运算节点之间用 friend 提供访问内部节点接口。

template<typename L, typename R>
struct AddExpr {const L& l; const R& r;AddExpr(const L& a,const R& b):l(a),r(b){}double eval(size_t i) const { return l.eval(i) + r.eval(i); }
};class Vec {std::vector<double> data;
public:double eval(size_t i) const { return data[i]; }template<typename R>friend AddExpr<Vec, R> operator+(const Vec& a, const R& b) {return {a,b};}// … 其他运算
};

6. SFINAE/Tag-Dispatch 友元函数

场景:根据类型特征启用/禁用不同版本的非成员函数。
做法:在类内部声明模板友元,并结合 std::enable_if

#include <type_traits>
class Container {// 只有当 T 为可迭代类型时,才注入该友元template<typename T,typename = std::enable_if_t<std::is_same<decltype(std::declval<T>().begin()), typename T::iterator>::value>>friend void process(const T& c) {for (auto& x : c) { /* … */ }}
};

7. C++20 模块与友元

场景:在模块(module; export module M;)内部,想让某些实现隐藏于模块界面之外,却又可被同模块其它单元访问。
做法:使用 friend 将模块中的“私有接口”类/函数授权给模块外可见的类型。

// M.ixx (模块接口)
export module M;
export class PublicAPI {void foo();
};// M.cppm (模块实现)
module M;
class Helper { /* … */ };
friend class Helper;  // 仅在同模块实现单元可访问
void PublicAPI::foo() { /* Helper 可访问 PublicAPI 私有 */ }

8. 好友网络与访问轮廓

在复杂系统中,不同子系统之间有时需要部分越权访问,此时可定义“访问轮廓”接口(Access)类,将细粒度权限集中管理:

// access.hpp
class SubsysA;
class SubsysB;
class Access {
private:friend class SubsysA;  friend class SubsysB;static void grant(A& a, B& b) { /* … */ }
};// subsys_a.hpp
#include "access.hpp"
class SubsysA {void doA(A& a, B& b) {Access::grant(a,b);}
};// subsys_b.hpp 同理

9. 注意事项回顾

  1. 最小授权:只授权必要的函数/类,避免“一放就放大”;
  2. 文档化:在头文件注释中说明友元缘由,提醒维护者;
  3. 版本演进:内部私有成员改动时,及时修正友元函数签名;
  4. 可替代方案:能用公有接口或策略模式解决时,优先考虑更松耦合的方式。

通过以上高级用例,可以在 域内注入操作符隐藏实现细节构建高性能表达式模板精细化测试访问模块内私有互操作 等多种场景下,灵活运用 friend 关键字,既保留封装带来的优势,又实现必要的跨域访问。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/bicheng/84242.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TCP相关问题 第一篇

TCP相关问题1 1.TCP主动断开连接方为什么需要等待2MSL 如上图所示:在被动链接方调用close&#xff0c;发送FIN时进入LAST_ACK状态&#xff0c;但未收到主动连接方的ack确认&#xff0c;需要被动连接方重新发送一个FIN&#xff0c;而为什么是2MSL&#xff0c;一般认为丢失ack在…

STM32启动文件学习(startup_stm32f40xx.s)

原代码 ;******************** (C) COPYRIGHT 2016 STMicroelectronics ******************** ;* File Name : startup_stm32f40xx.s ;* Author : MCD Application Team ;* version : V1.8.0 ;* date : 09-November-2016 ;* Desc…

uni-app学习笔记二十三--交互反馈showToast用法

showToast部分文档位于uniapp官网-->API-->界面&#xff1a;uni.showToast(OBJECT) | uni-app官网 uni.showToast(OBJECT) 用于显示消息提示框 OBJECT参数说明 参数类型必填说明平台差异说明titleString是提示的内容&#xff0c;长度与 icon 取值有关。iconString否图…

【Ragflow】26.RagflowPlus(v0.4.0):完善解析逻辑/文档撰写模式全新升级

概述 在历经半个月的间歇性开发后&#xff0c;RagflowPlus再次迎来一轮升级&#xff0c;正式发布v0.4.0。 开源地址&#xff1a;https://github.com/zstar1003/ragflow-plus 更新方法 下载仓库最新代码&#xff1a; git clone https://github.com/zstar1003/ragflow-plus.…

【论文解读】Toolformer: 语言模型自学使用工具

1st author: ‪Timo Schick‬ - ‪Google Scholar‬ paper: Toolformer: Language Models Can Teach Themselves to Use Tools | OpenReview NeurIPS 2023 oral code: lucidrains/toolformer-pytorch: Implementation of Toolformer, Language Models That Can Use Tools, by…

Spring 官方推荐构造函数注入

1. 依赖关系明确 构造函数注入可以清晰地声明类的依赖关系&#xff0c;所有必需的依赖项都通过构造函数参数传递&#xff0c;使得代码的可读性更高。这种方式让类的使用者能够直观地了解类的依赖&#xff0c;而不需要通过注解或反射来猜测。 2. 增强代码健壮性 构造函数注入…

[深度学习]搭建开发平台及Tensor基础

一、实验目的 1. 掌握Windows下PyTorch 深度学习环境的配置 2. 掌握一种PyTorch开发工具 3. 理解张量并掌握Tensor的常用操作&#xff08;创建、调整形状、加、减、乘、除、取绝对值、比较操作、数理统计操作 4. 掌握Tensor与Numpy的互相转换操作 5. 掌握Tensor 的降维和…

【Zephyr 系列 14】使用 MCUboot 实现 BLE OTA 升级机制:构建安全可靠的固件分发系统

🧠关键词:Zephyr、MCUboot、OTA 升级、BLE DFU、双分区、Bootloader、安全固件管理 📌面向读者:希望基于 Zephyr 为 BLE 设备加入安全 OTA 升级功能的开发者 📊预计字数:5200+ 字 🧭 前言:为什么你需要 OTA? 随着设备部署数量增多与产品生命周期延长,远程升级(…

App Search 和 Workplace Search 独立产品现已弃用

作者&#xff1a;来自 Elastic The Search Product Team App Search 和 Workplace Search 的核心功能已集成到 Elasticsearch 和 Kibana 中。 我们宣布在 9.0 版本中弃用 App Search 和 Workplace Search。 如果你是 Elastic 的客户&#xff0c;当前正在使用 App Search 和 Wo…

Spring Boot + OpenAI 构建基于RAG的智能问答系统

一、技术架构设计 1.1 系统架构图 [前端]│▼ (HTTP/REST) [Spring Boot Controller]│▼ (Service Call) [问答处理服务层]├─▶ [知识库检索模块] ──▶ [向量数据库]└─▶ [OpenAI集成模块] ──▶ [OpenAI API]│▼ [结果组装与返回] 1.2 技术选型 组件技术栈版本要求…

Oracle实用参考(13)——Oracle for Linux物理DG环境搭建(2)

13.2. Oracle for Linux物理DG环境搭建 Oracle 数据库的DataGuard技术方案,业界也称为DG,其在数据库高可用、容灾及负载分离等方面,都有着非常广泛的应用,对此,前面相关章节已做过较为详尽的讲解,此处不再赘述。 需要说明的是, DG方案又分为物理DG和逻辑DG,两者的搭建…

【论文阅读29】区间预测CIPM(2025)

这篇论文主要研究的是滑坡位移的区间预测方法&#xff0c;提出了一种新型的预测模型&#xff0c;叫做复合区间预测模型&#xff08;CIPM&#xff09;&#xff0c;并以三峡库区的白家堡滑坡为案例进行了应用和验证。论文的核心内容和贡献包括&#xff1a; 背景与问题 滑坡位移预…

Linux 文件系统底层原理笔记:磁盘结构、ext2 文件系统与软硬链接解析

文章目录 一、理解硬件1.1 磁盘、服务器、机柜、机房1.2 磁盘物理结构1.3 磁盘的存储结构1.4 磁盘的逻辑结构1.4.1 理解过程1.4.2 真实过程 1.5 CHS && LBA地址 二、引入文件系统2.1 引入"块"概念2.2 引入"分区"概念2.3 引入"inode"概念…

75Qt窗口_Qt窗口概览

Qt 窗⼝ 是通过 QMainWindow类 来实现的。 QMainWindow 是⼀个为⽤⼾提供主窗⼝程序的类&#xff0c;继承⾃ QWidget 类&#xff0c;并且提供了⼀个预定义的布局。 QMainWindow 包含 ⼀个菜单栏&#xff08;menu bar&#xff09;、多个⼯具栏(tool bars)、多个浮动窗⼝&#x…

Appium+python自动化(九)- 定位元素工具

简介 环境搭建好了&#xff0c;其他方面的知识也准备的差不多了&#xff0c;那么就开始下一步元素定位&#xff0c;元素定位主要介绍如何使用uiautomatorviewer&#xff0c;通过定位到页面上的元素&#xff0c;然后进行相应的点击等操作. 此外在介绍另一款工具&#xff1a;Insp…

apipost将token设置为环境变量

右上角 可以新增或者是修改当前的环境 环境变量增加一个token,云端值和本地值可以不用写 在返回token的接口里设置后执行操作&#xff0c;通常是登录的接口 右侧也有方法提示 //设置环境变量 apt.environment.set("token", response.json.data.token); 在需要传t…

【Docker 02】Docker 安装

&#x1f308; 一、各版本的平台支持情况 ⭐ 1. Server 版本 Server 版本的 Docker 就只有个命令行&#xff0c;没有界面。 Platformx86_64 / amd64arm64 / aarch64arm(32 - bit)s390xCentOs√√Debian√√√Fedora√√Raspbian√RHEL√SLES√Ubuntu√√√√Binaries√√√ …

青少年编程与数学 01-011 系统软件简介 08 Windows操作系统

青少年编程与数学 01-011 系统软件简介 08 Windows操作系统 1. Windows操作系统的起源与发展1.1 早期版本&#xff08;1985-1995&#xff09;1.2 Windows 9x系列&#xff08;1995-2000&#xff09;1.3 Windows NT系列&#xff08;1993-2001&#xff09;1.4 Windows XP及以后版…

微服务架构的性能优化:链路追踪与可观测性建设

&#x1f4cb; 目录 引言&#xff1a;微服务性能挑战微服务架构性能瓶颈分析可观测性体系概述链路追踪技术深度解析性能监控指标体系日志聚合与分析分布式追踪系统实现性能优化策略与实践自动化性能调优故障诊断与根因分析最佳实践与案例研究未来发展趋势 引言 随着微服务架…

ubuntu屏幕复制

在ubnuntu20中没有办法正常使用镜像功能,这里提供一下复制屏幕的操作. 使用xrandr查看所有的显示器情况 这里我发现自己的电脑没有办法直接设置分辨率,但是外接的显示器可以设置,从命令行来说就是设置: xrandr --output HDMI-0 --mode 1920x1080那怎么样才能将原生电脑屏幕换…