【C++】 ——【模板初阶】——基础详解

news2024/11/24 8:28:46

目录

1. 泛型编程

1.1 泛型编程的概念

1.2 泛型编程的历史与发展

1.3 泛型编程的优势

1.4 泛型编程的挑战

2. 函数模板

2.1 函数模板概念

2.2 函数模板格式

2.3 函数模板的原理

2.4 函数模板的实例化

2.5 模板参数的匹配原则

2.6 函数模板的特化

2.7 函数模板的使用注意事项

2.8 函数模板的高级用法

3. 类模板

3.1 类模板的定义格式

3.2 类模板的实例化

3.3 类模板的特化

3.4 类模板成员函数的定义

3.5 类模板的使用注意事项

3.6 类模板的高级用法

结论


 

 

 

专栏:C++学习笔记

第一卷:C++ ———前言知识

第二卷:【C++】——入门基础知识

第二卷升华:【C++】——入门基础知识超详解

第三卷:第一篇【C++】————类与对象(上)-基础知识

第三卷:第一篇升华:剖析【C++】——类与对象(上)超详解——小白篇

第三卷:第二篇:剖析【C++】——类与对象(中)——小白篇—超详解

第三卷:第三篇:剖析【C++】——类和对象(下篇)——超详解——小白篇

第四卷:【C/C++】——小白初步了解——内存管理

在C++中,模板是一种强大的特性,可以实现代码的泛型编程,从而减少代码的重复,提高代码的复用性和可维护性。本文将详细讲解C++模板,涵盖以下几部分内容:

  1. 泛型编程
  2. 函数模板
  3. 类模板

1. 泛型编程

1.1 泛型编程的概念

泛型编程是一种编程范式,旨在编写与类型无关的代码,使得同一段代码能够处理不同的数据类型。这种编程方式提高了代码的通用性和复用性。在C++中,模板是实现泛型编程的核心机制。

1.2 泛型编程的历史与发展

泛型编程的概念最早由Alexander Stepanov和David Musser在1980年代提出。1990年代,泛型编程在C++标准模板库(STL)的实现中得到了广泛应用。STL提供了一组基于模板的容器、算法和迭代器,这些组件极大地提高了C++程序的效率和灵活性。

1.3 泛型编程的优势

  • 代码复用:模板允许开发人员编写一次代码,适用于多种数据类型,减少了代码的重复。
  • 类型安全:模板在编译时进行类型检查,避免了运行时错误。
  • 高效:模板在编译时实例化,生成的代码与手写的特定类型代码一样高效。

1.4 泛型编程的挑战

尽管泛型编程有许多优势,但它也带来了一些挑战:

  • 复杂性:模板代码的语法和错误信息较为复杂,初学者可能难以理解。
  • 编译时间:模板实例化会增加编译时间,尤其是在大型项目中。
  • 代码膨胀:由于模板实例化会生成多个版本的函数或类,可能导致可执行文件的体积增大。

2. 函数模板

2.1 函数模板概念

函数模板是用于创建通用函数的蓝图,允许我们编写与数据类型无关的函数。通过使用函数模板,可以避免为不同数据类型编写相同功能的多个函数,从而减少代码重复。

2.2 函数模板格式

函数模板的定义格式如下:

template <typename T>
返回类型 函数名(参数列表) {
    // 函数体
}

例如,一个简单的加法函数模板:

template <typename T>
T add(T a, T b) {
    return a + b;
}

2.3 函数模板的原理

函数模板的原理是通过在编译期间进行模板的实例化,将模板参数替换为实际参数类型,从而生成具体的函数版本。例如,调用add<int>(1, 2)会实例化一个int类型的add函数:

int add(int a, int b) {
    return a + b;
}

2.4 函数模板的实例化

函数模板的实例化可以是显式的或隐式的。隐式实例化是指编译器自动推断模板参数类型,而显式实例化是我们明确指定模板参数类型。例如:

隐式实例化:

add(1, 2); // 推断为 add<int>(1, 2)

显式实例化:

add<int>(1, 2);

2.5 模板参数的匹配原则

