C++ | C++模板

简介: C++ | C++模板函数模板实例1(函数重载):​​typename​​关键字实例2(函数模板):类模板实例3:实例4:实例5(可变数组):强类型语言弱类型语言STL

C++ | C++模板
模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码。

模板是创建泛型类或者函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,使用的是模板的概念。

每个容器都有一个单一的定义,比如向量,我们可以定义许多不同类型的向量,比如​​vector​​​ <​​int​​​> 或​​vector​​​ <​​string​​>。

// type是函数所使用的数据类型的占位符名称,这个名称可以在函数定义中使用。
template <typename type> ret-type func-name(parameter list)
{
    // 函数主体
}

函数模板
​​​:类型参数可以有多个,以逗号’​​,​​​'分隔,类型参数列表以​​<>​​包围。

函数名(形参列表):​​ret-type func-name(parameter list)​​;
实例1(函数重载):

/*******************************************************************
 *   > File Name: func-overload.cpp
 *   > Create Time: 2021年09月20日  9:27:47
 ******************************************************************/
#include <iostream>
using namespace std;

inline int const& MAX(int const& a, int const& b)
{
    return a < b ? b : a; /* 返回两个int型数中的较大数 */
}

inline float const& MAX(float const& a, float const& b)
{
    return a < b ? b : a; /* 返回两个float型数中的较大数 */
}

inline char const& MAX(char const& a, char const& b)
{
    return a < b ? b : a; /* 返回两个char型数中的较大数 */
}

inline bool const& MAX(bool const& a, bool const& b)
{
    return a < b ? b : a; /* 返回两个bool型数中的较大数 */
}

int main(int argc, char* argv[])
{
    int i = 10;
    int j = 20;
    cout << "MAX(i, j): " << MAX(i, j) << endl;

    float d1 = 20.01;
    float d2 = 99.999;
    cout << "MAX(d1, d2): " << MAX(d1, d2) << endl;

    bool b1 = true;
    bool b2 = false;
    cout << "MAX(b1, b2): " << MAX(b1, b2) << endl;

    char s1 = 'A';
    char s2 = 'a';
    cout << "MAX(s1, s2): " << MAX(s1, s2) << endl;

    return 0;
}

编译、运行:

PS D:\2.SoftTools\cygwin64\home\fly\workSpace\cplusplus\day4> make
g++ -o func-overload func-overload.cpp -g -Wall
PS D:\2.SoftTools\cygwin64\home\fly\workSpace\cplusplus\day4> .\func-overload.exe
MAX(i, j): 20
MAX(d1, d2): 99.999
MAX(b1, b2): 1
MAX(s1, s2): a

通过函数重载定义四个名字相同、参数列表不同的函数。本质上来说,定义了三个功能相同、函数体相同的函数,只是数据类型不同而已。

​​通过函数模板,我们可以将四个函数压缩成一个函数。​​

数据的值,可以通过函数参数传递,在函数定义时数据的值是未知的,只有等到函数调用时接收了实参才能决定其值,这就是值的参数化。

在C++中,数据的类型也可以通过参数来传递,在函数定义时可以不指定具体的数据类型,当发生函数调用时,编译器可以根据传入的参数自动推断数据类型。这就是类型的参数化。

值(​​value​​)和类型(​​type​​)是数据的两个主要特征,它们在C++中可以被参数化。

函数模板(​​Function​​),实际是建立了一个通用函数,它所用到的数据类型(包括返回值类型、形参类型、局部变量类型)可以不具体指定,而是用一个虚拟的类型来代替(标识符来占位),等到函数调用时再根据传入的实参来逆推出真正的类型。

​​typename​​关键字
也可以使用​​class​​​关键字代替,最开始使用​​class​​​来指明类型参数,后续才引入了​​typename​​关键字。

实例2(函数模板):

/*******************************************************************
 *   > File Name: func-template.cpp
 *   > Create Time: 2021年09月20日  9:19:33
 ******************************************************************/
#include <iostream>
#include <string>
using namespace std;

