广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++超详细讲解泛型
  • 646
分享到

C++超详细讲解泛型

2024-04-02 19:04:59 646人浏览 八月长安
摘要

目录1.了解泛型编程2.函数模板2.1简单示例2.2多个模板参数2.3模板实例化2.4模板和普通函数同时存在2.5函数模板不支持定义和声明分离3.类模板3.1简单示例3.2成员函数声

1.了解泛型编程

就好比活字印刷术,可以灵活调整印刷的板块和内容,比只能固定印刷某一个内容的雕版印刷术效率更高,也让印刷术由此得到了更广泛的应用。

c++中,函数重载和模板的出现,让泛型编程得到了实际的应用。其中模板,就是类似活字印刷术一样的存在。

2.函数模板

八八了那么多没用的,让我们来看看函数模板的语法实现吧

2.1简单示例

下面是一个最简单的交换函数的例子,通过标明模板参数T,让编译器自动识别函数传参,并调用出不同的函数

template<typename T>
void Swap(T& left,T& right)
{
    T temp = left;
    left = right;
    right = temp;
}

其中,typename是定义模板的关键字,我们可以使用class来替代,但不能使用struct

可以看到,编译器成功调用了Swap函数,交换了int类型和double类型

2.2多个模板参数

如果我们尝试把int和double同时传参给这个函数,会发生什么呢?

编译器会报错,表示模板参数T不明确

这时候我们有几种解决方法

首先是将double强转为int(反过来亦可)

你会发现还是不行,那是因为强转并不支持用double引用int。所以我们把函数传参中的引用去掉,即可正常调用这个函数(暂且不提传引用和传值的区别)

使用多个模板参数

和函数传参类似,我们也可以设置多个模板参数

在下图中,我使用typeid关键字来打印模板参数T1和T2的类型。

使用typeid需要包含头文件#include <typeinfo>

可以看到,实际上函数在调用这个模板的时候,已经实例化了这个函数(即替换模板参数为正确参数类型)这时候在后台处理的时候,其实Show函数已经实例化为了下面这个样子

void Show(int left, double right)
{
    cout << typeid(left).name() << endl;
    cout << typeid(right).name() << endl;
}

2.3模板实例化

上面的方式,是编译器自动帮我们实例化模板参数。在实际使用中,我们还可以自己指定实例化为什么类型

  • 利用强制类型转换
  • 使用<int>直接指定实例化为int类型

使用第二种方式的时候,编译器会对另外一个不匹配的参数进行隐式类型转换。如果转换不成功,则会报错。

另外注意的是,函数模板参数T同样可以用来作为返回值,但是不能通过返回值来推断参数T的类型。比如下面这个函数,我们在使用的时候就需要直接指定模板参数T,而不能写一个int* ptr=test(10)让编译器通过“返回值是int*接收的,所以函数模板参数T是int”来推断。

template<typename T>
T* test(int num)
{
	return new T[num];
}

函数模板支持给予参数缺省值

当一个参数不确定的时候,函数模板是支持给予缺省值的

template<typename T=char>
T* test(int num)
{
	return new T[num];
}

比如这样,当我们没有直接指定的时候,编译器就会将T作为char类型,返回一个num大小的char(一个字节)的空间

注意:当有多个模板参数时,缺省值需要从右往左给

函数模板的传参也支持缺省值

template<typename T1>
void Add(T1 left, T1 right=10)
{
    cout << "Add temp "<<typeid(left).name() << " " << typeid(right).name() << endl;
    cout << left + right << endl << endl;
}
int main()
{
    int a=1;
    Add(a);
}

在这种情况下,编译器会正确调用该函数模板

2.4模板和普通函数同时存在

以Add函数为例,在函数模板存在的同时,我们还可以单独写一个int类型的add函数。这都归功于函数重载的存在。

同时,我们还可以使用<int>来指定函数模板重载为已存在的Add函数。因为本质上这两个函数是不同的,并不会冲突。

函数在调用的时候,首先会去调用已经存在的函数。当参数和已存在的函数不匹配时,才会调用函数模板

2.5函数模板不支持定义和声明分离

一般情况下,我们都会在头文件中生命函数,在另外一个源文件中定义函数。

但是模板是不支持这么做的!编译器会报错 链接错误

error LNK2019:无法解析的外部符号……

所以我们需要将函数模板的声明和定义放在一个头文件中。在部分使用场景,会使用.hpp来表示这个头文件是包含了函数定义的(即.h和.cpp集合体)。需要注意,这并不是一个硬性要求,你也可以直接使用.h,并将声明和定义放入其中。

