重学C++系列之模板

news2024/9/28 17:35:31

一、什么模板

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

二、模板有几种

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

        函数模板的语法:

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


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

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

        类模板的语法


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

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

    
}

三、模板的特性

        1、函数模板

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

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

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

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

        2、类模板

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

四、使用场景

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

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

五、案例

        1、函数模板的简单使用

#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、类模板的简单使用

#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、基类为模板类,派生类为普通类

#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、基类为模板类,派生类也为模板类

#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++设计模式的单例模式

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

#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++系列之智能指针》。

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

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

相关文章

无涯教程-jQuery - hide( )方法函数

如果显示了 hide()方法&#xff0c;它们只是隐藏每个匹配元素集。此方法还有另一种形式&#xff0c;可以控制动画的速度。 hide( ) - 语法 selector.hide( ); hide( ) - 示例 以下是一个简单的示例&#xff0c;简单说明了此方法的用法- <html><head><title…

软件测试人员一定要会的用例设计思路

职场新人对测试用例的困惑无非有以下几点 1、什么是测试用例&#xff0c;为什么要写测试用例&#xff1f; 2、不知道怎么写&#xff0c;写了也不知道写的是否完整。 一、什么是测试用例&#xff1f; 百科的释义&#xff1a; 测试用例是对一项特定的软件产品进行测试任务的…

day45-Netflix Mobile Navigation(左边侧边栏动态导航)

50 天学习 50 个项目 - HTMLCSS and JavaScript day45-Netflix Mobile Navigation&#xff08;左边侧边栏动态导航&#xff09; 效果 index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name&…

pinia在vue3中的使用

总结&#xff1a; 在store文件夹中建一个pinia的文件userStore.js 1.要想使用pinia必须先引入defineStore 这里我们使用es6的模块化语法导出的 import { defineStore } from pinia 2.然后使用export const useUserStore defineStore(user,{}) defineStore 方法有两个参数&…

NAT协议(网络地址转换协议)详解

NAT协议&#xff08;网络地址转换协议&#xff09;详解 为什么需要NATNAT的实现方式静态NAT动态NATNAPT NAT技术的优缺点优点缺点 NAT协议是将IP数据报头中的IP地址转换为另外一个IP地址的过程&#xff0c;主要用于实现私有网络访问公有网络的功能。这种通过使用少量的IP地址代…

机器视觉系统组成,你知道多少?

机器视觉系统是一个复杂而高效的技术体系&#xff0c;它的组成主要包括以下几个核心部件&#xff1a; 相机和镜头&#xff1a;相机是机器视觉系统的眼睛&#xff0c;用于捕捉被测物的图像。镜头是相机的重要组成部分&#xff0c;它可以调节焦距、光圈和通光量&#xff0c;帮助获…

第3章 配置与服务