template <typename T> /* 模板头 */
inline T const& MAX(T const& a, T const& b) /* 函数头 */
{
    return a < b ? b : a; /* 返回较大的值 */
}

int main(int argc, char* argv[])
{
    int i = 10;
    int j = 20;
    cout << "MAX(i, j): " << MAX(i, j) << endl;

    double d1 = 20.01;
    double d2 = 99.999;
    cout << "MAX(d1, d2): " << MAX(d1, d2) << endl;

    string s1 = "ABCD";
    string s2 = "abcd";
    cout << "MAX(s1, s2): " << MAX(s1, s2) << endl;

    return 0;
}

编译、运行:

PS D:\2.SoftTools\cygwin64\home\fly\workSpace\cplusplus\day4> make
g++ -o func-template func-template.cpp -g -Wall
PS D:\2.SoftTools\cygwin64\home\fly\workSpace\cplusplus\day4> .\func-template.exe
MAX(i, j): 20
MAX(d1, d2): 99.999
MAX(s1, s2): abcd

类模板

template <typename type-param1,typename type-param2,...>
class class-name
{
    //TODO:
};

实例3:

/*******************************************************************
 *   > File Name: template-class.cpp
 *   > Create Time: 2021年09月20日 10:52:37
 ******************************************************************/
#include <iostream>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <stdexcept>
using namespace std;

// 定义了类Stack<>,实现了范型方法对元素进行出、入栈的操作
template <class T>
class Stack
{
private:
    vector<T> elems; // 元素
protected:
public:
    void push(T const&); // 入栈
    void pop();          // 出栈   
    T top() const;       // 返回栈顶元素
    bool empty() const{
        return elems.empty();
    };
};

template <class T> /* 模板头 */
void Stack<T>::push(T const& elem) /* 函数头 */
{
    elems.push_back(elem);
}

template <class T> /* 模板头 */
void Stack<T>::pop()/* 函数头 */
{
    if(elems.empty()){
        throw out_of_range("Stack<>::top(): empty stack");
    }

    elems.pop_back();
}

template <class T> /* 模板头 */
T Stack<T>::top() const/* 函数头 */
{
    if(elems.empty()){
        throw out_of_range("Stack<>::top(): empty stack");
    }

    return elems.back();
}

int main(int argc, char* argv[])
{
    try{
        Stack<int> intStack;
        Stack<string> stringStack;

        intStack.push(7);
        cout << intStack.top() << endl;

        stringStack.push("hello");
        cout << stringStack.top() << std::endl;
        stringStack.pop();
        stringStack.pop();

    }catch(exception const& ex){
        cerr << "Exception" << ex.what() << endl;
        return -1;
    }

    return 0;
}

编译、运行:

PS D:\2.SoftTools\cygwin64\home\fly\workSpace\cplusplus\day4> make
g++ -o template-class template-class.cpp -g -Wall
PS D:\2.SoftTools\cygwin64\home\fly\workSpace\cplusplus\day4> .\template-class.exe
7
hello
ExceptionStack<>::top(): empty stack
/*******************************************************************
 *   > File Name: template-class1.cpp
 *   > Create Time: 2021年09月20日 13:29:05
 ******************************************************************/
#include <iostream>
using namespace std;

template<class T1, class T2> /* 模板头 */
class Point{ /* 声明一个类Point */
public:
    Point(T1 x, T2 y):m_x(x), m_y(y){} /* 构造函数 */
public:
    T1 getX() const; /* 获取x坐标 */
    void setX(T1 x); /* 设置x坐标 */
    T2 getY() const; /* 获取y坐标 */
    void setY(T2 y); /* 设置y坐标 */
private:
    T1 m_x; // x坐标
    T2 m_y; // y坐标
};

template<class T1, class T2> /* 模板头 */
T1 Point<T1, T2>::getX() const /* 函数头,设置x坐标 */{ 
    return m_x;
}

template<class T1, class T2>
void Point<T1, T2>::setX(T1 x){ /* 设置x坐标 */
    m_x = x;
}

