C++ note------模板

本文详细介绍了C++中的模板概念,包括函数模板和类模板。函数模板利用`template`关键字,提供自动类型推导和显示指定类型的使用方式,提高了代码复用性。类模板则没有自动类型推导,成员函数在调用时创建,并且在作为函数参数时有多种传递方式。同时,文章强调了类模板继承时指定父类模板类型的重要性,以及模板声明与实现的处理策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、函数模板
template — 声明创建模板

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

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

#include <iostream>
using namespace std;

template<typename T>
void swapInt(T& a,T& b)
{
    T temp = a;
    a = b;
    b = temp;
}
void test01()
{
    int a = 10;
    int b = 20;
    swapInt(a,b);
    cout<<"自动类型推导"<<endl;
    cout<<"a = "<<a<<endl;
    cout<<"b = "<<b<<endl;
    swapInt<int>(a,b);
    cout<<"显示指定类型"<<endl;
    cout<<"a = "<<a<<endl;
    cout<<"b = "<<b<<endl;
}
int main(){
    test01();
    system("pause");
    return 0;
}

总结:
1.函数模板利用关键字 template
2.使用函数模板有两种方式:自动类型推导、显示指定类型
3.模板的目的是为了提高复用性,将类型参数化

注意事项:
1.自动类型推导,必须推导出一致的数据类型T,才可以使用
2.模板必须要确定出T的数据类型,才可以使用
排序函数模板

#include <iostream>
using namespace std;
#include <fstream>
template<typename T>
void mySwap(T& a,T& b)
{
    T temp = a;
    a = b;
    b = temp;
}
template <class T>
void mySort(T arr[],int len)
{
    for(int i = 0;i < len;i++)
    {
        int max = i;
        for(int j = i+1;j < len;j++)
        {
            if(arr[max] < arr[j])
            {
                max = j;
            }
        }
        if(max != i)
        {
            mySwap(arr[max],arr[i]);
        }
    }
}
template <typename T>
void printArray(T arr[],int len)
{
    for(int i = 0;i < len;i++)
    {
        cout<<arr[i]<<" ";
    }
    cout<<endl;
}
void test01()
{
    char charArr[] = "qwertyui";
    int num = sizeof(charArr)/sizeof(char);
    mySort(charArr,num);
    printArray(charArr,num);
}
void test02()
{
    int intArr[] = {7,4,9,11,3,6};
    int num = sizeof(intArr)/sizeof(int);
    mySort(intArr,num);
    printArray(intArr,num);
}
int main(){
    test01();
    test02();
    system("pause");
    return 0;
}

输出结果:
y w u t r q i e 
11 9 7 6 4 3

其实通过这个例子,很容易感觉得到,函数模板似乎和之前的多态有异曲同工之妙,都是同一件事物,却能够根据自己所需,实现相应的功能,甚至于会觉得像一个功能函数的调用

使用函数模板时,如果用自动类型推导,不会发生自动类型转换,即隐式类型转换

调用规则:

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

但是提供了函数模板,最好就不要提供普通函数,否则容易出现二义性

--------------------**********************************************--------------------------------

--------------------**********************************************--------------------------------

二、类模板
和函数模板相类似的用法,template后面加上创建的类,一般类模板使用class,函数模板使用typename,以区分两种模板

类模板与函数模板区别主要有两点:

template<class NameType, class AgeType = int> 
class Person
{
public:
	Person(NameType name, AgeType age)
	{
		this->mName = name;
		this->mAge = age;
	}
	void showPerson()
	{
		cout << "name: " << this->mName << " age: " << this->mAge << endl;
	}
public:
	NameType mName;
	AgeType mAge;
};

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

void test01()
{
	// Person p("孙悟空", 1000); // 错误 类模板使用时候,不可以用自动类型推导
	Person <string ,int>p("孙悟空", 1000); //必须使用显示指定类型的方式,使用类模板
	p.showPerson();
}

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

void test02()
{
	Person <string> p("猪八戒", 999); //类模板中的模板参数列表 可以指定默认参数
	p.showPerson();
}

int main() {

	test01();

	test02();

	system("pause");

	return 0;
}

而且,类模板中成员函数和普通类中成员函数创建时机是有区别的:
1.普通类中的成员函数一开始就可以创建

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

