当前位置: 首页 > news >正文

C++入门自学Day17-- 模版进阶知识

前言

        C++ 的两个强大且常被考查的主题就是 模板(templates) 和 继承/多态(inheritance & polymorphism)。模板把“泛型”和“编译期多态”带入语言,允许你写出与类型无关但高效的代码;继承把“运行时多态”带入语言,方便用公共接口操纵不同实现。

        本文按教学博客的风格来写:先给出模板函数的使用与进阶技巧(含现代 C++ 的常见高级手段),再讲 C++ 的继承特性(含虚函数、对象切片、虚继承等),每部分配上示例、常见陷阱与最佳实践,最后做统一总结。示例尽量贴近工程实战,适合直接放在博客里。


主要内容简介

  • 1、模板函数基础:函数模板、类型推导、非类型模板参数、模板重载/特化

  • 2、模板进阶:完美转发、折叠表达式、变参模板、SFINAE/enable_if、std::void_t 检测、C++20 Concepts

  • 3、继承基础:单继承、派生类、访问控制、构造/析构顺序、切片问题

  • 4、运行时多态:虚函数、纯虚函数(抽象类)、虚析构函数、override/final、dynamic_cast

  • 5、多重继承与虚继承:菱形问题、虚基类、二义性解决

  • 6、模板与继承的交互:CRTP、用模板约束继承关系(is_base_of / concepts)

  • 7、常见坑 & 最佳实践总结


一、C++ 模板函数(从基础到进阶)

1.1 函数模板 — 基本用法

template<typename T>
T Add(T a, T b) {return a + b;
}int x = Add<int>(1, 2); // 显示指定
auto y = Add(1, 2);     // 编译器推导为 int

要点:

  • 模板定义通常放在头文件(因为编译器需要在使用点见到定义进行实例化)。

  • 支持显式模板参数或由编译器类型推导

  • 函数模板可以与普通函数重载共存,重载解析遵循普通函数重载规则 + 模板优先级。


1.2 非类型模板参数 & 模板重载/特化

template<typename T, int N>
class Array{
private: T _a[N];
};int main(){Array<int, 1000> a;
}
  • 非类型模板参数(如 N)允许在编译期传入常量。

  • 函数模板可以做完整特化(rare),但不能做部分特化。部分特化只适用于类模板。

  •  可以作为非类型模板参数的类型

  • 整数类型(int, char, bool, 枚举等)

  • 指针或引用(必须是指向具有静态存储期的对象或函数的常量指针/引用)

  • std::nullptr_t

  • 注意:

  • 浮点型及自定义类型无法作为非类型模版参数

模版的特化(针对某些类型进行特殊化处理):
在函数模板显式特化时,函数签名必须和主模板一致,只有模板参数的具体化不同,否则不是有效的特化。
template<typename T>
bool IsEqual(const T& left , const T& right){return left == right;
}
// 模版的特化(特殊化处理)
template<>
bool IsEqual<char*>(char* const& left , char* const& right){ return (strcmp(left, right) == 0);
}int main(){int a = 0, b = 1;cout << IsEqual(a,b)<<endl;const char* p1 = "Hello";const char* p2 = "World";cout<< IsEqual(p1,p2) << endl;
}

输出描述:

0

0


特化的分类:1、全特化。2、偏特化

template<class T1, class T2>
class Data{public:Data(){cout<< "Data<T1,T2>"<<endl;}private:T1 _d1;T2 _d2;
};
template<>
class Data<int, char>{public:Data(){cout<< "全特化 Data<int,char>"<<endl;}private:;
};
template<class T2>
class Data<int,T2>{public:Data(){cout<<"偏特化 Data<int,T2>"<<endl;}private:;
};
int main(){Data<short, short> d1;Data<int, char> d2;Data<int, double> d3;
}

输出描述:

Data<T1,T2>
全特化 Data<int,char>
偏特化 Data<int,T2>


1.3 变参模板(variadic templates)与折叠表达式(C++17)

// 递归版本(老式)
template<typename T>
T Sum(T v){ return v; }template<typename T, typename... Ts>
T Sum(T head, Ts... tail) {return head + Sum(tail...);
}// C++17 折叠表达式,简洁高效
template<typename... Ts>
auto Sum2(Ts... args) {return (args + ... + 0); // fold expression
}

1.4 转发引用与完美转发(perfect forwarding)

用于实现通用工厂/封装函数,避免不必要拷贝:

