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

【C++】模板 - - - 泛型编程的魔法模具,一键生成各类代码

在这里插入图片描述

💻作 者 简 介:曾 与 你 一 样 迷 茫,现 以 经 验 助 你 入 门 C++。
💡个 人 主 页:@笑口常开xpr 的 个 人 主 页
📚系 列 专 栏:C++ 炼 魂 场:从 青 铜 到 王 者 的 进 阶 之 路
✨代 码 趣 语:模 板 是 “万 能 模 具” - - - 不 管 是 铁(int)、铜 (double)还 是 塑 料(自 定 义 类),只 要 往 模 具 里 一 放,就 能 按 同 样 的 造 型(逻 辑)做 出 对 应 材 质 的 零 件,不 用 每 种 材 质 都 重 新 造 个 新 模 具。
💪代 码 千 行,始 于 坚 持,每 日 敲 码,进 阶 编 程 之 路。
📦gitee 链 接:gitee

在这里插入图片描述

文 章 目 录

  • 一、模 版
    • (1)作 用
    • (2)typename
    • (3)非 类 型 模 板 参 数
    • (4)按 需 实 例 化
    • (5)array
  • 二、类 模 板
    • (1)定 义
    • (2)作 用
    • (3)基 本 语 法
  • 三、特 化
    • (1)定 义
    • (2)类 模 板
      • 1、全 特 化
      • 2、偏 特 化
    • (3)函 数 模 板 特 化
  • 四、模 板 的 分 离 编 译
  • 五、优 缺 点
    • (1)优 点
    • (2)缺 陷
  • 六、总 结

         本 文 围 绕 C++ 模 板 核 心 知 识(类 / 函 数 模 板、特 化、分 离 编 译 等)及 vector、array 容 器 特 性 展 开,结 合 代 码 示 例 拆 解 关 键 概 念,助 力 入 门 者 掌 握 泛 型 编 程 与 容 器 使 用 基 础。


一、模 版

模 版

(1)作 用

  1. 模 版 可 以 控 制 容 器 的 数 据 类 型。
  2. 模 版 可 以 控 制 某 种 设 计 逻 辑,比 如 适 配 器 模 式、数 组 栈、链 表 栈 等 等。
  3. 传 递 类 型,使 用 仿 函 数,让 类 重 载 operator()

(2)typename

  1. 定 义 模 版 时 可 以 使 用 typename 或 者 class,二 者 是 等 价 的。
template<typename Container>
template<class Container>
  1. 使 用 类 模 板 使 用 :: 时 要 注 意 前 面 是 类 型 还 是 对 象。如 果 是 类 型,前 面 要 加 typename,如 果 是 对 象,前 面 不 需 要。例 如 将 函 数 改 成 泛 型 时,使 用 迭 代 器 之 前 要 加 typename
#include<vector>
#include<iostream>
using namespace std;void Print(const vector<int>& v)
{vector<int>::const_iterator it = v.begin();while (it != v.end()){cout << *it << " ";it++;}cout << endl;
}
//template<typename Container>//class和typename的作用一样
template<class Container>
void Print(const Container& v)
{typename Container::const_iterator it = v.begin();while (it != v.end()){cout << *it << " ";it++;}cout << endl;
}
int main()
{vector<int> vec;vec.push_back(1);vec.push_back(2);vec.push_back(3);vec.push_back(4);for (auto e : vec){cout << e << " ";}cout << endl;Print(vec);return 0;
}

         Print 函 数 在 没 有 使 用 模 版 之 前,使 用 迭 代 器 时 进 行 了 实 例 化,这 里 是 类 型,使 用 模 版 参 数 后 没 有 实 例 化,无 法 区 分 迭 代 器 前 面 是 类 型 还 是 对 象,使 用 typename 来 区 分 这 里 是 类 型 还 是 对 象。如 果 使 用 auto 则 不 需 要 使 用 typename


(3)非 类 型 模 板 参 数

         模 板 参 数 分 为 类 类 型 形 参 与 非 类 型 形 参。

类 型 形 参:出 现 在 模 板 参 数 列 表 中,跟 在 class 或 者typename 之 类 的 参 数 类 型 名 称。