template<class T1, class T2>
T2 Point<T1, T2>::getY() const{ /* 设置y坐标 */
    return m_y;
}

template<class T1, class T2>
void Point<T1, T2>::setY(T2 y){ /* 设置x坐标 */
    m_y = y;
}

int main(int argc, char* argv[])
{
    Point<int, int> p1(10, 20); // 和函数模板不同,在实例化需要显示指明数据类型
    cout << "x = " << p1.getX() << ", y = " << p1.getY() << endl;

    Point<int, char *>p2(10, "东经180度");
    cout << "x = " << p2.getX() << ", y = " << p2.getY() << endl;

    /* 赋值号两边都要指明具体的数据类型,且保持一致 */
    Point<char*, char*> *p3 = new Point<char *, char *>("东经180度", "北纬210度");
    cout << "x = " << p3->getX() << ", y = " << p3->getY() << endl;

    return 0;
}

实例5(可变数组):

/*******************************************************************
 *   > File Name: template-class-arr.cpp
 *   > Create Time: 2021年09月20日 15:10:16
 ******************************************************************/
#include <iostream>
#include <cstring>
using namespace std;

template <class T> /* 模板头 */
class Carray{ /* Carray类 */
    int size; // 数组元素的个数
    T *ptr;   // 指向动态分配的数组
public:
    Carray(int n = 0); // n表示数组个数
    Carray(Carray& a);
    ~Carray(); /* 析构函数 */

    void push_back(const T& a); /* 加入新的元素 */
    Carray& operator=(const Carray& a); /* 重载操作符= */
    T length() {return size;}; /* 获取数组的大小 */  
    T& operator[](int i) /* 重载[],支持下标访问 */
    {
        return ptr[i];
    } 
};

template <class T>
Carray<T>::Carray(int n):size(n)/* 为类成员size赋值为n */
{
    if(0 == n){ /* 数组元素为0 */
        ptr = NULL;
    }else{
        ptr = new T[n];/* 申请空间 */
    }
}

template <class T>
Carray<T>::Carray(Carray& a)
{
    if(!a.ptr){ /* a.ptr为NULL,重置可变数组 */
        ptr = NULL;
        size = 0;
        return;
    }

    ptr = new T[a.size]; /* 申请空间 */
    memcpy(ptr, a.ptr, sizeof(T)*a.size); /* 复制a.ptr到ptr */
    size = a.size; /* 给size赋值 */
}

template <class T>
Carray<T>::~Carray()
{
    if(ptr){
        delete [] ptr; /* 释放ptr所指向的数组空间 */
    }
}

template <class T>
Carray<T> & Carray<T>::operator=(const Carray& a)
{
    if(this == &a){ // 防止a=a的情况导致出现的错误
        return *this;
    }
    
    if(a.ptr == NULL){
        if(ptr){ /* 不为空 */
            delete [] ptr; /* 释放ptr指向的数组空间 */
        }

        ptr = NULL;
        size = 0; /* 让数组的个数为0 */
        return *this;
    }
    if(size < a.size){ /* 空间不够大 */
        if(ptr){/* 不为空 */
            delete [] ptr;/* 释放ptr指向的数组空间 */
        }
        ptr = new T[a.size]; /* 申请空间 */
    }
    memcpy(ptr, a.ptr, sizeof(T)*a, size); /* 拷贝a.ptr的数据到ptr */
    size = a.size;
    return *this;
}

template <class T>
void Carray<T>::push_back(const T& v)
{
    if(ptr)
    {
        T* tmPtr = new T[size + 1]; /* 重新分配空间 */
        memcpy(tmPtr, ptr, sizeof(T)*size); /* 拷贝原数组内容 */
        delete []ptr; /* 释放原数组的空间 */
        ptr = tmPtr; /* ptr指向新的位置 */
    }
    else /* 数组本来是空的 */
    {
        ptr = new T[1];/* 申请一个元素的空间 */
    }

    ptr[size ++ ] = v; /* 加入新的数组元素 */
}

