当前位置 博文首页 > Zhi Zhao的博客:C++入门基础教程(八):模板

    Zhi Zhao的博客:C++入门基础教程(八):模板

    作者:[db:作者] 时间:2021-08-08 13:07

    前言

    博主通过对C++基础知识的总结,有望写出深入浅出的C++基础教程专栏,并分享给大家阅读,今后的一段时间我将持续更新C++入门系列博文,想学习C++的朋友可以关注我,希望大家有所收获。

    一、函数模板

    1.1 基本概念

    语法:template

    template----声明创建模板

    typename----表明其后面的符号是一种数据类型,可以用class代替

    T-----------通用的数据类型,名称可以替换,通常为大写字母。

    使用模板有两种方式:1)自动类型推导;2)显示指定类型。

    // 函数模板
    template<typename T>
    void myswap(T &a, T &b)
    {
    	T temp = a;
    	a = b;
    	b = temp;
    }
    // 利用函数模板实现交换
    void test()
    {
    	int a = 1, b = 2;
    	
    	// 1.自动类型推导
    	myswap(a, b);
        
        cout << "a= " << a << endl;
    	cout << "b= " << b << endl;
    
    	// 2.显示指定类型
    	myswap<int>(a, b);
    
    	cout << "a= " << a << endl;
    	cout << "b= " << b << endl;
    }

    1.2?函数模板注意事项

    1)自动类型推导,必须推导出一致的数据类型T;

    2)模板必须要确定出T的数据类型。显示指定类型:例如函数 myswap(a, b);

    // 模板
    template<class T>
    void myswap(T &a, T &b)   // typename可以替换为class
    {
    	T temp = a;
    	a = b;
    	b = temp;
    }
    
    // 1、自动类型推导,必须推导出一致的数据类型T
    void test01()
    {
    	int a = 1, b = 2;
    	char c = 'c';
    	// 1.自动类型推导
    	//myswap(a, b);  // 正确!
    	//myswap(a, c);  // 错误!推导不出一致的T类型
    
    	// 2.显示指定类型
    	//myswap<int>(a, b);
    
    	cout << "a= " << a << endl;
    	cout << "b= " << b << endl;
    }
    
    // 2、模板必须要确定出T的数据类型
    template<class T>
    void func()
    {
    	cout << "func的调用" << endl;
    }
    
    void test02()
    {
    	func<int>();
    }

    1.3 普通函数与函数模板的区别

    1)普通函数调用时可以发生自动类型转换(隐式类型转换);
    2)函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换,但是利用显示指定类型的方式就可以。

    int Add01(int a, int b)
    {
    	return a + b;
    }
    
    template<class T>
    T Add02(T a, T b)
    {
    	return a + b;
    }
    
    void test()
    {
    	int a = 10, b = 20;
    	char c = 'c';
    	
    	// 调用普通函数
    	cout << Add01(a, c) << endl;
    
    	// 利用模板
    	// 自动类型推导,不会发生隐式类型转换
    	cout << Add02(a, c) << endl;  // 错误
    	// 显示指定类型,可以发生隐式类型转换
    	cout << Add02<int>(a, c) << endl;
    }

    1.4 普通函数与函数模板的调用规则

    1)如果函数模板和普通函数都可以调用,优先调用普通函数;
    2)可以通过空模板参数列表,强制调用函数模板;
    3)函数模板可以发生重载;
    4)如果函数模板可以产生更好的匹配,优先调用函数模板。

    void myPrint(int a, int b)
    {
    	cout << "调用普通函数" << endl;
    }
    
    template<class T>
    void myPrint(T a,T b)
    {
    	cout << "调用函数模板" << endl;
    }
    
    template<class T>
    void myPrint(T a, T b,T c)
    {
    	cout << "调用重载函数模板" << endl;
    }
    
    void test()
    {
    	int a = 10, b = 20;
    	char c = 'c', d = 'd';
    	// 1.如果函数模板和普通函数都可以调用,优先调用普通函数
    	myPrint(a,b);
    	// 2.可以通过空模板参数列表,强制调用函数模板
    	myPrint<>(a, b);
    	// 3.函数模板可以发生重载
    	myPrint(a, b, 100);
    	// 4.如果函数模板可以产生更好的匹配,优先调用函数模板
    	myPrint(c, d);
    }

    二、类模板

    2.1 类模板与函数模板的区别

    1)类模板没有自动类型推导的使用方式

    template<class NameType,class AgeType>
    class Person
    {
    public:
    	Person(NameType name,AgeType age)
    	{
    		this->m_Name = name;
    		this->Age = age;
    	}
    	void showPerson()
    	{
    		cout << "name= " << this->m_Name 
    			<< " age= " << this->Age << endl;
    	}
    	NameType m_Name;
    	AgeType Age;
    };
    
    void test()
    {
    	//Person p("张三",23);  错误,无法用自动类型推导
    	Person<string, int>p1("张三", 23);
    	p1.showPerson();
    }

    2)类模板在模板参数列表中可以有默认参数?

    template<class NameType,class AgeType=int>
    class Person
    {
    public:
    	Person(NameType name,AgeType age)
    	{
    		this->m_Name = name;
    		this->Age = age;
    	}
    	void showPerson()
    	{
    		cout << "name= " << this->m_Name 
    			<< " age= " << this->Age << endl;
    	}
    	NameType m_Name;
    	AgeType Age;
    };
    
    // 类模板在模板参数列表中可以有默认参数
    void test()
    {
    	Person<string> p2("李四", 24);
    	p2.showPerson();
    }

    2.2 类模板对象做函数参数

    1)指定传入的类型——直接显示对象的数据类型;(此方式在开发中常用)

    2)参数模板化——将对象中的参数变为模板进行传递;

    3)整个类模板化——将这个对象类型模板化进行传递。

    template<class	T1, class T2>
    class Person
    {
    public:
    	Person(T1 name, T2 age)
    	{
    		this->m_Name = name;
    		this->Age = age;
    	}
    	void showPerson()
    	{
    		cout << "姓名:" << this->m_Name
    			<< " 年龄:" << this->Age << endl;
    	}
    	T1 m_Name;
    	T2 Age;
    };
    
    // 1.指定传入的类型——直接显示对象的数据类型
    void printPerson1(Person<string, int>&p1)
    {
    	p1.showPerson();
    }
    void test01()
    {
    	Person<string, int>p1("张三", 23);
    	printPerson1(p1);
    }
    
    // 2.参数模板化——将对象中的参数变为模板进行传递
    template<class	T1, class T2>
    void printPerson2(Person<T1, T2>&p2)
    {
    	p2.showPerson();
    	cout << "T1的类型为:" << typeid(T1).name() << endl;
    	cout << "T2的类型为:" << typeid(T2).name() << endl;
    }
    void test02()
    {
    	Person<string, int>p2("李四", 24);
    	printPerson2(p2);
    }
    
    // 3.整个类模板化——将这个对象类型模板化进行传递
    template<class T>
    void printPerson3(T &p3)
    {
    	p3.showPerson();
    	cout << "T的类型为:" << typeid(T).name() << endl;
    }
    void test03()
    {
    	Person<string, int>p3("王五", 25);
    	printPerson3(p3);
    }

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

    问题:类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到。

    解决方式1:直接包含.cpp源文件;

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

    template<class	T1, class T2>
    class Person
    {
    public:
    	Person(T1 name, T2 age);
    
    	void showPerson();
    
    	T1 m_Name;
    	T2 m_Age;
    };
    
    // 构造函数的类外实现
    template<class T1, class T2>
    Person<T1, T2>::Person(T1 name, T2 age)
    {
    	this->m_Name = name;
    	this->m_Age = age;
    }
    
    // 成员函数的类外实现
    template<class T1, class T2>
    void Person<T1, T2>::showPerson()
    {
    	cout << "姓名:" << this->m_Name
    		<< " 年龄:" << this->m_Age << endl;
    }

    结束语

    大家的点赞和关注是博主最大的动力,博主所有博文中的代码文件都可分享给您(除了少量付费资源),如果您想要获取博文中的完整代码文件,可通过C币或积分下载,没有C币或积分的朋友可在关注、点赞和评论博文后,私信发送您的邮箱,我会在第一时间发送给您。博主后面会有更多的分享,敬请关注哦!

    cs
    下一篇:没有了