模板参数的匹配原则是编译器如何确定模板参数类型的规则。当调用函数模板时,编译器会尝试匹配模板参数和函数参数类型。如果匹配成功,则进行实例化;否则,编译会失败。匹配原则包括:

  1. 类型推断:编译器根据传递的实际参数类型推断模板参数类型。例如,add(1, 2)推断为add<int>(1, 2)
  2. 显式指定:调用模板函数时显式指定模板参数类型。例如,add<int>(1, 2)
  3. 默认参数:模板参数可以有默认类型。例如:
    template <typename T = int>
    T multiply(T a, T b) {
        return a * b;
    }
    

2.6 函数模板的特化

在某些情况下,我们可能需要对特定类型进行特殊处理,这时可以使用模板特化。模板特化允许我们为某些特定类型定义模板的特化版本。例如:

template <>
const char* add<const char*>(const char* a, const char* b) {
    static char result[100];
    strcpy(result, a);
    strcat(result, b);
    return result;
}

上述代码特化了add函数模板,使其可以处理const char*类型的字符串连接。

2.7 函数模板的使用注意事项

  1. 模板参数推断:在调用模板函数时,编译器会根据传递的参数推断模板参数类型。如果推断失败,需要显式指定模板参数类型。
  2. 编译错误信息:模板代码的编译错误信息通常比较复杂,调试时需要耐心和细致。特别是在模板嵌套和特化时,错误信息可能难以解读。
  3. 代码膨胀:由于模板实例化会生成多个函数版本,可能导致可执行文件体积增大。每次实例化模板时,都会生成一份新的代码副本,这在某些情况下可能导致二进制文件过大。
  4. 与非模板函数的冲突:在同一作用域中,如果存在与模板函数签名相同的非模板函数,可能会导致二义性和冲突。为避免这种情况,可以使用命名空间或显式实例化来区分模板函数和非模板函数。

2.8 函数模板的高级用法

函数模板的高级用法包括模板参数包(variadic templates)、模板别名(alias templates)等。例如,使用模板参数包实现一个通用的打印函数:

template <typename T>
void print(T arg) {
    std::cout << arg << std::endl;
}

template <typename T, typename... Args>
void print(T arg, Args... args) {
    std::cout << arg << ", ";
    print(args...);
}

上述代码利用模板参数包实现了一个递归打印函数,可以处理任意数量的参数。 

3. 类模板

3.1 类模板的定义格式

类模板允许我们创建一个通用的类,该类可以处理不同的数据类型。类模板的定义格式如下:

template <typename T>
class ClassName {
    // 类成员和方法
};

例如,一个简单的栈(Stack)类模板:

template <typename T>
class Stack {
private:
    std::vector<T> elements;

public:
    void push(T const& element) {
        elements.push_back(element);
    }

    void pop() {
        elements.pop_back();
    }

    T top() const {
        return elements.back();
    }
};

在这个例子中,Stack类模板定义了一个通用的栈,可以存储任意类型的数据。 

3.2 类模板的实例化

类模板的实例化类似于函数模板。例如:

Stack<int> intStack;
intStack.push(1);
intStack.push(2);
intStack.pop();
int topElement = intStack.top();

以上代码实例化了一个int类型的Stack对象,并对其进行了操作。

3.3 类模板的特化

与函数模板类似,我们也可以对类模板进行特化。例如:

template <>
class Stack<bool> {
private:
    std::vector<bool> elements;

public:
    void push(bool element) {
        elements.push_back(element);
    }

    void pop() {
        elements.pop_back();
    }

    bool top() const {
        return elements.back();
    }
};

上述代码特化了Stack类模板,使其可以处理bool类型。

3.4 类模板成员函数的定义

类模板的成员函数可以在类外定义。定义时需要再次指定模板参数。例如:

template <typename T>
void Stack<T>::push(T const& element) {
    elements.push_back(element);
}

template <typename T>
void Stack<T>::pop() {
    elements.pop_back();
}

template <typename T>
T Stack<T>::top() const {
    return elements.back();
}

这种定义方式使得类模板的实现更加清晰和模块化。

3.5 类模板的使用注意事项

  1. 模板参数推断:在实例化类模板时,需要明确指定模板参数类型,编译器无法自动推断。
  2. 代码膨胀:由于模板实例化会生成多个类版本,可能导致可执行文件体积增大。每次实例化模板时,都会生成一份新的代码副本,这在某些情况下可能导致二进制文件过大。
  3. 编译错误信息:模板代码的编译错误信息通常比较复杂,调试时需要耐心和细致。特别是在模板嵌套和特化时,错误信息可能难以解读。
  4. 与非模板类的冲突:在同一作用域中,如果存在与模板类签名相同的非模板类,可能会导致二义性和冲突。为避免这种情况,可以使用命名空间或显式实例化来区分模板类和非模板类。

