C++中的泛型详细讲解

这篇具有很好参考价值的文章主要介绍了C++中的泛型详细讲解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.定义

它是一种泛化的编程方式,其实现原理为程序员编写一个函数/类的代码示例,让编译器去填补出不同的函数实现。允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换句话说,泛型允许您编写一个可以与任何数据类型一起工作的类或方法。

2.模板

模板是泛型编程的基础,是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念。每个容器都有一个单一的定义,比如 向量,我们可以定义许多不同类型的向量,比如 vector vector 。可以使用模板来定义函数和类,我们来具体分析一下模板函数和模板类的创建和使用:

2.1 模板函数

我们想实现像Python一样,一个带有参数的方法,它的相同参数可以传递不同类型的值。我们通过下面的例子来了解一下:

#include<iostream>
using namespace std;

template<typename T>
void Test(T& arg1,T& arg2) // 这是一个实现两个变量值交换的函数
{
    T temp = arg1;
    arg1 = arg2;
    arg2 = temp;
}
// typename 是定义模板的关键字,可以用class来替代(注意不能用struct)
int main()
{
    int a = 10, b = 20;
    double c = 5.2, d = 10.5;
    Test(a, b);
    Test(c, d);
    cout << a << " " << b << endl;
    cout << c << " " << d << endl;
}
// 输出结果:
20 10
10.5 5.2

// 我们交换了int类型的a与b的值,double类型c和d的值

如果我们将int和double同时传给Swap这个函数,那么编译器会报错,表示模板参数T不明确,那么我们需要做如下改动:

(1)把函数传参中的引用去掉

(2)把a强制转换成double类型,Swap((double)a, c)

#include<iostream>
using namespace std;

template<typename T>
void Test(T arg1,T arg2) // 把“&”引用去掉
{
    T temp = arg1;
    arg1 = arg2;
    arg2 = temp;
    cout << arg1 << " " << arg2 << endl;
}

int main()
{
    int a = 10;
    double c = 5.2;

    Test((double)a, c);
    cout << "a:" << a << " c:" << c << endl;
}

// 输出结果:
5.2 10
a:10 c:5.2
// a,c值没有变,是因为我们传参是值传递

接下来我们看一下,多个模板参数的情况:

#include<iostream>
#include<typeinfo>
using namespace std;

template<typename T1, typename T2>
void Info(T1 arg1,T2 arg2)
{
    cout << typeid(arg1).name() << endl;
    cout << typeid(arg2).name() << endl;
}

int main()
{
    int a = 10;
    double c = 5.2;

    Info(a, c);
    cout << "a:" << a << " c:" << c << endl;
}

// 输出结果:
i
d
a:10 c:5.2

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

void Info(int arg1,double arg2)
{
    cout << typeid(arg1).name() << endl;
    cout << typeid(arg2).name() << endl;
}

2.2 函数模板实例化

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

  • 利用强制类型转换
  • 使用<int>直接指定实例化为int类型
#include<iostream>
using namespace std;

template<typename T>
void Test(T arg1,T arg2) // 把“&”引用去掉
{
    T temp = arg1;
    arg1 = arg2;
    arg2 = temp;
    cout << arg1 << " " << arg2 << endl;
}

