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
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/84242.shtml
英文地址,請注明出處:http://en.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那怎么樣才能將原生電腦屏幕換…