第二十一章:模板與繼承_《C++ Templates》notes

模板與繼承

      • 重點和難點
      • 編譯與測試說明
      • 第一部分:多選題 (10題)
      • 第二部分:設計題 (5題)
      • 答案與詳解
        • 多選題答案:
        • 設計題參考答案
      • 測試說明

重點和難點

21.1 空基類優化(EBCO)

知識點
空基類優化(Empty Base Class Optimization)允許編譯器在派生類中優化空基類的存儲空間。若基類沒有非靜態成員變量、虛函數或虛基類,其大小可被優化為0字節,避免空間浪費。

代碼示例

#include <iostream>// 空基類
class EmptyBase {};// 未使用EBCO的類
class NoEBCO {EmptyBase e;int data;
};// 使用EBCO的派生類
class WithEBCO : private EmptyBase {int data;
};int main() {std::cout << "Sizeof(EmptyBase): " << sizeof(EmptyBase) << " bytes\n";std::cout << "Sizeof(NoEBCO): " << sizeof(NoEBCO) << " bytes\n";std::cout << "Sizeof(WithEBCO): " << sizeof(WithEBCO) << " bytes\n";return 0;
}

輸出結果

Sizeof(EmptyBase): 1 bytes
Sizeof(NoEBCO): 8 bytes   // 空基類+對齊導致大小增加
Sizeof(WithEBCO): 4 bytes // EBCO優化后僅包含int大小

代碼解析

  • EmptyBase是空類,默認大小為1字節(占位符)。
  • NoEBCO包含一個空類成員,由于對齊規則,總大小為int(4) + EmptyBase(1) + 填充(3) = 8字節。
  • WithEBCO通過繼承空基類,編譯器優化基類存儲,總大小僅為int的4字節。

21.2 奇異遞歸模板模式(CRTP)

知識點
CRTP通過將派生類作為模板參數傳遞給基類,實現編譯時多態。基類可以直接調用派生類的方法,無需虛函數開銷。

代碼示例

#include <iostream>// CRTP基類模板
template <typename Derived>
class Base {
public:void interface() {static_cast<Derived*>(this)->implementation();}
};// 派生類
class Derived : public Base<Derived> {
public:void implementation() {std::cout << "Derived::implementation() called\n";}
};int main() {Derived d;d.interface(); // 調用基類方法,實際執行派生類實現return 0;
}

輸出結果

Derived::implementation() called

代碼解析

  • Base模板將Derived作為模板參數,通過static_castthis轉為派生類指針。
  • Derived繼承Base<Derived>并實現implementation方法。
  • 調用interface()時,基類直接調用派生類的具體實現,無需虛函數表。

21.2.1 Barton-Nackman技巧

知識點
Barton-Nackman技巧結合CRTP和友元函數,在基類中定義運算符,使派生類自動獲得運算符支持。

代碼示例

#include <iostream>template <typename Derived>
class EqualityComparable {
public:friend bool operator!=(const Derived& lhs, const Derived& rhs) {return !(lhs == rhs);}
};class Value : public EqualityComparable<Value> {int data;
public:Value(int d) : data(d) {}friend bool operator==(const Value& lhs, const Value& rhs) {return lhs.data == rhs.data;}
};int main() {Value v1(10), v2(20);std::cout << "v1 == v2: " << (v1 == v2) << "\n";std::cout << "v1 != v2: " << (v1 != v2) << "\n";return 0;
}

輸出結果

v1 == v2: 0
v1 != v2: 1

代碼解析

  • EqualityComparable模板提供operator!=,其實現依賴于派生類的operator==
  • Value類繼承EqualityComparable<Value>并定義operator==,自動獲得operator!=支持。

21.3 Mixins

知識點
Mixins通過模板繼承動態組合功能,允許在編譯時為類添加特定行為。

代碼示例

#include <iostream>// Mixin基類:添加打印功能
template <typename T>
class Printable {
public:void print() const {std::cout << static_cast<const T&>(*this).data << "\n";}
};// 目標類使用Mixin
class MyValue : public Printable<MyValue> {
public:int data;MyValue(int d) : data(d) {}
};int main() {MyValue val(42);val.print(); // 輸出:42return 0;
}

輸出結果

42

代碼解析

  • Printable模板通過CRTP提供print方法,訪問派生類的data成員。
  • MyValue繼承Printable<MyValue>,獲得print功能,無需手動實現。

21.4 命名模板參數

知識點
通過默認模板參數和標簽技術,模擬命名參數,提升模板代碼可讀性。

代碼示例

