C++函数模板、特例化、非类型参数、类模板、allocator

函数模板

模板对类型能进行参数化成【模板参数】,输入的是类型,生成的是代码。使用的时候,每指定一份类型,模板就会根据类型生成一份新的代码(比如函数模板实例化生成的是【模板函数】),有利于减少代码量,通过较少的代码也能实现函数重载。

调用函数模板的时候,一般通过<>传入【模板参数】,也就是【类型参数】。编译器生成相应的函数代码之后,再通过()传入实参。

模板的实参推演:调用模板的时候可以根据用户传入的实参的类型,推导出模板类型参数的具体类型。

以下是一个简单的 C++ 函数模板案例,用于交换两个值:

#include <iostream>
// 定义一个函数模板,用于交换两个值的内容
template <typename T>// T是一个模板参数,它表示一个占位符,一个模板参数意味着一个模板需要接收一个类型。如果有多个模板参数,则需要接收多种类型。
void swapValues(T &a, T &b) {T temp = a;a = b;b = temp;
0}int main() {int x = 5, y = 10;double m = 3.14, n = 6.28;std::cout << "Before swapping: x = " << x << ", y = " << y << std::endl;swapValues<int>(x, y);//在函数调用点,编译器根据用户指定的类型,从原模板实例化一份代码出来,如果已经实例化代码了,则无需该代码实例化。再根据这个实例化的代码,传值参数。std::cout << "After swapping: x = " << x << ", y = " << y << std::endl;std::cout << "Before swapping: m = " << m << ", n = " << n << std::endl;swapValues(m, n);//模板的实参推演std::cout << "After swapping: m = " << m << ", n = " << n << std::endl;return 0;
}

注意:模板一般在头文件中定义,在源文件中进行#include包含使用。尽量不要在一个cpp文件中定义,在另一个cpp文件中使用。

模板特例化

函数模板特例化允许我们为特定类型或特定类型组合提供自定义的实现,以满足特殊需求或处理特定情况。

函数模板特例化与函数模板同名,于此同时也能和同名普通函数共存。

函数模板的特例化例子如下,细节是另起一个同名模板,但是关键字的使用方法有差异。

#include <iostream>
#include <cstring> // For strlen and strcpy// 通用模板函数
template <typename T>
void swapValues(T &a, T &b) {T temp = a;a = b;b = temp;
}// 模板特化,用于交换 const char* 类型的指针
template <>
void swapValues<const char*>(const char* &a, const char* &b) {// 计算字符串的长度size_t lengthA = std::strlen(a);size_t lengthB = std::strlen(b);// 创建临时缓冲区,用于交换字符串的内容char* temp = new char[lengthA + 1]; // +1 是为了容纳字符串末尾的空字符 '\0'// 拷贝字符串内容到临时缓冲区std::strcpy(temp, a);// 交换字符串指针delete[] a; // 释放原来指针 a 指向的内存a = new char[lengthB + 1]; // 为 a 分配新的内存空间std::strcpy(const_cast<char*>(a), b); // 使用 const_cast 将 const char* 转换为 char*,然后拷贝字符串内容到 adelete[] b; // 释放原来指针 b 指向的内存b = new char[lengthA + 1]; // 为 b 分配新的内存空间std::strcpy(const_cast<char*>(b), temp); // 使用 const_cast 将 const char* 转换为 char*,然后拷贝字符串内容到 b// 释放临时缓冲区delete[] temp;
}int main() {const char* str1 = "Hello";const char* str2 = "World";std::cout << "Before swapping: str1 = " << str1 << ", str2 = " << str2 << std::endl;swapValues(str1, str2);std::cout << "After swapping: str1 = " << str1 << ", str2 = " << str2 << std::endl;return 0;

函数模板的非类型参数

模板的非类型参数,必须是整数类型(整数或者地址/引用都可以)。只能使用,不能修改。

实际上就相当于传入了一个参数。

#include <iostream>// 函数模板,使用非类型参数来传递一个整数常量
template <int N>
void printNumber() {std::cout << "The number is: " << N << std::endl;
}int main() {// 使用函数模板,并传递常量值作为非类型参数printNumber<5>(); // 输出: The number is: 5printNumber<10>(); // 输出: The number is: 10return 0;
}

类模板

类模板的规则其实与函数模板一样,都是通过传入类型参数生成模板类代码之后再调用。

使用类模板的时候,模板名+类型参数列表才构成了模板名称。但是我们一般在构造和析构函数名的后面不加模板参数列表(比如<T>),其它出现模板名的地方都加上参数类型列表。

本人习惯先用简单的类型参数实现一个类,然后再把这个类改成类模板,

以下是一个使用整数类型的顺序栈(SeqStack)类的示例实现:

#include <iostream>
#include <vector>class SeqStack {
private:std::vector<int> elements;  // 用于存储栈元素的容器public:void push(const int& element) {elements.push_back(element);}void pop() {if (!elements.empty()) {elements.pop_back();}}int& top() {return elements.back();}bool empty() const {return elements.empty();}
};int main() {SeqStack stack;stack.push(10);stack.push(20);stack.push(30);std::cout << "Top element: " << stack.top() << std::endl;stack.pop();std::cout << "Top element after pop: " << stack.top() << std::endl;if (stack.empty()) {std::cout << "Stack is empty." << std::endl;} else {std::cout << "Stack is not empty." << std::endl;}return 0;
}