所以,类模板中的成员函数并不是一开始就创建的,在调用时才去创建

同时,类模板对象也可以做函数参数,传入函数一共有三种方式:

template<class NameType, class AgeType = int> 
class Person
{
public:
	Person(NameType name, AgeType age)
	{
		this->mName = name;
		this->mAge = age;
	}
	void showPerson()
	{
		cout << "name: " << this->mName << " age: " << this->mAge << endl;
	}
public:
	NameType mName;
	AgeType mAge;
};

1.指定传入的类型 ,直接显示对象的数据类型

void printPerson1(Person<string, int> &p) 
{
	p.showPerson();
}
void test01()
{
	Person <string, int >p("孙悟空", 100);
	printPerson1(p);
}

2.参数模板化,将对象中的参数变为模板进行传递

template <class T1, class T2>
void printPerson2(Person<T1, T2>&p)
{
	p.showPerson();
	cout << "T1的类型为: " << typeid(T1).name() << endl;
	cout << "T2的类型为: " << typeid(T2).name() << endl;
}
void test02()
{
	Person <string, int >p("猪八戒", 90);
	printPerson2(p);
}

3.整个类模板化,将这个对象类型 模板化进行传递

template<class T>
void printPerson3(T & p)
{
	cout << "T的类型为: " << typeid(T).name() << endl;
	p.showPerson();

}
void test03()
{
	Person <string, int >p("唐僧", 30);
	printPerson3(p);
}

另外,类模板与普通类一样可以用于继承,需要注意的是;
1.当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
2.如果不指定,编译器无法给子类分配内存
3.如果想灵活指定出父类中T的类型,子类也需变为类模板
如果父类是类模板,子类需要指定出父类中T的数据类型

template<class T>
class Base
{
	T m;
};

//class Son:public Base  //错误,c++编译需要给子类分配内存,必须知道父类中T的类型才可以向下继承
class Son :public Base<int> //必须指定一个类型
{
};
void test01()
{
	Son c;
}

//类模板继承类模板 ,可以用T2指定父类中的T类型
template<class T1, class T2>
class Son2 :public Base<T2>
{
public:
	Son2()
	{
		cout << typeid(T1).name() << endl;
		cout << typeid(T2).name() << endl;
	}
};

void test02()
{
	Son2<int, char> child1;
}

通常来讲,我们都习惯将函数声明写在.h头文件中,函数的实现写在.c源文件中,然而对于类模板,函数声明与实现如果分开写,会导致程序链接不到,所以解决办法之一就是直接包含写有模板的源文件,但还有更好的办法,那就是将声明和实现写在同一个文件,文件后缀改为.hpp,属于模板文件

hello.cpp
#include <iostream>
using namespace std;
#include "hello.hpp"
void test01()
{
    Person<string,int> p("Jack",18);
    p.showPerson();
}
int main(){
    test01();
    system("pause");
    return 0;
}
hello.hpp
#pragma once
#include <iostream>
using namespace std;
#include <string>

template<class T1,class T2>
class Person
{
public:
    Person(T1 name,T2 age);
    void showPerson();
public:
    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<<"name:"<<this->m_Name;
    cout<<"age:"<<this->m_Age;
}

最后是关于友元函数与类模板,友元函数可以在类内实现也可以在类外实现
1.类外实现

#include <iostream>
using namespace std;
#include <string>
//1.先声明一下类模板
template<class T1,class T2> class Person;
//2.友元函数的类外实现(全局函数)
template<class T1,class T2>
void printPerson1(Person<T1,T2> &p)
{
    cout<<"友元函数类内声明,类外实现"<<endl;
}
template<class T1,class T2>
class Person
{
//3.友元函数的类内声明
    friend void printPerson1<>(Person<T1,T2> &p);
public:
    Person(T1 name,T2 age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
private:
    T1 m_Name;
    T2 m_Age;
};
void test01()
{
    Person<string,int> p("史蒂芬",21);
    printPerson1(p);
}
int main(){
    test01();
    system("pause");
    return 0;
}

2.类内实现(推荐使用这种)

template<class T1,class T2>
class Person
{
    friend void printPerson1(Person<T1,T2> &p)
    {
        cout<<"友元函数类内实现"<<endl;
    }
public:
    Person(T1 name,T2 age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
private:
    T1 m_Name;
    T2 m_Age;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值