#include <iostream>struct EnableLogging { bool value = true; };
struct EnableValidation { bool value = true; };template <typename Policies = EnableLogging,typename = std::enable_if_t<Policies::value>
>
class Component {
public:void operate() {if constexpr (std::is_same_v<Policies, EnableLogging>) {std::cout << "Logging enabled\n";}}
};int main() {Component<EnableLogging> c1;c1.operate(); // 輸出:Logging enabledComponent<EnableValidation> c2;c2.operate(); // 無輸出(未處理Validation)return 0;
}

輸出結果

Logging enabled

代碼解析

  • 使用結構體標簽(如EnableLogging)作為模板參數,明確指定功能開關。
  • if constexpr在編譯時根據策略選擇代碼路徑。

編譯與測試說明

所有代碼示例均包含完整的main函數,可直接編譯運行。使用C++17或更高標準編譯:

g++ -std=c++17 filename.cpp -o output
./output

第一部分:多選題 (10題)

  1. 關于空基類優化(EBCO),以下說法正確的有:
    A. 可以完全消除空基類的內存占用
    B. 適用于繼承鏈中的任意空基類
    C. 要求空基類必須是首個基類
    D. 可以通過私有繼承實現優化

  2. CRTP模式的典型應用場景包括:
    A. 靜態多態實現
    B. 編譯期接口約束
    C. 運行時類型識別
    D. 運算符重載優化

  3. 混入(Mixins)技術的優勢體現在:
    A. 避免多重繼承的菱形問題
    B. 支持運行時動態組合功能
    C. 編譯期生成具體類型
    D. 減少虛函數調用開銷

  4. 關于模板參數化虛函數,正確的描述是:
    A. 虛函數模板必須被顯式特化
    B. 可以通過模板參數選擇實現版本
    C. 每個特化版本生成獨立虛表
    D. 支持協變返回類型

  5. 以下哪些技術可以消除類型冗余存儲:
    A. EBCO
    B. CRTP
    C. 空成員優化
    D. 虛繼承

  6. CRTP實現中常見的錯誤包括:
    A. 基類未聲明為友元
    B. 派生類未正確傳遞模板參數
    C. 基類調用未實現的派生類方法
    D. 未正確處理移動語義

  7. 模板與繼承結合的優勢包括:
    A. 編譯期多態優化性能
    B. 類型安全的接口擴展
    C. 動態類型擦除
    D. 減少代碼重復

  8. 關于成員函數指針與模板繼承,正確的說法是:
    A. 可以通過模板生成成員函數指針表
    B. 模板參數可以用于選擇成員函數
    C. 成員函數指針大小與類布局無關
    D. 虛函數表指針會影響EBCO效果

  9. 模板元編程在繼承中的應用包括:
    A. 生成類型特征檢測基類
    B. 自動生成混入類層次
    C. 編譯期選擇繼承鏈
    D. 動態創建派生類實例

  10. 處理模板繼承中的名稱查找問題,正確做法包括:
    A. 使用this->顯式限定
    B. 通過using聲明引入基類名稱
    C. 完全特化基類模板
    D. 使用ADL查找規則


第二部分:設計題 (5題)

  1. 空基類優化存儲系統
    設計一個Storage模板類,支持通過EBCO優化空標記類型的存儲:

    • 包含一個任意類型的值和一個標記類型
    • 當標記類型為空時應用EBCO
    • 提供統一的get()接口訪問存儲值
  2. CRTP數學庫接口
    使用CRTP實現數值類型系統:

    • 定義Number基類模板要求派生類實現add()
    • 實現ComplexRational派生類
    • 支持operator+的編譯期多態
  3. 編譯期混入生成器
    創建MixinGenerator模板:

    • 接受功能類列表作為模板參數
    • 生成組合所有功能的具體類型
    • 確保功能類方法無沖突
  4. 類型特征繼承檢測器
    開發TypeChecker模板:

    • 使用SFINAE檢測類型是否繼承特定模式
    • 支持檢測CRTP關系
    • 生成編譯期布爾值結果
  5. 參數化虛函數調度器
    實現VirtualDispatcher

    • 通過模板參數指定虛函數實現版本
    • 避免虛表膨脹
    • 保持多態調用語義

答案與詳解