3.6 类模板的高级用法

类模板的高级用法包括嵌套模板、模板模板参数(template template parameter)等。例如,使用模板模板参数实现一个通用的容器适配器:

template <typename T, template <typename> class Container = std::deque>
class Stack {
private:
    Container<T> elements;

public:
    void push(T const& element) {
        elements.push_back(element);
    }

    void pop() {
        elements.pop_back();
    }

    T top() const {
        return elements.back();
    }

    bool isEmpty() const {
        return elements.empty();
    }
};

上述代码定义了一个通用的Stack类模板,可以使用不同的容器类型(如std::dequestd::vector等)作为底层存储。 

结论

通过函数模板和类模板,C++提供了强大的泛型编程能力,使得代码可以更加通用和复用。在实际编程中,合理地使用模板可以显著提高代码的质量和维护性。希望通过本文的讲解,大家能够对C++模板有一个全面的理解,并能够在自己的项目中灵活应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1879723.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

目前常见的几款企业级im即时通讯软件有哪些?

在目前的市场上&#xff0c;有几款常见的企业级即时通讯软件广受企业青睐。以下是其中几款主流企业级即时通讯软件的介绍和特点。 1. 微软Teams 微软Teams是一款集即时通讯、协作和视频会议功能于一体的企业级通讯软件。它提供了实时聊天、语音通话、视频会议、文件共享和团队…

账号和权限的管理1

文章目录 修改用户账号的属性usermod格式常用选项 用户账号的初始化配置文件文件来源主要的用户初始配置文件 组账号文件添加组账号groupadd格式常用选项其他选项 删除组账号groupdel格式 查询账号信息groups格式 id格式 finger格式 W、who、users格式 文件/目录的权限和归属访…

整车功能开发

整车功能开发 站在前人的肩膀上&#xff0c;从系统功能架构集成角度梳理下整车功能开发相关内容 1、整车功能开发相关文件介绍 1.1 配置表 上面的表格&#xff0c;是一种车辆特性的表达方式&#xff0c;其实比较传统&#xff0c;我们称之为配置表&#xff08;Feature list&a…

Python使用defaultdict简化值为list的字典

原始代码&#xff1a; from typing import Dictrelated_objects_for_fetch: Dict[str, list] {}for key, value in [(k1, v1), (k1, v2), (k2, v2), (k3, v3), (k2, v2)]:if key not in related_objects_for_fetch:related_objects_for_fetch[key] []if value not in (value…

AH1117-3.3芯片使用记录

今天在新做好的电路板上测试电源时发现一个问题&#xff0c;那就是散热端不能接地&#xff0c;接地了就好像短路一样&#xff0c;芯片会热的厉害&#xff0c;当我把该引脚与地的所有连接都切断后&#xff0c;短路现象消失&#xff0c;特此记录一下&#xff0c;防止自己下次再犯…

Shell脚本编程 — Shell Script

Shell脚本编程 — Shell Script 基本概念示例脚本示例1&#xff1a;备份文件示例2&#xff1a;监控磁盘使用情况示例3&#xff1a;批量重命名文件 运行脚本提示 Shell脚本是一种编程语言&#xff0c;主要用于在Unix/Linux系统中自动化执行任务。它通过编写一系列的命令来完成特…

49 - 列出指定时间段内所有的下单产品(高频 SQL 50 题基础版)

49 - 列出指定时间段内所有的下单产品 -- 指定2020年2月的方法 -- (1) order_date between 2020-02-01 and 2020-02-29 -- (2) order_date like 2020-02% -- (3) DATE_FORMAT(order_date, "%Y-%m") "2020-02" -- (4) LEFT(order_date, 7) 或 subst…

AI Agent:技术原理与未来趋势

在人工智能的快速发展中&#xff0c;AI Agent作为一项创新技术&#xff0c;正逐渐成为研究和应用的热点。AI Agent不仅仅是执行命令的程序&#xff0c;它们能够感知环境、做出决策并采取行动&#xff0c;展现出类似人类的群体协作能力。本文将探讨AI Agent的技术原理、开源框架…

