C++ 模版★★★
Hello!!大家早上中午晚上好!!今天来复习模版部分知识点!!!
一、什么是模版?
模版是泛型编程的基础 -> 什么是泛型编程?-> 代码复用的一种手段 -> 什么手段? -> 编写与类型无关的通用代码,所以模版就是一段与类型无关的通用代码!
二、什么是函数模版?
模版分两类,一类是函数模版,一类是类模版;
2.1函数模版的定义
函数模版代表了一个函数家族,该函数模版与类型无关,使用时被参数化,根据实参类型生成特定类型的函数;
2.2函数模版语法
template<typename T1,typename T2,...,typename Tn>
返回类型 函数名(参数列表){}
2.3简单的函数模版
template <typename T>
T Add(T a1, T a2)
{
return a1 + a2;
}
int main()
{
int a = 1, b = 2;
double a1 = 1.2, b1 = 2.2;
cout<<Add(a, b)<<endl;
cout << Add(a1, b1) << endl;
return 0;
}
运行:
2.4模版的实例化:
只要写出通用的函数模版,编译器会根据传入的实参自动实例化出对应参数类型的函数!
2.5显式实例化
上面编译器根据参数类型自动推导出对应的函数,属于隐式实例化;当编译器不能自动推导的时候就需要显式实例化:
当Add传参第一个参数为int,第二个参数为double,且模版参数只有一个的时候,编译器不懂该实例化成int还是double,就会报错;这时有两个方法解决:一是强制类型转换,二是显式实例化
显式实例化(只需要在函数名后加<类型名>):
注意:当参数类型不匹配时,编译器会尝试类型转换,如果转换失败还是会报错!
三、类模版
3.1类模版的定义
template <class T1,class T2,...,calss Tn>
class 类模版名
{
//类成员
};
简单的类模版:
template <class T>//template(关键字) + <class/typename T(T是根据传入的类型实例化出对应的类型)>
class A
{
public:
A(T a,T b)
{
_a = a;
_b = b;
}
T Add()
{
return _a + _b;
}
private:
T _a;
T _b;
};
int main()
{
A<int> a(3, 5);//显式实例化对象a,并用3,5初始化
cout << a.Add() << endl;//调用对象a里的成员函数Add()
A<double> b(3.3, 5.5);
cout << b.Add() << endl;
return 0;
}
运行:
3.2类模板的实例化
3.3非类型模版参数
模版参数分类型模版参数和非类型模版参数;
跟在class 或typename 后面的是类型模版参数,没有跟在class或typename后面的是非类型模版参数;
非类型模版参数:必须是常量,必须在编译时就能确定!
简单的非类型模版参数:
template <class T,size_t N=20>//T为类型模版参数,size_t N=20为非类型模版参数
class Array
{
public:
T& operator[](size_t index)
{
return _arr[index];
}
size_t size()
{
return _size;
}
private:
T _arr[N];//使用非类型模版参数定义一个静态数组
size_t size=0;
};
注意:非类型模版参数不能是 浮点型、不能是 类对象、不能是字符串!
四、模版的特化
对于某些特殊的类型如果不进行特殊处理会参数错误的结果,这时候就需要模版的特化!!
模版的特化必须要有模版原型才能特化!!
例:两个数比小
4.1函数模版的特化
建议直接写函数就行!
4.2类模版的特化
类模板原形:
template <class T1,class T2>
class A
{
public:
A(T1 a, T2 b)
{
_a = a;
_b = b;
}
private:
T1 _a;
T2 _b;
};
类模版全特化:
template<>//template 后面+<>
class A<double, double>
{
public:
A(double a, double b)
{
cout << "A(double a,double b)" << endl;
_a = a;
_b = b;
}
private:
double _a;
double _b;
};
类模板的半特化:
template<class T1>
class A<T1, double>
{
public:
A(T1 a, double b)
{
cout << "A(T1 a,double b)" << endl;
_a = a;
_b = b;
}
private:
T1 _a;
double _b;
};
类模版的偏特化(针对模版参数更进一步的限制):
//两个参数偏特化为指针类型
template <class T1, class T2>
class A<T1*, T2*>
{
public:
A(T1* a, T2* b)
{
cout << "A(T1* a,T2* b)" << endl;
*_a = *a;
*_b = *b;
}
private:
T1* _a;
T2* _b;
};
//两个参数偏特化为引用类型
template <class T1, class T2>
class A<T1&, T2&>
{
public:
A(T1&a, T2& b)
{
cout << "A(T1& a,T2& b)" << endl;
_a = a;
_b = b;
}
private:
T1& _a;
T2& _b;
};
测试:
int main()
{
int aa = 10;
int bb = 20;
A<int, int> a(1, 2);
A<double, double> b(1.1, 2.2);
A<int, double> c(11, 22.2);
A<int*, int*>d(&aa, &bb);
A<int&, int&>e(aa, bb);
return 0;
}
运行:
五、模版分离编译
当类模板的声明和定义分离在不同文件时,执行会发生错误
//a.cpp定义
#define _CRT_SECURE_NO_WARNINGS 1
#include "a.h"
template<class T>
T Add(T a, T b)
{
return a + b;
}
//a.h声明
#pragma once
template <class T>
T Add(T t1, T t2);
//test.cpp测试调用
#include "a.h"
int main()
{
cout << Add(1, 2) << endl;
cout << Add(1.1, 2.2) << endl;
return 0;
}
运行报错:
原因分析:
因为每个.cpp文件都是分离编译生成各自的.obj文件,编译时,编译器看向.app文件时,没有看到对Add函数模版的实例化,不会实例化出具体的Add函数代码;然后在链接阶段,test.cpp调用了Add函数发生错误!!
解决方法:
声明定义在同一文件中!
#pragma once
//在.h文件内直接定义,不分离
template<class T>
T Add(T a, T b)
{
return a + b;
}
//test.cpp
#include "a.h"
int main()
{
cout << Add(1, 2) << endl;
cout << Add(1.1, 2.2) << endl;
return 0;
}
好了,今天就复习到这里!!如果您有收获请点赞收藏+关注哦!谢谢!!!
如果您有更好的建议欢迎评论区留言!!
咱下期见!!!