1 CoreCms.Net.Configuration.AppSettingsHelper using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.Json; namespace CoreCms.Net.Configuration { /// <summary> /// 【应用设置助手--类】 /// <remarks> /// 摘要&#x…

LLVM(2)IR入门

1 不支持类型的隐式转换 int factorial(int val);int factorial(int val) {if (val < 2)return 1;return factorial(val - 1) factorial(val - 2); }int main(int argc, char **argv) {return factorial(2) * 7 42; }生成IR代码 clang -emit-llvm -S t3.cpp -o t3.ll ;…

Android平台GB28181设备接入侧如何同时对外输出RTSP流?

技术背景 GB28181的应用场景非常广泛&#xff0c;如公共安全、交通管理、企业安全、教育、医疗等众多领域&#xff0c;细分场景可用于如执法记录仪、智能安全帽、智能监控、智慧零售、智慧教育、远程办公、明厨亮灶、智慧交通、智慧工地、雪亮工程、平安乡村、生产运输、车载终…

云原生架构

1. 何为云原生&#xff1f; 很多IT业内小伙伴会经常听到这个名词&#xff0c;那么什么是云原生呢&#xff1f;云原生是在云计算环境中构建、部署和管理现代应用程序的软件方法。 当今时代&#xff0c;众多企业希望构建高度可扩展、灵活且有弹性的应用程序&#xff0c;以便能够快…

Linux CentOS 8 编译安装Apache Subversion

前言 距离上一篇发表已经过去了5年零2个多月&#xff0c;这次重新开始写技术博客&#xff0c;理由和原来一样&#xff0c;也就是想把自己学习和工作中遇到的问题和知识记录下来&#xff0c;今天记录一下Linux CentOS 8通过编译安装svn的过程。 下载SVN 下载地址&#xff1a;…

使用frp中的xtcp映射穿透指定服务实现不依赖公网ip网速的内网穿透p2p

使用frp中的xtcp映射穿透指定服务实现不依赖公网ip网速的内网穿透p2p 管理员Ubuntu配置公网服务端frps配置service自启(可选) 配置内网服务端frpc配置service自启(可选) 使用者配置service自启(可选) 效果 通过frp实现内网client访问另外一个内网服务器 管理员 1&#xff09;…

PHP8的注释-PHP8知识详解

欢迎你来到PHP服务网&#xff0c;学习《PHP8知识详解》系列教程&#xff0c;本文学习的是《PHP8的注释》。 什么是注释&#xff1f; 注释是在程序代码中添加的文本&#xff0c;用于解释和说明代码的功能、逻辑或其他相关信息。注释通常不会被编译器或解释器处理&#xff0c;而…

深度学习实战44-Keras框架下实现高中数学题目的智能分类功能应用

大家好,我是微学AI ,今天给大家介绍一下深度学习实战44-Keras框架实现高中数学题目的智能分类功能应用,该功能是基于人工智能技术的创新应用,通过对数学题目进行智能分类,提供个性化的学习辅助和教学支持。该功能的实现可以通过以下步骤:首先,采集大量的高中数学题目数据…

Clion实现Stm32标准库-HAL库开发配置

1、配置CLion用于STM32开发&#xff08;基于hal库开发&#xff09; 原文链接 https://zhuanlan.zhihu.com/p/145801160 2、配置CLion用于STM32开发&#xff08;基于标准库开发&#xff09; 参考文章&#xff1a;https://www.bilibili.com/read/cv11442303

不再困扰!教你如何方便快捷地将文件转换成PDF格式!

大家都知道PDF是电脑中最常见且兼容性最好的一种文档文件格式&#xff0c;而这种格式的文件既能保留文档的原始布局&#xff0c;又能够方便阅读和打印。那么&#xff0c;当我们需要将其他格式的文件转换成PDF格式时应该如何处理呢&#xff1f;我们可以使用一款文档转换工具&…

【QT 网络云盘客户端】——获取用户文件列表信息

目录 1.获取用户文件列表信息分析 2.设置图标属性 3.向服务器获取文件的数量 4.向服务器获取文件信息列表 4.显示图标 1.获取用户文件列表信息分析 1.将QListWidget设置为图标模式 2. 当我们点击"按下载量升序","按下载量降序",“更新” 菜单选项 都会…

3、HAproxy高级配置

基于cookie的会话保持 在 HAProxy 中&#xff0c;可以通过使用 cookie 配置来实现基于 Cookie 的会话保持。cookie 配置用于配置与会话保持相关的选项&#xff0c;允许您定义要在HTTP响应中插入或重写的Cookie以及其他与Cookie会话保持相关的参数。 以下是一些常用的 cookie 配…

已解决 IDEA Maven 项目中 “Could not find artifact“ 问题的常见情况和解决方案

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

QT第四讲

思维导图 基于QT的网络聊天室 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTcpServer> //服务器类 #include<QTcpSocket> //客户端类 #include<QMessageBox> //对话框类 #include<QList…