要将这个类改为类模板,需要进行以下修改:

在类定义前声明类模板,指定类型参数。例如,将类名改为 template <typename T> class SeqStack。

在类内部的成员函数和成员变量声明中,使用类型参数 T 代替具体的类型。例如,将 std::vector<int> 改为 std::vector<T>。其次在类内部使用类名的地方加上<T>(比如赋值函数的返回类型,拷贝构造函数和赋值函数的参数类型)。

在类外部定义成员函数时,也需要将函数模板化,并指定类型参数 T。例如,将 void push(const int& element) 改为 template <typename T> void SeqStack<T>::push(const T& element)。

修改后的代码示例如下:

#include <iostream>
#include <vector>template <typename T>
class SeqStack {
private:std::vector<T> elements;  // 用于存储栈元素的容器public:void push(const T& element) {elements.push_back(element);}void pop() {if (!elements.empty()) {elements.pop_back();}}T& top() {return elements.back();}bool empty() const {return elements.empty();}
};int main() {SeqStack<int> stack;stack.push(10);stack.push(20);stack.push(30);std::cout << "Top element: " << stack.top() << std::endl;stack.pop();std::cout << "Top element after pop: " << stack.top() << std::endl;if (stack.empty()) {std::cout << "Stack is empty." << std::endl;} else {std::cout << "Stack is not empty." << std::endl;}return 0;

allocator

我们调用类模板,传入自定的类型参数,如果模板里new了这个类型参数的数组。因为new的时候会初始化一个对象,所以会调用构造函数,数组大小为1000就创建了1000个类型对象并且调用了1000次该类型的构造函数,析构的时候就调用了1000次析构函数析构该数组。

为了避免这种问题,我们在构造函数里需要把内存开辟和对象构造分开。析构数组的时候需要只用析构容器有效的元素,然后释放数组的堆内存。删除数组元素的时候调用了对象的析构函数,释放元素对象占用的外部资源,而不是仅仅通过移动头尾指针表示删除了元素。

可以借用allocator来解决上述问题。Allocator做了四件事,内存开辟/内存释放,对象构造/对象析构。

C++中allocator的用法:

#include <iostream>
#include <memory> // For std::allocatorint main() {// 使用std::allocator分配和释放int类型的内存std::allocator<int> allocator;// 分配单个int对象的内存int* p = allocator.allocate(1);// 在分配的内存中构造对象allocator.construct(p, 42);// 使用分配的对象std::cout << *p << std::endl;// 销毁对象allocator.destroy(p);//释放数组内存allocator.deallocate(p, 1);return 0;
}

