重学C++系列之模板

一、什么模板

模板的引入跟泛型编程 有关,泛型编程指编写和编译时,对于参数的类型是一个不确定的类型,直到程序运行时,才能确定真正的类型。而泛型编程的实现主要通过函数模板和类模板。

二、模板有几种

模板有两种,函数模板和类模板。

函数模板的语法:

cpp 复制代码
语法:以关键字template指定一个函数为函数模板
在定义函数时,要指定泛型,指定泛型通常使用关键字 typename 或者 class 
泛型通常用大写字母T代表 泛型符号:<>


template<typename T>
函数的返回值 函数名(参数列表)
{
    函数体
}

备注:一个函数如果它是函数模板,不是所有的类型都是使用泛型。

类模板的语法

cpp 复制代码
// 类中成员变量类型和成员函数的参数的烈性不确定,在类创建对象时,才确定其类型

// 语法示例
template<class T>
class 类名
{
    // 类的定义
private:
    T 成员变量1;
public:
    类名(T a = T());    // 构造函数

    
}

三、模板的特性

1、函数模板

(1)函数模板在调用时,可以根据实参自动推导泛型,也可以显式指定泛型

(2)当普通函数和函数模板同时出现,如果普通函数有严格匹配,那么,优先调用普通函数

(3)函数模板也可以进行函数重载,并且泛型也可以设置默认参数。

(4)**函数模板经过实例化后生成的具体函数称为模板函数。**模板函数的编译机制是二次编译,对于模板函数而言,它真正的函数名是发生变化的,新函数名 = 原函数名 + <具体的类型>。

2、类模板

类中成员变量类型和成员函数的参数类型不确定,在类创建对象时,才确定其类型

四、使用场景

1、类模板的应用,可以用来设计容器 ,也可以用来在设计模式中

2、模板类也是一个类,它也可以用来做基类。

五、案例

1、函数模板的简单使用

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;


// 模板的声明必须要和函数的声明紧挨一起
template <typename T>
void show(T *a, int len)
{
    for(int i = 0; i < len; i++)
    {
        cout << a[i] << " ";
    }
    cout << endl;
}


int main()
{
    int a[5];
    for(int i = 0; i < 5; i++)
    {
        a[i] = i+1;
    }

    show(a, 5);

    show("hello", strlen("hello") + 1);

    return 0;
}

2、类模板的简单使用

cpp 复制代码
#include <iostream>
#include <cstring>

using namespace std;


template <class T>
class Test
{
private:
    T *array;
    int len;
public:
    Test(const T* array = T(), int len = 1)
    {
        if(len <= 0)
        {
            cout << "len is less than 0" << endl;
            return;
        }
        this->array = new T[len];
        this->len = len;
        for(int i = 0; i < len; i++)
        {
            this->array[i] = array[i];
        }
    }
    ~Test()
    {
        if(array != nullptr)
        {
            delete [] array;
            array = nullptr;
        }
    }
    void show()
    {
        if(array == nullptr || len == 0)
        {
            cout << "array is null" << endl;
            return;
        }
        for(int i = 0; i < len; i++)
        {
            cout << array[i] << " ";
        }
        cout << endl;
    }
};


int main()
{
    int a[5] = {1, 2, 3, 4, 5};

    
    Test<int> test1(a, 5);
    Test<char> test2((char*)"hello", strlen("hello") + 1);

    test1.show();
    test2.show();


    return 0;
}

3、基类为模板类,派生类为普通类

cpp 复制代码
#include <iostream>


using namespace std;

// 模板类
template <class T>
class Base  // 类名为 Base<T>
{
protected:
    T a;
public:
    Base(T a1 = T())
    {
        this->a = a1;
    }
};

// 派生类为普通类,此时模板类要指定具体数据类型
class Test:public Base<int>
{
private:
    int b;
public:
    // 必须显式调用构造函数初始化列表来初始化基类成员
    Test(int a = 0, int b = 0):Base<int>(a)
    {
        this->b = b;
    }
    
    // 重载输出流运算符函数
    friend ostream& operator << (ostream & output, const Test& other);
};


ostream& operator << (ostream & output, const Test& other)
{
    output << "a = " << other.a << endl;
    output << "b = " << other.b << endl;
    return output;
}


int main()
{
    Test test1(10, 100);
    cout << test1 << endl;


    return 0;
}

4、基类为模板类,派生类也为模板类

cpp 复制代码
#include <iostream>


using namespace std;

// 模板类
template <class T>
class Base  // 类名为 Base<T>
{
protected:
    T a;
public:
    Base(T a1 = T())
    {
        this->a = a1;
    }
};