非 类 型 形 参:用 一 个 常 量 作 为 类 (函 数) 模 板 的 一 个 参 数,在 类 ( 函 数 ) 模 板 中 可 将 该 参 数 当 成 常 量 来 使 用。非 类 型 模 板 参 数 必 须 是 常 量 或 者 整 型,主 要 用 于 定 义 数 组。

#include<iostream>
using namespace std;
template<class T,size_t N>
class stack
{
//静态栈
private:T _a[N];int _top;
};
int main()
{stack<int,10> st1;  //10stack<int,100> st2;  //100return 0;
}

         上 面 的 代 码 中 N 是 非 类 型 模 板 参 数,N 是 常 量,不 能 修 改 N,比 define 更 灵 活。作 用 域 是 整 个 类,形 参 的 作 用 域 是 函 数。T 是 类 型 模 板 参 数。

注 意

  1. 浮 点 数、类 对 象 以 及 字 符 串 是 不 允 许 作 为 非 类 型 模 板 参 数 的。
  2. 非 类 型 的 模 板 参 数 必 须 在 编 译 期 就 能 确 认 结 果。

(4)按 需 实 例 化

         函 数 调 用 会 进 行 实 例 化,如 果 不 调 用 不 会 实 例 化。有 的 编 译 器 会 出 现,Vs 编 译 器 会 出 现 这 种 情 况。

#include<iostream>
using namespace std;
template<class T,size_t N>
class stack
{
//静态栈
public:void Func(){N = 0;}
private:T _a[N];int _top;
};
int main()
{stack<int,10> st1;  //10stack<int,100> st2;  //100//按需实例化//st1.Func();return 0;
}

不 调 用 Func
代 码 编 译 成 功。
在这里插入图片描述
调 用 Func
代 码 编 译 失 败。
在这里插入图片描述


(5)array

array
         array 属 于 C++ 中 比 较 没 用 的 东 西,用 处 比 较 小,没 有 vector 好 用。也 就 是 C 语 言 中 的 数 组。array 的 优 势 在 于 处 理 数 组 越 界 的 问 题,数 组 越 界 时 使 用 array 会 发 生 断 言 处 理。C 语 言 的 数 组 只 能 检 查 少 部 分 的 越 界 处 理。
在这里插入图片描述

#include<iostream>
#include<array>
using namespace std;
int main()
{array<int, 10> a;for (auto e : a){cout << e << " ";}cout << endl;a[10];//越界访问会中断程序return 0;
}

在这里插入图片描述


二、类 模 板

类 模 板

(1)定 义

         类 模 板 允 许 定 义 一 个 带 参 数 的 类,这 个 参 数 可 以 是 任 意 类 型,从 而 让 类 能 适 应 多 种 数 据 类 型,而 不 需 要 重 复 写 多 份 代 码。

(2)作 用

         实 现 类 的 泛 型 化,提 高 代 码 复 用 性。

(3)基 本 语 法

#include<iostream>
using namespace std;
template <class T>  // T是类型参数
class ClassName 
{
public:T member;ClassName(T m) : member(m) {}void print() {cout << member << endl;}
};
int main() 
{ClassName<int> obj1(10);      ClassName<double> obj2(3.14); obj1.print(); obj2.print(); return 0;
}

在这里插入图片描述
         使 用 类 模 板 时,必 须 显 式 指 定 类 型 参 数(编 译 器 不 会 像 函 数 模 板 那 样 自 动 推 导 类 模 板 类 型)。

三、特 化

(1)定 义

         通 常 情 况 下,使 用 模 板 可 以 实 现 一 些 与 类 型 无 关 的 代 码,但 对 于 一 些 特 殊 类 型 的 可 能 会 得 到 一 些 错 误 的 结 果,需 要 特 殊 处 理。在 原 模 板 类 的 基 础 上,针 对 特 殊 类 型 所 进 行 特 殊 化 的 实 现 方 式。

(2)类 模 板

1、全 特 化

         全 特 化 即 是 将 模 板 参 数 列 表 中 所 有 的 参 数 都 确 定 化。(必 须 包 括 原 来 的 类 型)

#include<iostream>
using namespace std;
template <class T>
class Pair 
{
public:Pair(T a, T b) : first(a), second(b) {}void print() {cout << first << " " << second << endl;}
private:T first, second;
};//全特化版本
template<>
class Pair<int> 
{
public:Pair(int a, int b) : first(a), second(b) {}void print() {cout << "int special: " << first << ", " << second << endl;}
private:int first, second;
};
int main()
{Pair<double> p1(1.2, 3.4); //调用普通模板Pair<int> p2(10, 20);      //调用全特化版本p1.print();p2.print();return 0;
}