C++的容器模板都会把空间配置器写入模板代码中,防止重复开销。

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

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

相关文章

深度学习(16)--基于经典网络架构resnet训练图像分类模型

目录 一.项目介绍 二.项目流程详解 2.1.引入所需的工具包 2.2.数据读取和预处理 2.3.加载resnet152模型 2.4.初始化模型 2.5.设置需要更新的参数 2.6.训练模块设置 2.7.再次训练所有层 2.8.测试网络效果 三.完整代码 一.项目介绍 使用PyTorch工具包调用经典网络架构…

redis 主从模式,sentinel 模式配置

编辑 sentinel.xml 和 redis.conf redis.conf 中核心是配置 bind 192.168.64.144 daemonize yes protected-mode no dbfilename redis-6379.rdb #默认dump.rdb replica-read-only yes # 自动2.6副本默认只读&#xff0c;也就是slave只有只读权限 replicationOf myapplicat…

Win10/Win11复制粘贴必须刷新才能显示

问题描述&#xff1a; 新建文件夹或复制粘贴文件时&#xff0c;不管是在桌面还是在其他磁盘中都需要右击刷新一下才显示。比如新建一个文件夹不显示以为没有新建成功&#xff0c;导致重复建了好几个。 解决方法&#xff1a; 使用WinR打开运行&#xff0c;输入regedit&#x…

HCIE-Datacom证书有效期多久?HCIE考试有哪些内容?

如今越来越多的人开始关注并参与到华为认证的学习中来。 其中&#xff0c;华为认证数据通信专家(HCIE-Datacom)作为华为认证体系中的高级认证&#xff0c;备受瞩目。 那么&#xff0c;关于HCIE-Datacom证书的有效期以及HCIE考试的内容&#xff0c;你知道多少呢&#xff1f;下…

PCIe学习笔记(2)错误处理和AER/DPC功能

文章目录 PCIe ErrorAER (Advanced Error Reporting)DPC (Downstream Port Containment) 处理器上错误通常可分为detected和undetected error。Undetected errors可能变得良性(benign)&#xff0c;也可能导致系统故障如silent data corruptions (SDC)。Detected errors则又可分…

Flutter面试之渲染流程

Flutter面试中必问的一个面试题就是渲染相关的话题。作为Google在2018年发布的一款跨平台UI框架,使用Dart作为其开发语言,底层使用Skia图形库进行视图渲染,渲染速度和用户体验堪比原生。 Flutter渲染流程 总的来说,Flutter中一帧的渲染可以分为三个过程:请求渲染、绘制和…

MySQL基础学习

MySQL基础 注意&#xff1a;本文的图片截图自尚硅谷MySQL笔记。 一&#xff1a;基本概述&#xff1a; 什么是数据库&#xff1a; 数据库是一种用来存储和管理数据的系统。它是一个组织化的数据集合&#xff0c;可以通过计算机系统进行访问、管理和更新。数据库可以存储各种…

如何使用CloakQuest3r获取受安全服务保护的网站真实IP地址

关于CloakQuest3r CloakQuest3r是一款功能强大的纯Python工具&#xff0c;该工具可以帮助广大研究人员获取和查看受Cloudflare和其他安全服务商保护的网站真实IP地址。 Cloudflare是一种广泛采用的网络安全和性能增强服务&#xff0c;而CloakQuest3r的核心任务就是准确识别隐…

每天一个数据分析题(一百六十)

以下关于代码片段&#xff08;使用sklearn&#xff09;的使用和PCA&#xff08;主成分分析&#xff09;的描述中&#xff0c;哪项是正确的&#xff1f; A. preprocessing.scale(data)用于对数据进行归一化处理&#xff0c;确保PCA分析前各特征处于同一量级。 B. PCA(n_compon…

怎么在抖音带自己的货?带货方式和带货要求,如下所示

我是王路飞。 不管你是无货源的新手小白&#xff0c;还是有货源的厂家/供应链&#xff0c;想在抖音卖货的话&#xff0c;无非就两种方式&#xff1a;要么开店、要么开直播带货。 看似都是在抖音卖货&#xff0c;但其实这是两条不同的赛道。 这篇文章就给你们聊下想在抖音卖货…

