简介
一种用于实现通用编程的机制。
通过使用模板,我们可以编写可复用的代码,可以适用于多种数据类型。
C++ 模板的语法使用角括号 (< >) 来表示泛型类型,并使用关键字 template 来定义和声明模板。
示例
cpp
#include <iostream>
// 定义一个模板函数
template<typename T>
T add(T a, T b) {
return a + b;
}
int main() {
int x = 5, y = 10;
float f1 = 2.5, f2 = 3.7;
// 调用模板函数
std::cout << "Sum of integers: " << add(x, y) << std::endl;
std::cout << "Sum of floats: " << add(f1, f2) << std::endl;
return 0;
}
优点:
提高代码的灵活性和复用性
总结
将数据类型可以作为参数进行传递
原理
编译器会对函数模板进行两次编译,
在声明的地方对模板代码本身进行编译,
在调用的地方对参数替换后的 代码进行编译。
分类
函数模板
类模板
函数模板
步骤
1,声明
2,使用
声明语法
位置:在函数上
cpp
//class 和 typename 都是一样的,用哪个都可以
template<class 模板名,class 模板名,...>
//或
template<typename 模板名,typename 模板名,...>
使用语法
//在该方法任何一处使用数据类型的地方都可以使用模板名替换
注意
1,函数模板可以自动推导参数的类型 , 但是不会进行类型转换
2,函数模板可以自动类型推导 , 也可以显式指定类型
3,只能在声明的所在函数中使用
示例
cpp
//定义模板
template<class T>
//在形参中使用模板
void MySwap(T& a,T& b){
//在局部变量中使用模板
T temp = a;
a = b;
b = temp;
}
void test01(){
int a = 10;
int b = 20;
cout << "a:" << a << " b:" << b << endl;
//1.函数模板可以自动推导参数的类型
MySwap(a,b);
cout << "a:" << a << " b:" << b << endl;
char c1 = 'a';
char c2 = 'b';
cout << "c1:" << c1 << " c2:" << c2 << endl;
//2. 函数模板显式指定类型
MySwap<char>(c1, c2);
cout << "c1:" << c1 << " c2:" << c2 << endl;
}
补充
函数模板会编译两次:
1,在加载时对函数模板进行第一次编译
2,在调用时推导 T 的类型再次将函数模板编译为模板函数
普通函数和函数模板的区别
1, 函数模板不允许自动类型转化 普通函数能够自动进行类型转。 ( 示例 1)
cpp
template<typename T>
void func(T a, T b){}
//普通函数
void func02(int a, int b){}
void test02()
{
func(10, 20);
//func(10, 'a');//err
func02(10, 'a');
}
2, 函数模板和普通函数同时识别 , 优先使用普通函数 , 加 <> 强制使用函数模板
cpp
template<typename T>
void func(T a, T b){
cout<<"函数模板"<<endl;
}
//普通函数
void func(int a, int b){
cout<<"普通函数"<<endl;
}
void test02()
{
//普通函数
func(10, 20);
//显示调用函数模板
func<>(10, 20);
}
3, 函数模板可以使用 <>, 普通函数不行
函数模板的局限性
示例
cpp
template <class Z>
void print(Z z)
{
cout << z << endl;
}
class Person{
private:
char name[32];
int age;
public:
Person()
{
}
Person(char name[],int age)
{
strcpy(this -> name,name);
this -> age = age;
}
~Person()
{
}
};
int main(int argc, char *argv[])
{
Person p("张三",18);
//此时函数模板已经推导出类型为Person,但是因为Person没有对<<运算符进行重
载,所以依据无法使用
print(p);
return 0;
}
解决方案 1 ,重载 << 函数
cpp
template <class Z>
void print(Z z)
{
cout << z << endl;
}
class Person{
friend ostream& operator <<(ostream& out,Person& p);
private:
char name[32];
int age;
public:
Person()
{
}
Person(char name[],int age)
{
strcpy(this -> name,name);
this -> age = age;
}
~Person()
{
}
};
ostream& operator <<(ostream& out,Person& p)
{
out << p.name << " " << p.age << endl;
return out;
}
int main(int argc, char *argv[])
{
Person p("张三",18);
print(p);
return 0;
}
解决方案 2 :函数模板具体化
cpp
template <class Z>
void print(Z z)
{
cout << z << endl;
}
class Person{
friend void print<Person>(Person p);
private:
char name[32];
int age;
public:
Person()
{
}
Person(char name[],int age)
{
strcpy(this -> name,name);
this -> age = age;
}
~Person()
{
}
};
//函数模板具体化
template<> void print<Person>(Person p)
{
cout << p.name << " " << p.age << endl;
}
int main(int argc, char *argv[])
{
Person p("张三",18);
print(p);
print(10);
return 0;
}
类模板
步骤
定义
使用
定义
语法
cpp
template <class 模板名1,class 模板名2>
位置:类的上面
使用
创建对象
语法
类名 < 实际数据类型 1, 实际数据类型 2,...> 对象名 ( 实参列表 );
注意
类模板示例化对象是不能自动推导类型 , 需指定
示例
cpp
#include <iostream>
#include <cstring>
using namespace std;
//定义模板
template <class T,class Z>
class Data{
private:
T t1;
Z z1;
public:
Data(){}
Data(T t,Z z)
{
t1 = t;
z1 = z;
}
void showData()
{
cout << t1 << " " << z1 << endl;
}
};
void test01(){
//创建模板类对象
//Data d01;//类模板示例化对象是不能自动推导类型,错误的
//Data d02(10,'a');//类模板示例化对象是不能自动推导类型,错误的
Data<int,char> d03(10,'a');//类模板示例化对象时必须指定模板对应的数据
类型
Data<string,float> d04("abc",18.8f);
d03.showData();
d04.showData();
}
int main(int argc, char *argv[])
{
test01();
return 0;
}
类模板成员函数外部实现
注意 : 外部实现函数时也需定义与类相同的函数模板
示例
cpp
#include <iostream>
#include <cstring>
using namespace std;
//定义模板
template <class T,class Z>
class Data{
private:
T t1;
Z z1;
public:
Data(){}
Data(T t,Z z);
void showData();
};
template<class T, class Z>
Data<T,Z>::Data(T t, Z z)
{
t1 = t;
z1 = z;
}
template<class T, class Z>
void Data<T,Z>::showData()
{
cout << t1 << " " << z1 << endl;
}
void test01(){
Data<string,float> d01("abc",18.8f);
d01.showData();
}
int main(int argc, char *argv[])
{
test01();
return 0;
}
类模板的分文件使用
注意:
在实例化模板之前,编译器对模板的定义体是不处理的
在实例化模板时编译器必须在上下文中可以查看到其定义实体
因此模板的实例化与定义体必须放到同一文件中。
但是如果文件名为.h 皆为是头文件 , 在头文件中定义函数不符合头文件的将声明与实例分开的规则
顾将头文件名改为hpp, 并在前内部进行示例化
示例
data.hpp
cpp
#ifndef DATA_H
#define DATA_H
#include<iostream>
using namespace std;
template<class T1, class T2>
class Data
{
private:
T1 a;
T2 b;
public:
Data();
Data(T1 a, T2 b);
void showData();
};
template<class T1, class T2>
Data<T1,T2>::Data()
{
}
template<class T1, class T2>
Data<T1,T2>::Data(T1 a, T2 b)
{
this->a = a;
this->b = b;
}
template<class T1, class T2>
void Data<T1,T2>::showData()
{
cout<<"a="<<a<<", b="<<b<<endl;
}
#endif // DATA_H
main.cpp
cpp
#include <iostream>
#include "data.hpp"
using namespace std;
int main(int argc, char *argv[])
{
Data<int,char> ob1(100, 'a');
ob1.showData();
return 0;
}
类模板作为函数参数
注意 : 该函数必须是类模板的友元函数
示例
cpp
#include <iostream>
using namespace std;
template <class X>
class A{
friend void printA(A<X>& a);
template <class T>
friend void printB(A<T>& a);
private:
X x;
public:
A(){}
A(X x)
{
this -> x = x;
}
void showA()
{
cout << x << endl;
}
};
//普通函数作为类模板的友缘函数
void printA(A<int> &a)
{
cout << a.x << endl;
}
void test01()
{
A<int> a(10);
printA(a);
}
//模板函数作为类模板的友缘函数
template <class T>
void printB(A<T> &a)
{
cout << a.x << endl;
}
void test02()
{
A<int> a(10);
printB(a);
}
int main(int argc, char *argv[])
{
test02();
return 0;
}
类模板的派生
1, 类模板派生普通子类
示例
cpp
#include <iostream>
using namespace std;
template <class T>
class Fu{
private:
T t;
public:
Fu()
{
}
Fu(T t)
{
this -> t = t;
}
void showFu()
{
cout << t << endl;
}
};
//子类实例父类模板数据
class Zi01:public Fu<int>
{
public:
Zi01()
{
}
Zi01(int x):Fu(x)
{
}
};
int main(int argc, char *argv[])
{
Zi01 z01(10);
z01.showFu();
return 0;
}
2, 类模板派生类模板
示例
cpp
#include <iostream>
using namespace std;
template <class T>
class Fu{
private:
T t;
public:
Fu()
{
}
Fu(T t)
{
this -> t = t;
}
void showFu()
{
cout << t << endl;
}
};
template <class X>
class Zi02:public Fu<X>{
public:
Zi02()
{
}
Zi02(X x):Fu<X>(x)
{
}
};
int main(int argc, char *argv[])
{
Zi02<char> z02('a');
return 0;
}
练习自定义集合
MyArr.hpp
cpp
#ifndef MYARR_H
#define MYARR_H
#include <iostream>
using namespace std;
template <class T>
class MyArr{
private:
T* ts;
int size;//数据长度
int count;//容量
public:
MyArr();
MyArr(const MyArr& myarr);
~MyArr();
T get(int index);
bool add(T t);
int getSize();
};
template<class T>
MyArr<T>::MyArr()
{
size = 0;
count = 5;
ts = new T[count];
}
template <class T>
MyArr<T>::MyArr(const MyArr<T>& myarr)
{
this->ts = new T[myarr.count];
this->size = myarr.size;
this->count= myarr.count;
/**
* C库函数void *memcpy(void *str1, const void *str2, size_t n)
* 从存储区 str2 复制 n 个字节到存储区 str1。
*/
memcpy(ts,myarr.ts,this->size*sizeof(T));
}
template <typename T>
MyArr<T>::~MyArr()
{
if(ts == NULL)
{
return;
}
delete[] ts;
ts = NULL;
}
template <typename T>
T MyArr<T>::get(int index)
{
if(index < 0 || index > size)
{
return NULL;
}
return ts[index];
}
template <class T>
bool MyArr<T>::add(T t)
{
if(size == count)
{
T * newTs = new T[count * 2];
memcpy(newTs,ts,sizeof(T)*size);
delete[] ts;
ts = newTs;
count *= 2;
}
ts[size] = t;
size++;
return true;
}
template<class T>
int MyArr<T>::getSize()
{
return size;
}
#endif // MYARR_H
main.cpp
cpp
#include <iostream>
#include "myarr.hpp"
#include "cstring"
using namespace std;
int main(int argc, char *argv[])
{
MyArr<int> arr01;
arr01.add(1);
arr01.add(2);
arr01.add(3);
arr01.add(4);
arr01.add(5);
arr01.add(6);
int s01 = arr01.getSize();
for(int i = 0; i < s01; i++)
{
cout << arr01.get(i) << endl;
}
MyArr<int> arr02;
arr02.add('a');
arr02.add('b');
arr02.add('c');
arr02.add('d');
arr02.add('e');
arr02.add('f');
int s02 = arr02.getSize();
for(int i = 0; i < s02; i++)
{
cout << (char)arr02.get(i) << endl;
}
MyArr<string> arr03;
arr03.add("abc");
arr03.add("123");
arr03.add("qaz");
arr03.add("hello");
arr03.add("hi");
int s03 = arr03.getSize();
for(int i = 0; i < s03; i++)
{
cout << arr03.get(i) << endl;
}
return 0;
}
类型转换
上行转换
概念 : 父类指针指向子类空间
注意
安全的
下行转换
子类指针指向父类空间
注意
不安全
类型转换
静态转换
语法
static_cast<T>(要转换的数据)
示例
// 基本类型转换 支持
int num = static_cast < int > ( 3.14f );
// 基本指针类型转换 不支持
float f = 0.0f ;
//int *p1 = static_cast<int *>(&f);
// 上行转换 支持(安全)
Base * p2 = static_cast < Base *> ( new Son );
// 下行转换 支持(不安全)
Son * p3 = static_cast < Son *> ( new Base );
// 不相关类型转换 不支持
//Son *p4 = static_cast<Son *>(new Other);
动态转换
语法
dynamic_cast<T>(要转换的数据 )
示例
// 基本类型转换 不支持
//int num = dynamic_cast<int>(3.14f);
// 基本指针类型转换 不支持
float f = 0.0f ;
//int *p1 = dynamic_cast<int *>(&f);
// 上行转换 支持(安全)
Base * p2 = dynamic_cast < Base *> ( new Son );
// 下行转换 不支持(不安全)
//Son *p3 = dynamic_cast<Son *>(new Base);
// 不相关类型转换 不支持
//Son *p4 = dynamic_cast<Son *>(new Other);
常量转换
语法
const_cast<T>
注意
只能对指针与引用的变量使用
示例
cpp
//将非const 转换成 const
int num = 10;
const int *p1 = const_cast<const int *>(&num);
//将const 转换成 非const
const int data=0;
int *p = const_cast<int *>(&data);
5, 重新解释转换(垃圾一个不用看)