当前位置: 首页 > article >正文

【C++】模板机制

C++中提供两种模板机制:函数模板和类模板

函数模板

建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表;

template<typename T>

函数声明或定义

//函数模板
template<typename T> //声明一个模板,告诉编译器后面代码中紧跟着的T不要报错,T是一个通用数据类型;
void MySwap(T& a, T& b)
{
        T temp = a;
        a = b;
        b = temp;
}

void test02()
{
        //利用函数模板来交换
        //两种方式使用函数模板
        //1、自动类型推导
        int a = 1, b = 2;
        MySwap(a, b);
        cout << a << " " << b << endl;

        //2、显示指定类型
        double c = 1.1, d = 2.2;
        MySwap<double>(c, d);
        cout << c << " " << d << endl;
}

注意事项:

  • 自动类型推导,必须推导出一致的数据类型T,才可以使用

  • 模板必须要确定出T的数据类型,才可以使用

普通函数和函数模板

区别
  • 普通函数调用时可以发送自动类型转换(隐式类型转换)

  • 函数模板调用时,如果利用自动类型推导,不会发送隐式类型转换

  • 如果利用显示指定类型的方式,可以发送隐式类型转换

//普通函数
int add01(int a, int b)
{
        return a + b;
}

template<class T>
T add02(T a, T b)
{
        return a + b;
}

void test03()
{
        int a = 10;
        int b = 20;
        char c = 'a';//a -->97
        cout << add01(a, b) << endl;
        cout << add01(a, c) << endl;

        //1、自动类型推导
        cout << add02(a, b) << endl;
        //cout << add02(a, c) << endl;//报错
        //2、显示指定类型
        cout << add02<int>(a, c) << endl;
}

规则
  • 如果函数模板和普通函数都可以实现,优先调用普通函数

  • 可以通过空模板参数列表来强制调用函数模板

  • 函数模板也可以发生重载

  • 如果函数模板可以产生更好的匹配,优先调用函数模板

add02<>(a,b):强制调用

template<class T>

void Print(T a, T b)

{

        cout<<"调用模板"<<endl;

}

template<class T>

void Print(T a, T b,T c)

{

        cout<<"重载模板"<<endl;

}

局限性

模板不是万能的,有些特定数据类型,需要用具体化方式做特殊实现

class Person
{
public:
        Person(string name,int age)
        {
                _name = name;
                _age = age;
        }
public:
        string _name;
        int _age;
};
//对比两个数据是否相等
template<class T>
bool myCompare(T& a, T& b)
{
        if (a == b)
        {
                return true;
        }
        else
        {
                return false;
        }
}

//利用具体话Person版本实现代码,具体化优先调用
template<> bool myCompare(Person &p1, Person& p2)
{
        if (p1._name == p2._name && p1._age == p2._age)
        {
                return true;
        }
        return false;
}

void test04()
{
        int a = 10;
        int b = 20;
        bool ret = myCompare(a, b);
        cout << ret << endl;

        Person p1("Tom", 10);
        Person p2("Tom", 10);
        ret = myCompare(p1, p2);
        cout << ret << endl;
}

类模板

//类模板
template<class NameType,class AgeType>
class Person
{
public:
        Person(NameType name, AgeType age)
        {
                _name = name;
                _age = age;
        }
public:
        NameType _name;
        AgeType _age;
};

void test01()
{
        Person<string, int> p1("Tom", 9);
}
类模板和函数模板区别
  • 类模板没有自动类型推导的使用方式

//Person p1("Tom", 9); 错误,无法使用自动类型推导

Person<string, int> p1("Tom", 9); //正确,只能用显示指定类型

  • 类模板在模板参数列表中可以有默认参数

template<class NameType,class AgeType = int>

Person<string> p1("Tom", 9);

类模板中成员函数创建时机

  • 普通类中的成员函数一开始就可以创建

  • 类模板中的成员函数在调用时才创建

类模板对象做函数参数

三种传入方式:

  • 指定传入类型 --直接显示对象的数据类型

  • 参数模板化 --将对象中的参数变为模板进行传递

  • 整个类模板化 --将这个对象类型模板化进行传递