C语言系列13——网络编程基础

网络编程是计算机编程的一个分支&#xff0c;它允许程序在网络中发送和接收数据。本文将深入探讨网络编程的基础&#xff0c;包括Socket编程入门、客户端与服务器端的通信&#xff0c;以及常见网络协议的应用。 目录 写在开始1 Socket编程的基本概念与原理1.1 什么是Socket&…

【Redis】理论进阶篇------Redis的持久化

一、前言 前面学习了Redis的相关的十大数据类型以及用SpringBoot集成我们的Redis的工具代码的书写。从这篇文章开始&#xff0c;就会从Redis相关的一些理论&#xff08;也是面试和工作的热点知识&#xff09;如&#xff1a;Redis的持久化、Redis的订阅发布模型、Redis集群环境搭…

用python绘制黄金价格变化曲线

首先你得从mt4把数据导出为csv&#xff1a;mt4如何导出数据-CSDN博客 1、引入必要的库 import numpy as np import pandas as pd import matplotlib.pyplot as plt 2、然后通过pandas载入csv数据 raw pd.read_csv("XAUUSDm1.csv", headerNone, index_colNone, p…

P1024 [NOIP2001 提高组] 一元三次方程求解题解

题目 有形如&#xff1a;这样的一个一元三次方程。给出该方程中各项的系数&#xff08;a,b,c,d均为实数&#xff09;&#xff0c;并约定该方程存在三个不同实根&#xff08;根的范围在−100至100之间&#xff09;&#xff0c;且根与根之差的绝对值≥1。要求由小到大依次在同一…

YOLOv5代码解读[01] train.py

# YOLOv5 🚀 by Ultralytics, GPL-3.0 license import argparse import math import os import random import sys import time from copy import deepcopy from datetime import datetime from pathlib import Path import numpy as np import yaml from tqdm import tqdmim…

文件包含+文件上传漏洞(图片马绕过)

目录 一.文件包含二.文件上传三.图片马四.题目 一.文件包含 将已有的代码以文件形式包含到某个指定的代码中&#xff0c;从而使用其中的代码或者数据&#xff0c;一般是为了方便直接调用所需文件&#xff0c;文件包含的存在使得开发变得更加灵活和方便&#xff08;若对用户输入…

网络安全-pikachu之文件上传漏洞1

文件上传漏洞是危害极大的&#xff0c;一旦成功&#xff0c;可以获取服务器的最高权限。 pikachu介绍&#xff1a; 文件上传功能在web应用系统很常见&#xff0c;比如很多网站注册的时候需要上传头像、上传附件等等。当用户点击上传按钮后&#xff0c;后台会对上传的文件进行判…

☀️将大华摄像头画面接入Unity 【1】配置硬件和初始化摄像头

一、硬件准备 目前的设想是后期采用网口供电的形式把画面传出来&#xff0c;所以这边我除了大华摄像头还准备了POE供电交换机&#xff0c;为了方便索性都用大华的了&#xff0c;然后全都连接电脑主机即可。 二、软件准备 这边初始化摄像头需要用到大华的Configtool软件&#…

【知识点】CNN中concat与add的区别

cat操作经常用于将特征联合&#xff0c;多个卷积特征提取框架提取的特征融合或者是将输出层的信息进行融合&#xff1b;而add层更像是信息之间的叠加。 add是在一个特征上增加其语义信息&#xff0c;对最终的图像的分类是有益&#xff1b;cat导致的结果改进可能是由于cat操作通…

工业自动化部署选择主板的关键因素

**在构建任何计算机时&#xff0c;选择合适的主板至关重要。**对于游戏台式机&#xff0c;您需要选择能够支持您玩的游戏类型而不会出现任何问题的最新和最佳规格。当涉及工业应用时&#xff0c;影响您决策的变量变得更加重要。作为任何基于计算的应用中最关键的组件之一&#…