int main(int argc, char* argv[])
{
    Carray <int> a;
    for(int i = 0; i< 5; ++i){
        a.push_back(i); /* 赋值 */
    }

    for(int i = 0; i< a.length(); ++i){
        cout << a[i] << " "; /* 打印、输出 */
    }
    cout << endl;

    return 0;
}
PS D:\2.SoftTools\cygwin64\home\fly\workSpace\cplusplus\day4> make
g++ -o template-class-arr template-class-arr.cpp -g -Wall                                             
PS D:\2.SoftTools\cygwin64\home\fly\workSpace\cplusplus\day4> .\template-class-arr.exe
0 1 2 3 4

强类型语言
强类型语言在定义变量时需要显式地指明数据类型,并且一旦为变量指明了某种数据类型,该变量以后就不能赋予其他类型的数据了,除非经过强制类型转换或隐式类型转换。

弱类型语言
弱类型语言在定义变量时不需要显式地指明数据类型,编译器(解释器)会根据赋给变量的数据自动推导出类型,并且可以赋给变量不同类型的数据。

​​不管是强类型语言还是弱类型语言,在编译器(解释器)内部都有一个类型系统来维护变量的各种信息。​​

STL
STL​(Standard Template Library,标准模板库)就是 C++ 对数据结构进行封装后的称呼。

C++模板和泛型程序设计

​​C++模板:函数、结构体、类 模板实现​​

目录
相关文章
|
2月前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
112 10
|
4月前
|
编译器 C++
【C++】——初识模板
【C++】——初识模板
【C++】——初识模板
|
25天前
|
安全 编译器 C++
【C++11】可变模板参数详解
本文详细介绍了C++11引入的可变模板参数,这是一种允许模板接受任意数量和类型参数的强大工具。文章从基本概念入手,讲解了可变模板参数的语法、参数包的展开方法,以及如何结合递归调用、折叠表达式等技术实现高效编程。通过具体示例,如打印任意数量参数、类型安全的`printf`替代方案等,展示了其在实际开发中的应用。最后,文章讨论了性能优化策略和常见问题,帮助读者更好地理解和使用这一高级C++特性。
41 4
|
25天前
|
算法 编译器 C++
【C++】模板详细讲解(含反向迭代器)
C++模板是泛型编程的核心,允许编写与类型无关的代码,提高代码复用性和灵活性。模板分为函数模板和类模板,支持隐式和显式实例化,以及特化(全特化和偏特化)。C++标准库广泛使用模板,如容器、迭代器、算法和函数对象等,以支持高效、灵活的编程。反向迭代器通过对正向迭代器的封装,实现了逆序遍历的功能。
34 3
|
5月前
|
程序员 C++
C++模板元编程入门
【7月更文挑战第9天】C++模板元编程是一项强大而复杂的技术,它允许程序员在编译时进行复杂的计算和操作,从而提高了程序的性能和灵活性。然而,模板元编程的复杂性和抽象性也使其难以掌握和应用。通过本文的介绍,希望能够帮助你初步了解C++模板元编程的基本概念和技术要点,为进一步深入学习和应用打下坚实的基础。在实际开发中,合理运用模板元编程技术,可以极大地提升程序的性能和可维护性。
|
28天前
|
编译器 C++
【c++】模板详解(1)
本文介绍了C++中的模板概念,包括函数模板和类模板,强调了模板作为泛型编程基础的重要性。函数模板允许创建类型无关的函数,类模板则能根据不同的类型生成不同的类。文章通过具体示例详细解释了模板的定义、实例化及匹配原则,帮助读者理解模板机制,为学习STL打下基础。
31 0
|
2月前
|
编译器 程序员 C++
【C++打怪之路Lv7】-- 模板初阶
【C++打怪之路Lv7】-- 模板初阶
18 1
|
2月前
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
60 0
C++入门6——模板(泛型编程、函数模板、类模板)
|
2月前
|
算法 编译器 C++
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
【C++篇】领略模板编程的进阶之美:参数巧思与编译的智慧
88 2
|
2月前
|
存储 编译器 C++
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
45 2