首页 > 编程语言 >【C++】 ——【模板初阶】——基础详解

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

时间:2024-06-30 18:00:14浏览次数:20  
标签:初阶 函数 编程 C++ 实例 泛型 模板

目录

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++模板有一个全面的理解,并能够在自己的项目中灵活应用。

标签:初阶,函数,编程,C++,实例,泛型,模板
From: https://blog.csdn.net/2303_77720864/article/details/140083210

相关文章

  • NzN的C++之路--拷贝构造函数&&赋值运算符重载
    目录Part1拷贝构造函数一、概念二、特征Part2赋值运算符重载一、运算符重载二、赋值运算符重载三、前置++和后置++重载Part3const成员Part4 取地址及const取地址操作符重载 Part1拷贝构造函数一、概念        拷贝构造函数:只有单个形参,该形参......
  • SSM-学情分析系统-56772(免费领源码+开发文档)可做计算机毕业设计JAVA、PHP、爬虫、APP
    学情分析系统摘 要随着科学技术的飞速发展,各行各业都在努力与现代先进技术接轨,通过科技手段提高自身的优势;对于学情分析系统当然也不能排除在外,随着网络技术的不断成熟,带动了学情分析系统,它彻底改变了过去传统的管理方式,不仅使服务管理难度变低了,还提升了管理的灵活性。这......
  • 基于SpringBoot的高校大学生学科竞赛管理系统+53135(免费领源码)可做计算机毕业设计JAVA
    springboot高校大学生学科竞赛管理系统的设计与实现摘 要随着互联网趋势的到来,各行各业都在考虑利用互联网将自己推广出去,最好方式就是建立自己的互联网系统,并对其进行维护和管理。在现实运用中,应用软件的工作规则和开发步骤,采用Java技术建设高校大学生学科竞赛管理系统。......
  • WPF TreeView 三层绑定模板
    在WPF应用程序中,使用TreeView来展示三层数据结构是一个常见的需求。为此,你需要定义适当的数据绑定和模板。以下是一个完整的示例代码,展示如何实现这一点。数据模型首先,我们需要定义三层数据模型。假设我们有三层数据结构:RootItem包含ChildItem,ChildItem又包含SubChildItem。pub......
  • 【C++】【MFC】MFC多文档框架
    相较于单文档架构,多文档基本架构则是有起码两个框架窗口,与其相对应的也会有两个文档类进行数据管理。参与架构的类:CMDIFrameWnd/CMDIChildWnd/CWinApp/CView/CDocument需要用的类:CDocTemplate(文档模板类)|->CMultiDocTemplate(多文档模板类)CDocM......
  • UE4 C++ 随机生成迷宫地图
    参考参考原理就是利用一个房间的三个方向(排除进入口)出口(可以减少,即设置墙壁),从而获得下一次房间生成的位置,其中涉及到对于多个房间重叠,生成结束后如何对缺口进行修补等功能实现RoomBaseActor该Actor类是后续创建房间的基类,如果想要固定所有房间形状即只改变出口个数,那么在该类......
  • C/C++ const 和 volatile 关键字要点总结
    const 和 volatile 是C/C++的两个关键字,各有不同的用途和要点。constconst 关键字用于声明常量,一旦声明为常量,其值就不能被修改。const 可以用于各种数据类型,也包括指针、函数参数、函数返回值和类成员函数。声明常量:声明为 const 的常量,在初始化后不能被修改。co......
  • C++ 面向对象高级开发 5、操作符重载与临时对象
    C++里面操作符就是一种函数。 任何成员函数都有一个this->pointer谁调用这个函数,就指向谁。      tempobject(临时对象)typename(); 没有名称即是临时对象。一般人少用,但标准库用的很多。   质疑精神,即使是标准库也有可以提高的地方。 ......
  • 【华为OD机试真题】224、欢乐的周末 | 机试真题+思路参考+代码分析(最新抽中CD卷)(C++、J
    文章目录一、题目......
  • C++系统相关操作4 - 获取CPU(指令集)架构类型
    1.关键词2.sysutil.h3.sysutil.cpp4.测试代码5.运行结果6.源码地址1.关键词关键词:C++系统调用CPU架构指令集跨平台实现原理:Unix-like系统:可以通过uname-m命令获取CPU架构类型。Windows系统:可以通过环境变量PROCESSOR_ARCHITECTURE获取CPU......