在这里插入图片描述


2、偏 特 化

         任 何 针 对 模 版 参 数 进 一 步 进 行 条 件 限 制 设 计 的 特 化 版 本。类 模 板 特 有,函 数 模 板 不 支 持。比 如 对 于 以 下 模 板 类:

template<class T1, class T2>
class Data
{
public:Data() { cout << "Data<T1, T2>" << endl;}
private:T1 _d1;T2 _d2;
};

两 种 表 现 方 式

  1. 部 分 特 化:将 模 板 参 数 类 表 中 的 一 部 分 参 数 特 化。
// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:Data() { cout << "Data<T1, int>" << endl;}
private:T1 _d1;int _d2;
};
  1. 参 数 更 进 一 步 的 限 制
             偏 特 化 并 不 仅 仅 是 指 特 化 部 分 参 数,而 是 针 对 模 板 参 数 更 进 一 步 的 条 件 限 制 所 设 计 出 来 的 一 个 特 化 版 本。
//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:Data() { cout << "Data<T1*, T2*>" << endl;}
private:T1 _d1;T2 _d2;
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:Data(const T1& d1, const T2& d2): _d1(d1), _d2(d2){cout << "Data<T1&, T2&>" << endl;}
private:const T1& _d1;const T2& _d2;
};
int main()
{Data<double, int> d1;	   //调用特化的int版本Data<int, double> d2;	   //调用基础的模板Data<int*, int*> d3;	   //调用特化的指针版本Data<int&, int&> d4(1, 2); //调用特化的指针版本return 0;
}

在这里插入图片描述

(3)函 数 模 板 特 化

步 骤

  1. 必 须 要 先 有 一 个 基 础 的 函 数 模 板
  2. 关 键 字 template 后 面 接 一 对 空 的 尖 括 号 <>
  3. 函 数 名 后 跟 一 对 尖 括 号,尖 括 号 中 指 定 需 要 特 化 的 类 型
  4. 函 数 形 参 表:必 须 要 和 模 板 函 数 的 基 础 参 数 类 型 完 全 相 同,如 果 不 同 编 译 器 可 能 会 报 一 些 奇 怪 的 错 误。
  5. 函 数 模 板 可 以 全 特 化(不 能 偏 特 化)。
  6. 如 果 需 要 针 对 某 类 类 型 做 特 殊 处 理,通 常 用 函 数 重 载 代 替 特 化。
#include<iostream>
using namespace std;
//函数模板 --- 参数匹配
template <class T>
bool Less(T a, T b) 
{cout << "Less(T a, T b)" << " ";return a < b;
}
//全特化版本(针对int*)
template <>
bool Less<int*>(int* a, int* b) 
{cout << "Less<int*>" << " ";return *a < *b;
}
int main()
{int a = 1;int b = 2;cout << Less(a, b) << endl;cout << Less(&a, &b) << endl;return 0;
}

在这里插入图片描述


四、模 板 的 分 离 编 译

定 义
         一 个 程 序(项 目)由 若 干 个 源 文 件 共 同 实 现,而 每 个 源 文 件 单 独 编 译 生 成 目 标 文 件,最 后 将 所 有 目 标 文 件 链 接 起 来 形 成 单 一 的 可 执 行 文 件 的 过 程 称 为 分 离 编 译 模 式。

stack.cpp

#include"stack.h"
using namespace sta;template<class T, class Container>
void stack<T,Container>::push(const T& x)
{_con.push_back(x);
}template<class T, class Container>
void stack<T, Container>::pop()
{_con.pop_back();
}void A::func1()
{}
//void A::func2()

stack.h

#pragma once
#include<vector>
#include<list>
#include<iostream>
using namespace std;
namespace sta
{template<class T,class Container = vector<T>>class stack{public:void push(const T& x);void pop();T& top(){return _con.back();}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:Container _con;};class A{public:void func1();void func2();};
}

test.cpp

#include"stack.h"
using namespace sta;
int main()
{stack<int> st;//有声明和定义st.push(1);st.push(2);st.push(3);A aa;aa.func1();//有声明和定义aa.func2();//有声明没有定义return 0;
}