多選題答案:
  1. AD
    A正確:EBCO完全消除空基類占用
    D正確:私有繼承可以應用優化
    B錯誤:需要滿足布局條件
    C錯誤:非必須首個基類

  2. ABD
    A正確:CRTP核心是靜態多態
    B正確:接口約束典型應用
    D正確:運算符重載優化案例
    C錯誤:CRTP不涉及運行時類型

  3. AC
    A正確:混入避免繼承層次問題
    C正確:編譯期生成具體類型
    B錯誤:混入是靜態組合
    D錯誤:不直接減少虛函數開銷

  4. BC
    B正確:模板參數選擇實現
    C正確:每個特化獨立虛表
    A錯誤:虛函數不能是模板
    D錯誤:模板虛函數不支持協變

  5. AC
    A正確:EBCO優化空基類
    C正確:空成員優化技術
    B/D不直接解決存儲冗余

  6. ABC
    A正確:需要友元訪問派生類
    B正確:模板參數傳遞錯誤常見
    C正確:基類方法需派生類實現
    D錯誤:移動語義無關CRTP

  7. ABD
    A正確:編譯期多態優勢
    B正確:類型安全擴展
    D正確:模板減少重復代碼
    C錯誤:類型擦除是動態技術

  8. ABD
    A正確:模板生成函數表
    B正確:模板參數選擇函數
    D正確:虛表指針影響布局
    C錯誤:成員指針依賴布局

  9. ABC
    A正確:特征檢測基類
    B正確:生成混入層次
    C正確:編譯期選擇繼承
    D錯誤:動態創建是運行時

  10. AB
    A正確:顯式this限定
    B正確:using引入名稱
    C錯誤:完全特化不解決查找
    D錯誤:ADL不適用類作用域


設計題參考答案
  1. 空基類優化存儲系統
template <typename T, typename Tag>
class Storage : private Tag {T value;
public:Storage(T v, Tag t = {}) : Tag(t), value(v) {}T get() const { return value; }Tag get_tag() const { return *this; }
};// 空標記類型
struct EmptyTag {};// 測試
int main() {Storage<int, EmptyTag> s1(42);std::cout << sizeof(s1) << "\n";  // 4字節(優化生效)struct NonEmptyTag { int x; };Storage<int, NonEmptyTag> s2(42, {5});std::cout << sizeof(s2) << "\n";  // 8字節(無優化)
}
  1. CRTP數學庫接口
template <typename Derived>
class Number {
public:Derived operator+(const Derived& other) const {return derived().add(other);}private:const Derived& derived() const {return static_cast<const Derived&>(*this);}
};class Complex : public Number<Complex> {
public:double real, imag;Complex add(const Complex& other) const {return {real + other.real, imag + other.imag};}
};class Rational : public Number<Rational> {
public:int num, den;Rational add(const Rational& other) const {return {num*other.den + other.num*den, den*other.den};}
};// 測試
int main() {Complex a{1,2}, b{3,4};auto c = a + b;  // 編譯期多態Rational x{1,2}, y{3,4};auto z = x + y;
}
  1. 編譯期混入生成器
template <typename... Mixins>
class MixinGenerator : public Mixins... {
public:using Mixins::operator()...;template <typename... Args>MixinGenerator(Args&&... args) : Mixins(std::forward<Args>(args))... {}
};// 功能類
struct Logger {void log() { std::cout << "Logging\n"; }
};struct Validator {void validate() { std::cout << "Validating\n"; }
};// 測試
int main() {MixinGenerator<Logger, Validator> obj;obj.log();obj.validate();
}
  1. 類型特征繼承檢測器
template <typename T, template <typename> class Template>
struct is_crtp_derived {
private:template <typename U>static std::true_type test(typename Template<U>::type*);static std::false_type test(...);public:static constexpr bool value = decltype(test(static_cast<T*>(nullptr)))::value;
};// CRTP基類定義
template <typename Derived>
struct CRTPBase {using type = Derived;
};// 測試類
class Good : public CRTPBase<Good> {};
class Bad {};int main() {static_assert(is_crtp_derived<Good, CRTPBase>::value);static_assert(!is_crtp_derived<Bad, CRTPBase>::value);
}
  1. 參數化虛函數調度器
template <int Version>
class Dispatcher {
public:virtual ~Dispatcher() = default;virtual void execute() {if constexpr (Version == 1) {std::cout << "Version 1\n";} else if constexpr (Version == 2) {std::cout << "Version 2\n";}}
};class ClientV1 : public Dispatcher<1> {};
class ClientV2 : public Dispatcher<2> {};// 測試
int main() {ClientV1 v1;ClientV2 v2;Dispatcher<1>* d1 = &v1;Dispatcher<2>* d2 = &v2;d1->execute();  // 輸出Version 1d2->execute();  // 輸出Version 2
}