int main()
{
    int a = 10;
    double c = 5.2;

    Test((double)a, c);    // 强制类型转换
    Test<int>(a, c);        // 直接指定
    cout << "a:" << a << " c:" << c << endl;
}
/*
使用第二种方式的时候,编译器会对另外一个不匹配的参数进行隐式类型转换。如果转换不成功,则会报错。

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

2.3 函数模板实例化

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

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

当有多个模板参数时,缺省值需要从右往左给,当然函数模板的传参也支持缺省值:

#include<iostream>
using namespace std;

template<typename T>
void Test(T arg1,T arg2=20) // 把“&”引用去掉
{
    T temp = arg1;
    arg1 = arg2;
    arg2 = temp;
    cout << arg1 << " " << arg2 << endl;
}

int main()
{
    int a = 10;

    Test(a);
}

2.4 模板函数与普通函数同时存在情况

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

#include<iostream>
using namespace std;

template<typename T>
void Test(T arg1,T arg2 = 90) 
{
    cout << "Test temp " << arg1 << " " << arg2 << endl;
}

void Test(int arg1,int arg2) 
{
   	cout << "Test " << arg1 << " " << arg2 << endl;
}

int main()
{
    int a = 10, b = 20;
    double c = 5.2, d = 10.5;

    Test(a);
    Test(a, b);
    Test(a, (int)c);    // 强转
    Test((double)a, c); // 强转
    Test<int>(a, c);    // 直接指定为int
}
// 输出结果:
Test temp 10 90
Test 10 20
Test 10 5
Test temp 10 5.2
Test temp 10 5

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

函数模板的声明和定义要放在一个头文件中。在部分使用场景,会使用.hpp来表示这个头文件是包含了函数定义的(即.h和.cpp的集合体)。需要注意,这并不是一个硬性要求,你也可以直接使用.h,并将声明和定义放入其中。因为单独的.h声明会在源文件顶部展开,而此时函数模板正常推演参数,但编译器并没有找到函数的实现,即这是一个没有地址的函数。从而导致编译器找不到函数的地址,产生了符号表的链接错误。其实是有的,我们可以在模板函数定义的.cpp中对我们需要使用的函数进行显式实例化指定

//头文件
//声明
template<typename T>
void Test(T arg1, T arg2);
//源文件
//定义
template<typename T>
void Test(T arg1, T arg2)
{
   cout << arg1 << " " << arg2 << endl;
}
//在源文件中显式实例化
template
void Test<int>(int arg1, int arg2);
template
void Test<double>(double arg1, double arg2);

显式实例化需要对我们要用的所有函数进行实例化,比如你需要用double类型,只显示实例化了int类型是不行的,依旧会报错。这样感觉非常多余……!所以还是把声明和定义放在同一个文件里面清晰明了一些。

3 类模板

正如我们定义函数模板一样,我们也可以定义类模板。泛型类声明的一般形式如下所示:

template <class type> class class-name {
.
.
.
}

在这里,type 是占位符类型名称,可以在类被实例化的时候进行指定。您可以使用一个逗号分隔的列表来定义多个泛型数据类型。

下面的实例定义了类 Stack<>,并实现了泛型方法来对元素进行入栈出栈操作:文章来源地址https://www.toymoban.com/news/detail-530955.html

#include <iostream>
#include <vector>
#include <cstdlib>
#include <string>
#include <stdexcept>
 
using namespace std;
 
template <class T>
class Stack { 
  private: 
    vector<T> elems;     // 元素 
 
  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<>::pop(): 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() 
{ 
    try { 
        Stack<int>         intStack;  // int 类型的栈 
        Stack<string> stringStack;    // string 类型的栈 
 
        // 操作 int 类型的栈 
        intStack.push(7); 
        cout << intStack.top() <<endl; 
 
        // 操作 string 类型的栈 
        stringStack.push("hello"); 
        cout << stringStack.top() << std::endl; 
        stringStack.pop(); 
        stringStack.pop(); 
    } 
    catch (exception const& ex) { 
        cerr << "Exception: " << ex.what() <<endl; 
        return -1;
    } 
}

// 输出结果:
7
hello
Exception: Stack<>::pop(): empty stack

到了这里,关于C++中的泛型详细讲解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • python中的泛型使用TypeVar

    PEP484的作者希望借助typing模块引入类型提示,不改动语言的其它部分。通过精巧的元编程技术,让类支持[]运算不成问题。但是方括号内的T变量必须在某处定义,否则要大范围改动python解释器才能让泛型支持特殊的[]表示法。 鉴于此,我们增加了typing.TypeVar构造函数,把变量

    2024年02月04日
    浏览(48)
  • 详解Java中的泛型(泛型的语法,擦除机制,泛型的上界)

    目录 一.什么是泛型 二.Java中为什么要使用泛型 三.泛型的语法 四.泛型类的使用 五.泛型的编译机制(擦除机制) 六.泛型的上界 泛型(Generics)是Java SE 5中引入的一个新特性,可以 使Java中的类和方法具有更广泛的类型范围 。通俗的说,它使得我们可以在定义类和方法时指定

    2024年02月05日
    浏览(47)
  • Java 中的泛型(两万字超全详解)

    博主将用 CSDN 记录 Java 后端开发学习之路上的经验,并将自己整理的编程经验和知识分享出来,希望能帮助到有需要的小伙伴。 博主也希望和一直在坚持努力学习的小伙伴们共勉!唯有努力钻研,多思考勤动手,方能在编程道路上行至所向。 由于博主技术知识有限,博文中

    2024年02月04日
    浏览(44)
  • Java 中的泛型是什么,它有什么作用?(十五)

    Java中的泛型是一种类型参数化机制,它使代码更具可读性、可重用性和稳健性。在Java中,通过使用泛型,可以将类型作为参数传递给类或方法,并在编译时执行类型检查,从而避免许多运行时错误。 泛型的基础 Java泛型的基础概念是类型变量、类型参数和类型边界。 类型变

    2024年02月03日
    浏览(38)
  • 【Rust 基础篇】Rust 中的泛型:结构体和方法

    在 Rust 中,泛型是一种强大的特性,可以在结构体和方法中使用通用的类型参数。通过泛型,我们可以编写更加灵活和可复用的代码。本篇博客将详细介绍如何在 Rust 的结构体和方法中使用泛型,包括泛型结构体的定义、泛型方法的实现以及对泛型参数的约束。 在 Rust 中,我

    2024年02月16日
    浏览(41)
  • 第8章-第1节-Java中的泛型(参数化类型)

    1、泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用。什么是泛型?为什么要使用泛型? 2、概念:在类声明体中用到了类型参数。 3、泛型类只支持类类型,不支持基本数据类型(如int),但可以用包装类(如Integer ) 泛型标识 含义 T Type 类

    2024年01月23日
    浏览(41)
  • ts 终于搞懂TS中的泛型啦! | typescript 入门指南 04

    大家好,我是王天~ 这篇文章是 ts入门指南系列中第四篇,主要讲解ts中的泛型应用,泛型在ts中是比较重要的概念,我花挺长时间才搞明白的,希望能帮助到大家 ~ ** ts 入门指南系列 ** Ts和Js 谁更适合前端开发?| typescript 入门指南 01 详解tsconfig.json 配置文件 | 02 ts入门指南

    2024年02月08日
    浏览(40)
  • C++之泛型算法

    大多数算法在头文件algorithm中,在头文件numeric中定义了一组数值泛型算法。理解算法最基本的方法就是了解他们是否读取元素,改变元素,或是重排元素顺序 使用STL算法经常需要使用函数对象和函数适配器,所以需要#include 所有算法都被设计用来处理一个或多个iterator区间,

    2024年02月03日
    浏览(32)
  • c++泛型算法相关笔记

    泛型算法:可以支持多种类型的算法 此处主要来讨论怎么使用标准库中定义的泛型算法 algorithm , numeric , ranges . 在引入泛型算法之前,还有一种是方法的形式,比如说 std::sort 和 std::list::sort ,前者是算法,后者是list类中定义的函数(方法) 为什么引入泛型算法,而不用方法

    2024年01月19日
    浏览(34)
  • C++标准库 -- 泛型算法 (Primer C++ 第五版 · 阅读笔记)

    顺序容器只定义了很少的操作:在多数情况下,我们可以添加和删除元素访问首尾元素、确定容器是否为空以及获得指向首元素或尾元素之后位置的迭代器。 我们可以想象用户可能还希望做其他很多有用的操作:查找特定元素、替换或删除一个特定值、重排元素顺序等。 标准库

    2023年04月21日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包