template<typename T, typename... Args>
std::unique_ptr<T> MakeUnique(Args&&... args) {return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

注意:

  • T&& 在模板上下文中可能是 转发引用(又称万能引用)。

  • 使用 std::forward 将参数按原始值类别(左值/右值)传递,保留移动语义。


二、C++ 模板的分离编译

🔹 1. 模板的基本编译机制

  • 普通函数 / 类:编译器在编译 .cpp 文件时就能生成确定的符号。

  • 模板函数 / 类:只有在**实例化(instantiation)**时(即编译器遇到具体类型实参)才会生成代码。

👉 因此模板代码必须在编译时可见,这就是为什么我们通常把模板的实现,函数,类的声明都写在 .h 文件里,而不是 .cpp。


🔹 2. 为什么模板不能直接“分离编译”

假设你写了两个文件:

// MyTemplate.h
template<typename T>
T add(T a, T b);
// MyTemplate.cpp
#include "MyTemplate.h"
template<typename T>
T add(T a, T b) {return a + b;
}

然后在 main.cpp 里:

#include "MyTemplate.h"
int main() {int x = add(1, 2);  // 想用 int 版本
}

⚠️ 这会导致 链接错误:因为 main.cpp 编译时看到 add 的声明,但 .o 文件里没生成 add<int> 的实例化。

因为mytemplate.cpp 中函数模版并不知道其对应类型。实例化是在main.cpp

编译器不会自动去 MyTemplate.cpp 里“搜模板定义”。


🔹 3. 解决方法

有三种常见方法:

✅ 方法一:全写在头文件里(最常见)
// MyTemplate.h
template<typename T>
T add(T a, T b) {return a + b;
}

这样所有用到 add 的翻译单元都能看到实现,编译器就能实例化。


✅ 方法二:显式实例化(explicit instantiation)

你可以在 .cpp 里告诉编译器:

“我需要这些具体类型的实例化,帮我生成一次即可”。

// MyTemplate.h
template<typename T>
T add(T a, T b);
// MyTemplate.cpp
#include "MyTemplate.h"template<typename T>
T add(T a, T b) {return a + b;
}// 显式实例化
template int add<int>(int, int);
template double add<double>(double, double);

这样 main.cpp 里用 add<int> 或 add<double> 时,链接器能找到在 MyTemplate.o 里已经生成的符号。

👉 适合库开发,不想把实现暴露在头文件时。


✅ 方法三:把实现放在 .tpp 或 .inl 文件中

约定俗成的做法:

  • .h 里只放声明

  • .tpp(或 .inl)里放实现

  • 在 .h 文件末尾 #include "MyTemplate.tpp"


这样使用者只需要 #include "MyTemplate.h",实现还是“逻辑分离”的。

  • 模板必须在实例化时可见,所以不能像普通函数那样分离编译。

  • 常见解决办法:

    1. 全部写在 .h(最常见)。

    2. 在 .cpp 中显式实例化需要的类型。

    3. .h + .tpp 组合,让逻辑结构更清晰。


三、总结

  • 模板:提供“编译期泛型”与静态多态。掌握变参模板、完美转发、SFINAE(或 Concepts)、std::void_t 检测,是写出高质量模板库的关键。优先用 Concepts(C++20)以获得更可读的接口约束。


文章转载自:

http://S1hK9sHR.thhrf.cn
http://E36u0gzm.thhrf.cn
http://ty3BNeJY.thhrf.cn
http://FJBZ1Txv.thhrf.cn
http://o1pYWb0j.thhrf.cn
http://e6sLhHAa.thhrf.cn
http://odzm6eyo.thhrf.cn
http://x3IpmuPg.thhrf.cn
http://vqkxgfX9.thhrf.cn
http://nSfXYHP4.thhrf.cn
http://IbiE8rKS.thhrf.cn
http://j4kIC4yn.thhrf.cn
http://1F6AIkd5.thhrf.cn
http://NnHM9DtN.thhrf.cn
http://Q7rxNEA9.thhrf.cn
http://og9Eipjx.thhrf.cn
http://nDAnAN0J.thhrf.cn
http://25nl6fQk.thhrf.cn
http://1yL8iZsa.thhrf.cn
http://hUVAgMPI.thhrf.cn
http://EebPsNfd.thhrf.cn
http://J87WbDtp.thhrf.cn
http://XjVaxftT.thhrf.cn
http://uF7yZGaB.thhrf.cn
http://jmsfePVo.thhrf.cn
http://9OXFnz4Q.thhrf.cn
http://jLunZibf.thhrf.cn
http://XPqxAN8t.thhrf.cn
http://VGAEovAa.thhrf.cn
http://iTniXeaV.thhrf.cn
http://www.dtcms.com/a/363174.html

相关文章:

  • 从程序员到「认识罕见病 DAO」发起人,他用 Web3 承载爱与责任
  • C# 简述委托,Func与Action委托。 他们之前有什么区别?
  • MCU上跑AI—实时目标检测算法探索
  • 将css中的线性渐变,径向渐变,锥心渐变,转成canvas中的渐变
  • 【高并发内存池】三、线程缓存的设计
  • SpringBoot3中使用Caffeine缓存组件
  • Ruoyi-vue-plus-5.x第三篇Redis缓存与分布式技术:3.2 缓存注解与使用
  • 悬停头部的实现方式之一 css: position: sticky
  • SQL Server-查询事务日志
  • 血缘元数据采集开放标准:OpenLineage Guides 在 Spark 中使用 OpenLineage
  • B2B营销面临的一些主要问题
  • 3025. 人员站位的方案数 I
  • HDI线路板与普通板有何区别?厂家生产难度在哪?
  • 【leetcode】236. 二叉树的最近公共祖先
  • 《“人工智能+”行动意见》深度解析:从智能红利到产业落地,直播SDK的技术价值与应用路径
  • Kafka:Java开发的消息神器,你真的懂了吗?
  • 货运系统源码 货运物流小程序 货运平台搭建 货运软件开发
  • 深度学习——基于卷积神经网络实现食物图像分类【4】(使用最优模型)
  • Directus搜索功能:全文检索和高级过滤的技术实现
  • LeetCode22生成括号算法
  • 【开题答辩全过程】以 基于PHP的蔬菜食杂购物系统为例,包含答辩的问题和答案
  • 完全背包|dfs
  • qt安装FFmpeg后编译遇到error: collect2.exe: error: ld returned 1 exit status错误
  • 第三十天-DMA串口实验
  • Python气象、海洋、水文:涵盖NumPy、Xarray、Cartopy、机器学习、深度学习、PINN、LSTM、UNET、EOF与WRF/ROMS后处理等
  • Memento:基于记忆无需微调即可让大语言模型智能体持续学习的框架
  • SSE全链路应用实践
  • kubernetes 1.31 节点之间(1个master ,多个worker)使用了哪些端口及防火墙设置
  • 软件测试面试题【内附超详细面试宝典】
  • @Apache Hive 介绍部署与使用详细指南