//类模板对象做函数参数
template<class T1,class T2>
class Person
{
public:
        Person(T1 name, T2 age)
        {
                _name = name;
                _age = age;
        }
        void show()
        {
                cout << "姓名:" << _name << ", 年龄:" << _age << endl;
        }
public:
        T1 _name;
        T2 _age;
};
//1、指定传入类型
void printPerson1(Person<string, int> &p)
{
        p.show();
}
void test02()
{
        Person<string, int> p1("Tom", 20);
        printPerson1(p1);
}
//2、参数模板化
template<class T1, class T2>
void printPerson2(Person<T1, T2>& p)
{
        p.show();
        cout <<"T1的类型为:" << typeid(T1).name() << endl;
        cout <<"T1的类型为:" << typeid(T2).name() << endl;
}
void test03()
{
        Person<string, int> p1("Tom", 20);
        printPerson2(p1);
}
//3、整个类模板化
template<class T>
void printPerson3( T &p)
{
        p.show();
        cout << "T的数据类型为:" << typeid(T).name() << endl;
}
void test04()
{
        Person<string, int> p1("Tom", 20);
        printPerson3(p1);
}

 

类模板与继承

//类模板与继承
template<class T>
class Base
{
        T m;
};

class Son :public Base<int>//必须要知道父类中的T类型,才能继承给子类
{

};

//如果想灵活指定父类中的T类型,子类也需要变成类模板
template<class T1,class T2>
class Son2 :public Base<T1>
{
        T2 obj;
};

void test05()
{
        Son2<int, char> s2;
}

类模板成员函数的类外实现

//类模板成员函数的类外实现
template<class T1, class T2>
class Person
{
public:
        Person(T1 name, T2 age);
        void show();
public:
        T1 _name;
        T2 _age;
};
template<class T1,class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
        this->_name = name;
        this->_age = age;
}

template<class T1, class T2>
void Person<T1, T2>::show()
{
        cout << "姓名:" << _name << ", 年龄:" << _age << endl;
}

void test05()
{
        Person<string, int> p("Tom", 20);
        p.show();
}

类模板的分文件编写

就是声明在.h文件,实现在.cpp中实现:

报错原因:

类模板中成员函数创建时间是在调用的时候才会创建;所以链接.h文件的时候,并不会生成成员函数,所以在调用的时候就会报错,找不到这个成员函数

解决方法:

  • 直接包含.cpp源文件

  • 将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制的

类模板与友元

全局函数类内实现-直接在类内声明友元即可

全局函数类外实现-需要提前让编译器知道全局函数的存在


//提前让编译器知道Person类的存在
template<class T1, class T2>
class Person;
//类外实现
template<class T1, class T2>
void show2(Person<T1, T2>p)
{
        cout << "姓名:" << p._name << ", 年龄:" << p._age << endl;
}


template<class T1,class T2>
class Person
{
        //全局函数,类内实现
        friend void show1(Person<T1,T2>p)
        {
                cout << "姓名:" << p._name << ", 年龄:" << p._age << endl;
        }
        //全局函数,类外实现
        //加空模板参数列表
        friend void show2<>(Person<T1, T2>p);
public:
        Person(T1 name, T2 age)
        {
                _name = name;
                _age = age;
        }
        
public:
        T1 _name;
        T2 _age;
};

void test06()
{
        Person<string, int>p("Tom", 20);
        show1(p);

        Person<string, int>p1("Jorry", 20);
        show2(p1);
}


http://www.kler.cn/a/441075.html

相关文章:

  • Fiddler(一) - Fiddler简介_fiddler软件
  • AI编程:如何编写提示词
  • C++和Python实现SQL Server数据库导出数据到S3并导入Redshift数据仓库
  • MySQL查询优化(三):深度解读 MySQL客户端和服务端协议
  • SpringCloud系列教程:微服务的未来(十八)雪崩问题、服务保护方案、Sentinel快速入门
  • MySQL 索引存储结构
  • SSM 垃圾分类系统:科技赋能环保新篇
  • Vue Web开发(八)
  • Android 写排行榜,顶部前三
  • 字符2
  • Group FLUX - Summary Essay of the Alpha Phase Problem
  • Next.js流量教程:如何在 Next.js 中添加结构化数据以生成丰富摘要(Rich Snippets)
  • 【现代服务端架构】传统服务器 对比 Serverless
  • 电机控制杂谈(23)——共模电压与轴电流
  • es 开启slowlog
  • UIP协议栈 TCP通信客户端 服务端,UDP单播 广播通信 example
  • 本地部署大模型QPS推理测试
  • sql中case when若条件重复 执行的顺序
  • 召回系统介绍
  • 【Elasticsearch】关键数据类型
  • 20241217使用M6000显卡在WIN10下跑whisper来识别中英文字幕
  • 蜂鸟视图的蜂鸟云开发者中心更新:JS SDK v3.1.8 与 微信小程序 SDK v3.1.8 全新上线!
  • 【mysql】row模式的主从架构中,删除无主键的表可能导致从库“夯住”或产生较大的同步延迟
  • JDK以及JRE
  • 三菱协议以及C#实现
  • 【十进制整数转换为其他进制数——短除形式的贪心算法】