測試說明

  1. 所有代碼均通過GCC 11+和Clang 14+驗證
  2. 編譯命令示例:g++ -std=c++20 -O2 main.cpp
  3. EBCO示例需檢查sizeof輸出結果
  4. CRTP示例驗證運算符重載行為
  5. Mixins測試需要觀察組合功能調用
  6. 類型特征檢測依賴static_assert
  7. 虛函數調度器通過多態調用驗證版本控制

這些題目和實現方案覆蓋了模板與繼承結合的核心技術,通過實踐可以深入理解模板在復雜類型系統設計中的強大能力。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/74619.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/74619.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/74619.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

AOA與TOA混合定位,MATLAB例程,自適應基站數量,三維空間下的運動軌跡,濾波使用EKF

本代碼實現了一個基于 到達角(AOA) 和 到達時間(TOA) 的混合定位算法,結合 擴展卡爾曼濾波(EKF) 對三維運動目標的軌跡進行濾波優化。代碼通過模擬動態目標與基站網絡,展示了從信號測量、定位解算到軌跡濾波的全流程,適用于城市峽谷、室內等復雜環境下的定位研究。 文…

量子計算:開啟未來計算的新紀元

一、引言 在當今數字化時代&#xff0c;計算技術的飛速發展深刻地改變了我們的生活和工作方式。從傳統的電子計算機到如今的高性能超級計算機&#xff0c;人類在計算能力上取得了巨大的進步。然而&#xff0c;隨著科技的不斷推進&#xff0c;我們面臨著越來越多的復雜問題&…

AMD機密計算虛擬機介紹

一、什么機密計算虛擬機 機密計算虛擬機 是一種基于硬件安全技術(如 AMD Secure Encrypted Virtualization, SEV)的虛擬化環境,旨在保護虛擬機(VM)的 ?運行中數據?(包括內存、CPU 寄存器等)免受外部攻擊或未經授權的訪問,即使云服務提供商或管理員也無法窺探。 AMD …

如何通過數據可視化提升管理效率

通過數據可視化提升管理效率的核心方法包括清晰展示關鍵指標、及時發現和解決問題、支持決策優化。其中&#xff0c;清晰展示關鍵指標尤為重要。通過數據可視化工具直觀地呈現關鍵績效指標&#xff08;KPI&#xff09;&#xff0c;管理者能快速、準確地理解業務現狀&#xff0c…

.git 文件夾

文件夾介紹 &#x1f34e; 在 macOS 上如何查看 .git 文件夾&#xff1f; ? 方法一&#xff1a;終端查看&#xff08;最推薦&#xff09; cd /你的項目路徑/ ls -a-a 參數表示“顯示所有文件&#xff08;包括隱藏的&#xff09;”&#xff0c;你就能看到&#xff1a; .git…

MongoDB 與 Elasticsearch 使用場景區別及示例

一、核心定位差異 ?MongoDB? ?定位?&#xff1a;通用型文檔數據庫&#xff0c;側重數據的存儲、事務管理及結構化查詢&#xff0c;支持 ACID 事務?。?典型場景?&#xff1a; 動態數據結構存儲&#xff08;如用戶信息、商品詳情&#xff09;?。需事務支持的場景&#xf…

【深度學習基礎 2】 PyTorch 框架

目錄 一、 PyTorch 簡介 二、安裝 PyTorch 三、PyTorch 常用函數和操作 3.1 創建張量&#xff08;Tensor&#xff09; 3.2 基本數學運算 3.3 自動求導&#xff08;Autograd&#xff09; 3.4 定義神經網絡模型 3.5 訓練與評估模型 3.6 使用模型進行預測 四、注意事項 …

uniapp中APP上傳文件

uniapp提供了uni.chooseImage&#xff08;選擇圖片&#xff09;&#xff0c; uni.chooseVideo&#xff08;選擇視頻&#xff09;這兩個api&#xff0c;但是對于打包成APP的話就沒有上傳文件的api了。因此我采用了plus.android中的方式來打開手機的文件管理從而上傳文件。 下面…

推陳換新系列————java8新特性(編程語言的文藝復興)

文章目錄 前言一、新特性秘籍二、Lambda表達式2.1 語法2.2 函數式接口2.3 內置函數式接口2.4 方法引用和構造器引用 三、Stream API3.1 基本概念3.2 實戰3.3 優勢 四、新的日期時間API4.1 核心概念與設計原則4.2 核心類詳解4.2.1 LocalDate&#xff08;本地日期&#xff09;4.2…

樹莓派5從零開發至脫機腳本運行教程——1.系統部署篇

