第二十一章:模板与继承_《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.mzph.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…