// 派生类也为模板类
template <class T>
class Test:public Base<T>
{
private:
    T b;
public:
    // 必须显式调用构造函数初始化列表来初始化基类成员
    Test(T a = 0, T b = 0):Base<T>(a)
    {
        this->b = b;
    }
    
    void show()
    {
        cout << "a = " << this->a << endl;
        cout << "b = " << this->b << endl;
    }
};


int main()
{
    Test<int> test1(100, 1000);   // 必须显式声明数据类型
    Test<char*> test2((char*)"hello", (char*)"world");
    test1.show();
    test2.show();

    return 0;
}

5、C++设计模式的单例模式

单实例模型是指保证系统中,只有一个类对象,也就是说,在设计一个类的时候,该类的对象只能在一个系统中,保证只有一(该类不能在类的外部,通过构造函数来创建对象)比如:资源的管理者,老师等。

cpp 复制代码
#include <iostream>

using namespace std;


// 第一步:设计一个单例模式 --- 模板类
// 单例模式的目的就是为了得到泛型类型(T)对象的唯一,那么,就在泛型类型(T)类中创建对象的方式全部去禁止掉
// 只通过模板类的静态成员函数这个接口来获取泛型这个对象
// 该单例模式存在线程安全问题(已修改),对于CPU是一个多线程,当其中一个线程正在执行创建对象时,
// 但是这个对象还没有创建成功,当另外一个线程想要创建对象,发现对象还有,那么,它也会去创建这个对象
// 为了解决这个问题,那么就给互斥资源上锁。


bool mutex = true;  // 互斥变量

template<class T>
class SingleTon
{
private:
    static T *instance;
public:
    static T* getInstance()
    {
        if(instance == nullptr)
        {
            while(1)
            {
                if(mutex)
                {
                    mutex = false;  // 上锁
                    instance = new T;   // 相当于调用了无参的构造函数
                    mutex = true;   // 解锁
                    break;  // 退出
                }
            }
            
        }
        return instance;
    }
};



// 第二步:设计要实例化的类
class Admin
{
public:
    void showInfo()
    {
        cout << "name:\t" << name << endl;
        cout << "age:\t" << age << endl;
    }
private:
    string name;
    int age;

    // 把所有可以创建类对象的成员函数禁止访问,留下一个构造函数来创建唯一的对象
    // 而且要把构造函数进行私有化,通过类SingleTon的静态指针来调用
    Admin(const string &name = "admin", int age = 20)
    {
        this->name = name;
        this->age = age;
    }

    // 禁止使用拷贝构造函数
    Admin(const Admin& other) = delete;

    Admin& operator = (const Admin& other) = delete;

    

    friend class SingleTon<Admin>;  // 声明为友元类
}; 

template<class T>
T *SingleTon<T>::instance = nullptr;

int main()
{
    // 第三步,通过单例模式静态成员函数来获取唯一的对象
    Admin *p1 = SingleTon<Admin>::getInstance(); 
    Admin *p2 = SingleTon<Admin>::getInstance(); 

    // 不管调用多少次,p1, p2, ..., pn值都是一样的

    cout << "p1 = " << p1 << endl;
    cout << "p2 = " << p2 << endl;

    p1->showInfo();
    p2->showInfo();


    return 0;
}

六、总结

模板的引入是为了适应泛型编程,简单来讲就是编写一份功能相同的代码,只是操作的数据类型不同,此时采用泛型编程可以很好解决问题,C++中采用模板来实现。本篇还讲解了设计模式中的单例模式,更多的应用场景看另外一篇《重学C++系列之智能指针》。

相关推荐
娅娅梨20 分钟前
C++ 错题本--not found for architecture x86_64 问题
开发语言·c++
兵哥工控24 分钟前
MFC工控项目实例二十九主对话框调用子对话框设定参数值
c++·mfc
我爱工作&工作love我32 分钟前
1435:【例题3】曲线 一本通 代替三分
c++·算法
娃娃丢没有坏心思1 小时前
C++20 概念与约束(2)—— 初识概念与约束
c语言·c++·现代c++
lexusv8ls600h1 小时前
探索 C++20:C++ 的新纪元
c++·c++20
lexusv8ls600h1 小时前
C++20 中最优雅的那个小特性 - Ranges
c++·c++20
白-胖-子1 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-统计数字
开发语言·c++·算法·蓝桥杯·等考·13级
好睡凯1 小时前
c++写一个死锁并且自己解锁
开发语言·c++·算法
依旧阳光的老码农2 小时前
标准C++ 字符串
开发语言·c++
白-胖-子2 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-成绩排序
c++·算法·蓝桥杯·真题·蓝桥等考