解决idea中git无法管理项目中所有需要管理的文件

点击文件->设置 选择版本控制—>目录映射 点击加号 设置整个项目被Git管理

淘宝天猫小学升级复习学习资料怎么领取大额优惠券返利购物更省钱?

暑假是一个很好的机会来回顾和巩固过去一年里学到的知识。通过复习学习资料&#xff0c;孩子可以加深对基础知识的理解和记忆&#xff0c;为下一学年的学习打下坚实的基础。对于即将升入更高年级的孩子来说&#xff0c;他们可能会面临更加复杂和深入的学习内容。购买升学复习资…

图灵虚拟机配置

导入虚拟机 点击新建&#xff0c;选择虚拟硬盘文件 环境机器.vmdk 配置网络

pcap包常见拆分方法

文章目录 Wireshark 拆分流量包SplitCap使用简介魔数报错示例结果 在进行流量分析时&#xff0c;经常需要分析pcap流量包。但是体积过大的流量包不容易直接分析&#xff0c;经常需要按照一定的规则把它拆分成小的数据包。 这里统一选择cic数据集里的Thursday-WorkingHours.pcap…

10大内网安全管理系统!企业内网安全必备系统

内网安全管理系统对于维护企业网络安全至关重要&#xff0c;它们帮助监控、管理内部网络资源&#xff0c;防止数据泄露和安全威胁。以下是十款知名的内网安全管理系统。 1. 安企神终端安全管理系统 详细介绍&#xff1a; 安企神是针对企业内网安全需求设计的一款综合管理系统&…

HTML5的多线程技术:Web Worker API

Web Workers API 是HTML5的一项技术&#xff0c;它允许在浏览器后台独立于主线程运行脚本&#xff0c;即允许进行多线程处理。这对于执行密集型计算任务特别有用&#xff0c;因为它可以防止这些任务阻塞用户界面&#xff0c;从而保持网页的响应性和交互性。Web Workers在自己的…

计算机图形学笔记----矩阵

矩阵和标量的运算 ,则 矩阵与矩阵相乘 的矩阵A&#xff0c;的矩阵B。两矩阵&#xff0c;结果为的矩阵&#xff0c;第一个矩阵的列数必须和第二个矩阵的行数相同&#xff0c;否则不能相乘 &#xff0c;中的每个元素等于A的第i行所对应的矢量和B的第j列所对应的矢量进行矢量点…

Django 页面展示模型创建表的数据

1&#xff0c;添加视图函数 Test/app8/urls.py from django.shortcuts import render from .models import Userdef create_user(request):if request.method POST:username request.POST.get(username)email request.POST.get(email)# ... 获取其他字段的值# 创建用户实例…

新手练习项目 7:猜数字游戏

名人说&#xff1a;莫听穿林打叶声&#xff0c;何妨吟啸且徐行。—— 苏轼《定风波莫听穿林打叶声》 Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#xff09; 目录 一、项目描述二、项目实现三、项目步骤四、项目扩展方向 更多项目内容&#xff0c;请关注我、订…

源码学习:文件描述符

在进程描述学习中&#xff0c;扯到了max_fds&#xff0c;接着就联想到了日常运维中常见的ulimit参数、sysctl内核参数&#xff0c;原来以为max_fds与这些个关联性比较强&#xff0c;但经过一早上折腾以后&#xff0c;发现其实还是有一些差距的。但是在学习过程中&#xff0c;却…

java基于ssm+jsp 固定资产管理系统

1前台首页功能模块 固定资产管理系统&#xff0c;在系统首页可以查看首页、设备信息、论坛信息、我的、跳转到后台等内容&#xff0c;如图1所示。 图1前台首页功能界面图 注册&#xff0c;在注册页面可以填写用户名、密码、姓名、性别、头像、身份证、手机等详细内容&#xff…

ASP.NET CORE应用针对IIS有哪两种部署模式?

一、ASP.NET CORE Core Module IIS其实也是按照管道的方式来处理请求的&#xff0c;但是IIS管道和ASP.NET CORE中间件管道有本质的不同。对于部署在IIS中的Web应用来说&#xff0c;从最初接收到请求到最终将响应发出去&#xff0c;这段处理流程被细分为一系列固定的步骤&#x…