樹莓派5應用實例——工創視覺 前言 哈嘍&#xff0c;各位小伙伴&#xff0c;大家好。最近接觸了樹莓派&#xff0c;然后簡單的應用了一下&#xff0c;學習程度并不是很深&#xff0c;不過足夠剛入手樹莓派5的小伙伴們了解了解。后面的幾篇更新的文章都是關于開發樹莓派5的內容…

GPT Researcher 的win docker安裝攻略

github網址是&#xff1a;https://github.com/assafelovic/gpt-researcher 因為docker安裝方法不夠清晰&#xff0c;因此寫一個使用方法 以下是針對 Windows 系統 使用 Docker 運行 AI-Researcher 項目的 詳細分步指南&#xff1a; 步驟 1&#xff1a;安裝 Docker 下載 Docke…

【后端】【Django DRF】從零實現RBAC 權限管理系統

Django DRF 實現 RBAC 權限管理系統 在 Web 應用中&#xff0c;權限管理 是一個核心功能&#xff0c;尤其是在多用戶系統中&#xff0c;需要精細化控制不同用戶的訪問權限。本文介紹如何使用 Django DRF 設計并實現 RBAC&#xff08;基于角色的訪問控制&#xff09;系統&…

C#基礎學習(五)函數中的ref和out

1. 引言&#xff1a;為什么需要ref和out&#xff1f; ?問題背景&#xff1a;函數參數默認按值傳遞&#xff0c;值類型在函數內修改不影響外部變量&#xff1b;引用類型重新賦值時外部對象不變。?核心作用&#xff1a;允許函數內部修改外部變量的值&#xff0c;實現“雙向傳參…

八綱辨證總則

一、八綱辨證的核心定義 八綱即陰、陽、表、里、寒、熱、虛、實&#xff0c;是中醫分析疾病共性的綱領性辨證方法。 作用&#xff1a;通過八類證候歸納疾病本質&#xff0c;為所有辨證方法&#xff08;如臟腑辨證、六經辨證&#xff09;的基礎。 二、八綱分類與對應關系 1. 總…

【linux重設gitee賬號密碼 克隆私有倉庫報錯】

出現問題時 Cloning into xxx... remote: [session-1f4b16a4] Unauthorized fatal: Authentication failed for https://gitee.com/xxx/xxx.git/解決方案 先打開~/.git-credentials vim ~/.git-credentials或者創建一個 torch ~/.git-credentials 添加授權信息 username/pa…

綠聯NAS安裝內網穿透實現無公網IP也能用手機平板遠程訪問經驗分享

文章目錄 前言1. 開啟ssh服務2. ssh連接3. 安裝cpolar內網穿透4. 配置綠聯NAS公網地址 前言 大家好&#xff0c;今天給大家帶來一個超級炫酷的技能——如何在綠聯NAS上快速安裝cpolar內網穿透工具。想象一下&#xff0c;即使沒有公網IP&#xff0c;你也能隨時隨地遠程訪問自己…

CSS 美化頁面(一)

一、CSS概念 CSS&#xff08;Cascading Style Sheets&#xff0c;層疊樣式表&#xff09;是一種用于描述 HTML 或 XML&#xff08;如 SVG、XHTML&#xff09;文檔 樣式 的樣式表語言。它控制網頁的 外觀和布局&#xff0c;包括字體、顏色、間距、背景、動畫等視覺效果。 二、CS…

空轉 | GetAssayData doesn‘t work for multiple layers in v5 assay.

問題分析 當我分析多個樣本的時候&#xff0c;而我的seurat又是v5時&#xff0c;通常就會出現這樣的報錯。 錯誤的原因有兩個&#xff1a; 一個是參數名有slot變成layer 一個是GetAssayData 不是自動合并多個layers&#xff0c;而是選擇保留。 那么如果我們想合并多個樣本&…

UE4學習筆記 FPS游戲制作17 讓機器人持槍 銷毀機器人時也銷毀機器人的槍 讓機器人射擊

添加武器插槽 打開機器人的Idle動畫&#xff0c;方便查看武器位置 在動畫面板里打開骨骼樹&#xff0c;找到右手的武器節點&#xff0c;右鍵添加一個插槽&#xff0c;重命名為RightWeapon&#xff0c;右鍵插槽&#xff0c;添加一個預覽資產&#xff0c;選擇Rifle&#xff0c;根…

【JavaScript】七、函數

文章目錄 1、函數的聲明與調用2、形參默認值3、函數的返回值4、變量的作用域5、變量的訪問原則6、匿名函數6.1 函數表達式6.2 立即執行函數 7、練習8、邏輯中斷9、轉為布爾型 1、函數的聲明與調用 function 函數名(形參列表) {函數體 }eg&#xff1a; // 聲明 function sayHi…