这是为什么呢?

因为单独的.h声明会在源文件顶部展开,而此时函数模板正常推演参数,但编译器并没有找到函数的实现,即这是一个没有地址的函数。从而导致编译器找不到函数的地址,产生了符号表的链接错误

有无解决办法?

其实是有的,我们可以在模板函数定义的.cpp中对我们需要使用的函数进行显式实例化指定

//头文件
//声明
template<typename T1>
void Add(T1 left, T1 right);
//源文件
//定义
template<typename T1>
void Add(T1 left, T1 right)
{
   cout << left + right << endl << endl;
}
//在源文件中显式实例化
template
void Add<int>(int left, int right);
template
void Add<double>(double left, double right);

显式实例化需要对我们要用的所有函数进行实例化,比如你需要用double类型,只显示实例化了int类型是不行的,依旧会报错。

这样感觉非常多余……对吧!所以还是老老实实把声明和定义放在同一个文件里面吧!

3.类模板

类模板的基本形式如下,这里作为一个小区分,我用class来当作模板参数名。实际上typename也是可以的

template<class T1, class T2, ...>class 类模板名{<!--{C}%3C!%2D%2D%20%2D%2D%3E-->// 类内成员定义}; template<class T1, class T2, ...>
class 类模板名
{
// 类内成员定义
};     

3.1简单示例

下面用一个非常简单的顺序表代码来演示一下类模板

template<class T>
class List
{
public:
    List(int capacity = 10)
        : _a(new T[capacity])
        , _size(0)
        , _capa(capacity)
    {}
 
    ~List();
    T& operator[](int pos)
    {
        assert(pos < _size);
        return _a[pos];
    }
private:
    T* _a;
    int _size;
    int _capa;
};
//类模板中函数放在类外进行定义时,需要加模板参数列表
template <class T>
List<T>::~List()
{
    delete[] _a;
    _size = _capa = 0;
}

可以看到,通过显式实例化的方式,我们成功让这个类模板变成了两个不同类型的顺序表

3.2成员函数声明和定义分离

其中需要注意的是析构函数,声明和定义分离的时候(同一文件),在定义的时候也需要加上模板参数

//类模板中函数放在类外进行定义时,需要加模板参数列表
template <class T>
List<T>::~List()
{
    delete[] _a;
    _size = _capa = 0;
}

个人觉得这样也非常麻烦,既然模板最好是声明和定义放在同一个文件,那还不如直接将类的成员函数直接定义到类内部。多省事!

如果是声明和定义放在不同文件中,显式实例化方式如下

template
class List <int>;
template
class List <double>;

需要什么类型的类,就得实例化这个类型。

到此这篇关于C++超详细讲解泛型的文章就介绍到这了,更多相关C++泛型内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C++超详细讲解泛型

本文链接: https://www.lsjlt.com/news/153139.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C++超详细讲解泛型
    目录1.了解泛型编程2.函数模板2.1简单示例2.2多个模板参数2.3模板实例化2.4模板和普通函数同时存在2.5函数模板不支持定义和声明分离3.类模板3.1简单示例3.2成员函数声...
    99+
    2022-11-13
  • C++primer超详细讲解泛型算法
    目录初识泛型算法只读算法写容器算法定制操作lambda表达式lambda捕获和返回再探迭代器插入迭代器iostream迭代器反向迭代器初识泛型算法 只读算法 只读取输入范围内的函数,...
    99+
    2022-11-13
  • Java 泛型超详细入门讲解
    目录1、什么是泛型2、泛型是怎么编译的泛型的编译机制:擦除机制1、什么是泛型 泛型其实就是将类型作为参数传递,泛型允许程序员在编写代码时使用一些以后才指定的类型 ,在实例化该类时将想...
    99+
    2022-11-13
  • C++BoostUtility超详细讲解
    目录一、说明二、Boost.Utility库示例和代码一、说明 Boost.Utility 库是杂项、有用的类和函数的集合,它们太小而无法在独立库中维护。虽然实用程序很小并且可以快速...
    99+
    2022-12-08
    C++ Boost Utility C++ Utility库
  • C++BoostUuid超详细讲解
    目录一、说明二、Boost.Uuid库示例和代码一、说明 Boost.Uuid 为 UUID 提供生成器。 UUID 是不依赖于中央协调实例的通用唯一标识符。例如,没有数据库存储所有...
    99+
    2022-12-08
    C++ Boost Uuid C++ Uuid标识符
  • C++超详细讲解强制类型转换
    目录1 C 强制类型转换2 C++ 强制类型转转1 C 强制类型转换 C 方式的强制类型转换的用法如下代码所示: (Type)(Expression) Type:需要转换成的类型Ex...
    99+
    2022-11-13
  • C++ Boost Assign超详细讲解
    目录说明Exercise说明 Boost.Assign Boost.Assign 库提供了帮助函数来初始化容器或向容器添加元素。如果需要将许多元素存储在一个容器中,这些函数尤其有用。...
    99+
    2022-12-09
    C++ Boost Assign C++ Assign库
  • VueMVVM模型超详细讲解
    目录理解MVVM模型什么是 MVVM 模型MVVM的组成部分Vue中的实现MVVM模型在Vue中的应用理解MVVM模型 我们知道每一个 Vue 应用都是从创建一个新的实例开始的,根据...
    99+
    2022-11-13
    Vue MVVM模型 Vue MVVM框架
  • Java详细分析讲解泛型
    目录1.泛型概念2.泛型的使用2.1泛型类语法2.2泛型方法语法2.3泛型接口语法2.4泛型在main方法中的使用3.擦除机制4.泛型的上界5.通配符5.1通配符的上界5.2通配符的...
    99+
    2022-11-13
  • Java 泛型详解(超详细的java泛型方法解析)
    目录2. 什么是泛型3. 使用泛型的好处4. 泛型的使用4.1 泛型类4.2 泛型方法4.3 泛型接口5. 泛型通配符5.1 通配符基本使用5.2 通配符高级使用6. 总结1. 为什...
    99+
    2022-11-12
  • C++超详细讲解标准库
    目录一、有趣的重载二、C++ 标准库三、小结一、有趣的重载 操作符 << 的原生意义是按位左移,例:1 <<2; 其意义是将整数 1 按位左移2位,即:000...
    99+
    2022-11-13
  • C++BoostPropertyTree示例超详细讲解
    目录一、提要二、应用示例练习一、提要 借助类 boost::property_tree::ptree,Boost.PropertyTree 提供了一个树结构来存储键/值对。树形结构意...
    99+
    2022-11-13
    C++ Boost PropertyTree C++ Boost PropertyTree示例
  • C++BoostVariant示例超详细讲解
    目录一、提要二、示例一、提要         Boost.Variant 提供了一个类似于 unio&...
    99+
    2022-11-13
    C++ Boost Variant C++ Boost Variant示例
  • C++BoostOptional示例超详细讲解
    目录一、概述二、Boost.Optional一、概述 数据结构类似于容器,因为它们可以存储一个或多个元素。但是,它们与容器不同,因为它们不支持容器通常支持的操作。例如,使用本部分介绍...
    99+
    2022-11-13
    C++ Boost Optional C++ Boost Optional示例
  • C++超详细讲解稀疏矩阵
    目录稀疏矩阵矩阵与稀疏矩阵的定义稀疏矩阵的转置详细思路思路一思路二稀疏矩阵的乘法详细思路稀疏矩阵 矩阵与稀疏矩阵的定义 Q:什么是矩阵 A:数学上,一个矩阵由 m 行 n 列的元素组...
    99+
    2022-11-13
  • C++超详细讲解析构函数
    目录特性析构函数处理自定义类型编译器生成的默认析构函数特性 析构函数是特殊的成员函数 特征如下: 析构函数名是~类名;无参数无返回值;一个类有且只有一个析构函数;对象声明周期结束,编...
    99+
    2022-11-13
  • C++超详细讲解构造函数
    目录类的6个默认成员函数构造函数特性编译器生成的默认构造函数成员变量的命名风格类的6个默认成员函数 如果我们写了一个类,这个类我们只写了成员变量没有定义成员函数,那么这个类中就没有函...
    99+
    2022-11-13
  • C++超详细讲解智能指针
    目录一、内存泄漏-永恒的话题二、深度思考三、智能指针分析四、小结一、内存泄漏-永恒的话题 动态申请堆空间,用完后不归还C++ 语言中没有垃圾回收的机制指针无法控制所指堆空间的生命周期...
    99+
    2022-11-13
  • C++超详细讲解函数对象
    目录一、客户需求二、存在的问题三、解决方案四、函数对象五、小结一、客户需求 编写一个函数 函数可以获得斐波那契数列每项的值每调用一次返回一个值函数可根据需要重复使用 下面来看第一个...
    99+
    2022-11-13
  • C++超详细讲解字符串类
    目录一、历史遗留问题二、解决方案三、标准库中的字符串类四、字符串循环右移五、小结一、历史遗留问题 C 语言不支持真正意义上的字符串C 语言用字符数组和一组函数实现字符串操作C 语言不...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作