在这里插入图片描述
         这 里 是 链 接 错 误,找 不 到 地 址。编 译 阶 段 看 只 有 声 明,声 明 是 一 种 承 诺,所 以 编 译 阶 段 检 查 声 明 函 数 名 参 数 返 回 可 以 对 上。等 着 链 接 的 时 候,使 用 修 饰 后 的 函 数 去 查 找。

3 种 情 况

  1. func1 有 声 明 和 定 义,链 接 成 功。
  2. func2 有 声 明 没 有 定 义,链 接 失 败。
  3. push 有 声 明 和 定 义,链 接 失 败。

方 法 1
         可 以 在 所 有 定 义 最 后 面 添 加 下 面 的 代 码 即 可,这 种 方 式 被 称 做 显 示 实 例 化,func2 需 要 添 加 定 义。

template
class stack<int>;

方 法 2
         将 声 明 和 定 义 放 到 一 个 文 件 “xxx.hpp” 里 面 或 者 “xxx.h” 其 实 也 是 可 以 的。这 里 推 荐 使 用 这 种 方 式。


五、优 缺 点

(1)优 点

  1. 模 板 复 用 了 代 码,节 省 资 源,更 快 的 迭 代 开 发,C++ 的 标 准 模 板 库 (STL) 因 此 而 产 生
  2. 增 强 了 代 码 的 灵 活 性

(2)缺 陷

  1. 模 板 会 导 致 代 码 膨 胀 问 题,也 会 导 致 编 译 时 间 变 长
  2. 出 现 模 板 编 译 错 误 时,错 误 信 息 非 常 凌 乱,不 易 定 位 错 误。

在这里插入图片描述


六、总 结

         本 文 梳 理 了 C++ 模 板 的 复 用 逻 辑、特 化 规 则 及 分 离 编 译 问 题,也 明 确 了 vector 与 array 的 特 性 差 异;模 板 虽 提 升 代 码 灵 活 性,但 需 注 意 编 译 膨 胀 与 错 误 定 位 问 题,掌 握 这 些 内 容 是 高 效 编 写 C++ 代 码 的 重 要 基 础。

http://www.dtcms.com/a/469942.html

相关文章:

  • Vue3知识详解(一)(基础知识部分)
  • 网站网页链接网站变灰色 html
  • Docker核心技术:深入理解网络模式 ——Bridge模式全栈实战与性能调优
  • Spring Web MVC构建现代Java Web应用的基石
  • 如何做tiktok的数据排行网站手机网站页面大小
  • 单片机睡眠模式详解:睡眠、停止与待机
  • 长春做网站公司哪家好做统计图的网站
  • 【Android Gradle学习笔记】第一天:认识下Gradle
  • 一级a做爰片免费网站孕交视频教程wordpress添加作者名字
  • 《基础算法递归-----汉诺塔问题》
  • 网站前台设计模板荆州网站建设 松滋网站建设
  • 【agent】AI 数字人构建8:本地edge-tts实现 tts
  • 做网站的法律贵州门户网站建设
  • 创建公司网站需要什么外贸网站系统
  • MySQL字符集与排序规则全解析
  • 在云计算环境中实施有效的数据安全策略
  • 建设电子商务网站的意义巴中市建设厅官方网站
  • DES 加密算法:核心组件、加解密流程与安全特性
  • 游戏怎么做充值网站天津市建设工程监理公司网站
  • 01-Python简介与环境搭建-练习
  • Flink面试题及详细答案100道(81-100)- 部署、优化与生态
  • 机器学习实践项目(一)- Rossman商店销售预测 - 预处理数据
  • spring-Integration
  • SQL核心语言详解:DQL、DML、DDL、DCL从入门到实践!
  • 相亲网站怎么做的免费做网站tk
  • 在阿里巴巴上做网站要多少钱怎样制作自己的app
  • 数据湖Hudi - 二级索引:配置方法、存储位置与自动构建全解析(附电商实操案例)
  • 基于K近邻(KNN)算法的高光谱数据分类MATLAB实现
  • 石油网页设计与网站建设万网如何上传网站
  • 乐迪信息:智慧煤矿输送带安全如何保障?AI摄像机全天候识别