Template
)是允许函数或者类通过泛型(generic types)的形式表现或运行的特性。template <模板形参表> 函数返回类型 函数(形参表);
template <模板形参表>
函数返回类型 函数(形参表){
函数体;
};
例如:
template <typename T> T Max(T a,T b){
return a>b?a:b;
}
函数(实参表)
产生模板特定类型的函数或者类的过程称为实例化
调用函数模板与调用函数完全一致
Max()
StringToNumber()
#include
#include
using namespace std;
/*
int Max(int a,int b){
return a>b?a:b;
}
double Max(double a,double b){
return a>b?a:b;
}
char Max(char a,char b){
return a>b?a:b;
}
*/
//使用模板,避免代码大量重复
template <typename T>
T Max(T a,T b){ //根据a和b的输入,自动识别类型
return a>b?a:b;
}
/*
void Swap(int& a,int& b){
int c = a;
a = b;
b = c;
}
void Swap(char& a,char& b){
char c = a;
a = b;
b = c;
}
*/
template<typename T>
void Swap(T& a,T& b){
T c = a;
a = b;
b = c;
}
template <typename T>
string NumToString(T num){
ostringstream oss;
oss << num;
return oss.str();
}
template <typename T>
T StringToNumber(const string& s){
T res;
istringstream iss(s);
iss >> res;
return res;
}
int main(){
cout << Max(12,10) << endl;
//cout << Max(1,3.4) << endl; //报错
//解决办法
cout << Max((double)1,2.2) << endl; //强转 double Max(double a,double b)
cout << Max<double>(10,2.2) << endl; //模板 double Max(double a,double b)
cout << Max('a','b') << endl;
int a = 1,b = 2;
cout << a << "," << b << endl;
Swap(a,b);
cout << a << "," << b << endl;
char c1 = 'c',c2 = 'd';
cout << c1 << "," << c2 << endl;
Swap(c1,c2);
cout << c1 << "," << c2 << endl;
cout << NumToString(1234) << endl;
cout << NumToString(1.234) << endl;
cout << StringToNumber<int>("1234") << endl; //告诉模板传入参数类型
cout << StringToNumber<float>("1.234") << endl; //告诉模板传入参数类型
}
12
2.2
10
b
1,2
2,1
c,d
d,c
1234
1.234
1234
1.234
template <模板形参表> class 类名;
template <模板形参表>
class 类名 {
}
类名<模板实参表> 对象;
class
类型形参或者typename
类型形参注:类模板的声明与实现通常都写在头文件中,是不能够分开的。
#include
#include
using namespace std;
template <typename T>
class Circle{
T r;
public:
Circle(T r):r(r){}
float GetLength()const{
return 2*M_PI*r;
}
float GetArea()const{
return M_PI*r*r;
}
};
int main(){
//模板实例化
Circle<int> c(3);
cout << c.GetLength() << "," << c.GetArea() << endl;
Circle<float> c2(3.14);
cout << c2.GetLength() << "," << c2.GetArea() << endl;
}
18.8496,28.2743
19.7292,30.9748
list
和字符list
#include
using namespace std;
template <typename T>
class SeqList{
T* list;
size_t size;
public:
SeqList():list(NULL),size(0){}
void Append(T num){
T* temp = new T[size+1];
for(int i = 0;i < size;++i){
temp[i] = list[i];
}
temp[size] = num;
++size;
delete [] list;
list = temp;
}
size_t GetSize(){
return size;
}
T& operator[](T i){
return list[i];
}
void ShowList(){
for(int i = 0;i < size;++i){
cout << list[i] << " ";
}
cout << endl;
}
};
int main(){
SeqList<int> l;
l.Append(1);
l.Append(2);
l.Append(3);
l.Append(4);
l.ShowList();
SeqList<char> c;
c.Append('H');
c.Append('E');
c.Append('L');
c.Append('L');
c.Append('O');
c.ShowList();
}
1 2 3 4
H E L L O
实例化有两类:
显示实例化:代码中明确指定类型的实例化
隐式初始化:根据参数类型自动匹配的实例化
注:
1、类模板参数允许自动类型转换(隐式转换);
2、函数模板参数不允许自动类型转换(隐式转换)
3、在模板参数列表中,class和typename完全一样。但是在语义上,class
表示类,typename
代表所有类型(类以及基本类型)。
4、请尽量使用typename
函数模板实参类型不一致问题
template <typename T>
inline const T& Max(const T& a, const T& b){
return a>b?a:b;
}
模板实例化时,如果输入数据为:
Max(2,2.4)
参数推导会出现模板实参类型int
与double
不一致的错误。
解决方法:
1、每个模板参数独立类型
template <typename T , typename U> inline const T& Max(const T& a, const U& b){
return a>b?a:b;
}
注意:这种解决方法还有一个问题,就是返回值只能强制设置为T或者U,不能自动推导。C++11的后置推导解决这个问题。
template <typename T, typename U>
inline auto Max(const T& a, const U& b)->decltype(a>b?a:b)
{
return a>b?a:b;
}
2、显示指定模板实参类型
Max<int>(2,2.4)
或者
Max<double>(2,2.4)
3、实参强制类型转换
Max(2,static_cast<int>(2.4))
或者
Max(static_cast<double>(2),2.4)
注:模板参数推导不允许类型自动转换,模板参数必须严格匹配。
函数模板实例显示指定模板实参可以显示指定模板实参,也可以不指定(类型自动推导),类模板实例化必须
template<typename T>
void Func(const T& n){}
// 特化
template<>
void Func(const int& n){}
实例:
#include
#include
#include
using namespace std;
/*
int Max(int a,int b){
return a>b?a:b;
}
double Max(double a,double b){
return a>b?a:b;
}
char Max(char a,char b){
return a>b?a:b;
}
*/
//命名空间
namespace My{
template <typename T>
T max(T a,T b){ //根据a和b的输入,自动识别类型
cout << "Max(" << a << "," << b << ")=";
return a>b?a:b;
}
//模板特化,特化的原因是有些输入参量,模板处理不了
template<>
const char* max(const char* a,const char* b){
return strcmp(a,b) > 0? a:b;
}
};
//使用模板,避免代码大量重复
template <typename T>
T Max(T a,T b){ //根据a和b的输入,自动识别类型
return a>b?a:b;
}
/*
void Swap(int& a,int& b){
int c = a;
a = b;
b = c;
}
void Swap(char& a,char& b){
char c = a;
a = b;
b = c;
}
*/
template<typename T>
void Swap(T& a,T& b){
T c = a;
a = b;
b = c;
}
template <typename T>
string NumToString(T num){
ostringstream oss;
oss << num;
return oss.str();
}
template <typename T>
T StringToNumber(const string& s){
T res;
istringstream iss(s);
iss >> res;
return res;
}
int main(){
cout << My::max(1,2) << endl;
cout << Max(12,10) << endl;
//cout << Max(1,3.4) << endl; //报错
//解决办法
cout << Max((double)1,2.2) << endl; //强转 double Max(double a,double b)
cout << Max<double>(10,2.2) << endl; //模板 double Max(double a,double b)
cout << Max('a','b') << endl;
cout << max(string("abcd"),string("efg")) << endl;
//cout << Max("ancd","efg") << endl; // 这样也可以???
cout << Max("abcd","efg") << endl; //输出结果错误,因为字符串不能用>或<进行比较,所以不能调用模板
//解决办法:模板特化
cout << My::max("abcd","efg") << endl;
int a = 1,b = 2;
cout << a << "," << b << endl;
Swap(a,b);
cout << a << "," << b << endl;
char c1 = 'c',c2 = 'd';
cout << c1 << "," << c2 << endl;
Swap(c1,c2);
cout << c1 << "," << c2 << endl;
cout << NumToString(1234) << endl;
cout << NumToString(1.234) << endl;
cout << StringToNumber<int>("1234") << endl; //告诉模板传入参数类型
cout << StringToNumber<float>("1.234") << endl; //告诉模板传入参数类型
}
Max(1,2)=2
12
2.2
10
b
efg
efg
abcd
efg
1,2
2,1
c,d
d,c
1234
1.234
1234
1.234
// 模板
template<class T>
class Test{};
// 全特化
template<>
class Test<int*>{};
实例:
#include
#include
using namespace std;
template <typename T>
class Circle{
T r;
public:
Circle(T r):r(r){}
float GetLength()const{
return 2*M_PI*r;
}
float GetArea()const{
return M_PI*r*r;
}
};
//类特化
template<>
class Circle<int>{
int r;
public:
Circle(int r):r(r){}
float GetLength()const{
cout << "Circle:" ;
return 2*M_PI*r;
}
float GetArea()const{
cout << "Circle:" ;
return M_PI*r*r;
}
};
int main(){
Circle<int> c(3);
cout << c.GetLength() << "," << c.GetArea() << endl; //调用特化模板
Circle<float> c2(3.14);
cout << c2.GetLength() << "," << c2.GetArea() << endl;
}
Circle<int>:18.8496,28.2743
19.7292,30.9748
template<typename T1,typename T2>
class Test{};
将模板参数偏特化为相同类型
template<typename T>
class Test<T,T>{};
将一个模板参数特化成具体类型
template<typename T>
class Test<T,int>{};
把两个类型偏特化成指针类型
template<typename T1,typename T2>
class Test<T1*,T2*>{};
注:类模板特化,相当于函数模板的重载
全特化和偏特化的编码区别:
全特化的模板参数列表为空template<>
,偏特化的模板参数列表不为空。
实例:
#include
#include
using namespace std;
// 引用类型模板
template <typename T>
bool Equal(const T& a,const T& b){
return a == b;
}
// 特化成浮点型
template<>
bool Equal(const double& a,const double& b){
return abs(a-b) < 1e-6;
}
// -------------------------------------------------
// 指针类型模板(函数模板重载)
template<typename T>
bool Equal(const T* a,const T* b){
return *a==*b;
}
// 特化成char*
template<>
bool Equal(const char* a,const char* b){
return strcmp(a,b)==0;
}
int main(){
cout << Equal(1,1) << endl;
cout << Equal(1.2,1.2) << endl;
cout << Equal(string("abc"),string("abc")) << endl;
cout << Equal(1,2) << endl;
cout << Equal(1.2,1.21) << endl;
cout << Equal(string("abcd"),string("abc")) << endl;
cout << Equal(1.2,(10.2-9)) << endl;
int arr[] = {1,2,3,1};
cout << Equal(arr,arr+3) << endl; // bool Equal(int*,int*)
cout << Equal("abc","abcd") << endl;
}
综合应用:空间坐标中的点、线、三角形和直角三角形
#include
#include
#include
using namespace std;
class Point {
protected:
int x,y;
public:
Point(int x,int y):x(x),y(y) {}
int GetX()const {
return x;
}
int GetY()const {
return y;
}
friend ostream& operator<<(ostream& os,const Point& p) { //流对象不能拷贝构造,所以要加引用
return os << "(" << p.x << "," << p.y << ")";
}
};
// 继承的本质是A is a B
// Circle is a Point ???
// 但是圆本身不是点
// 说圆包含一个点更合适
// A has a B
// Circle has a Pointer
class Circle { /*:public Point*/
int r;
Point center;
public:
Circle(int x,int y,int r):center(x,y),r(r) {}
int GetR()const {
return r;
}
Point GetCenter()const {
return center;
}
friend ostream& operator<<(ostream& os,const Circle& c) {
return os << "(" << c.center.GetX() << "," << c.center.GetY() << "," << c.r << ")";
}
};
class Line {
Point a;
Point b;
public:
Line(const Point& a,const Point& b):a(a),b(b) {}
float GetLength()const {
int w = a.GetX() - b.GetX();
int h = a.GetY() - b.GetY();
return sqrt(w*w+h*h);
}
bool IsParallel(const Line& line)const {
int x1 = a.GetX() - b.GetX();
int y1 = a.GetY() - b.GetY();
int x2 = line.a.GetX() - line.b.GetX();
int y2 = line.a.GetY() - line.b.GetY();
return x1*y2 == x2*y1;
}
bool IsVertical(const Line& line)const {
int x1 = a.GetX() - b.GetX();
int y1 = a.GetY() - b.GetY();
int x2 = line.a.GetX() - line.b.GetX();
int y2 = line.a.GetY() - line.b.GetY();
return x1*x2 + y2*y1 == 0;
}
friend ostream& operator<<(ostream& os,const Line& l) {
return os << l.a << "~" << l.b;
}
};
class Triangle {
protected:
Point a;
Point b;
Point c;
public:
Triangle(Point a,Point b,Point c):a(a),b(b),c(c) {
ostringstream oss;
oss << a << b << c;
if(Line(a,b).IsParallel(Line(a,c))) throw invalid_argument(oss.str()+"三点不能共线");
}
float GetLength()const {
return Line(a,b).GetLength() + Line(a,c).GetLength() + Line(b,c).GetLength();
}
virtual float GetArea()const {
float p = GetLength()/2.0;
return sqrt(p*Line(a,b).GetLength()+p*Line(a,c).GetLength()+p*Line(b,c).GetLength());
}
friend ostream& operator<<(ostream& os,const Triangle& t) {
return os << "[" << t.a << "," << t.b << "," << t.c << "]";
}
};
class RightAngleTriangle:public Triangle {
public:
RightAngleTriangle(const Point& a,const Point& b,const Point& c):Triangle(a,b,c) {
bool vertical = Line(a,b).IsVertical(Line(a,c)) || Line(a,c).IsVertical(Line(b,c)) || Line(a,b).IsVertical(Line(b,c));
if(!vertical) throw invalid_argument("不能构成直角三角形");
}
float GetArea()const override {
cout << "直角三角形面积:" << endl;
Line l1(a,b);
Line l2(a,c);
Line l3(b,c);
if(l1.IsVertical(l2)) {
return l1.GetLength()*l2.GetLength()/2.0;
} else if(l1.IsVertical(l3)) {
return l1.GetLength()*l3.GetLength()/2.0;
} else if(l2.IsVertical(l3)) {
return l2.GetLength()*l3.GetLength()/2.0;
} else {
return 0;
}
}
};
void PrintTriangle(const Triangle& t) {
cout << t.GetLength() << " " << t.GetArea() << endl;
}
int main() {
Point p(3,4);
cout << p << endl;
Circle c(1,2,3);
cout << c << endl;
Point o(0,0);
Line l(o,p);
cout << l << ":" << l.GetLength() << endl;
Triangle t(p,o,Point(3,0));
cout << t << ": Length" << t.GetLength() << " Area:" << t.GetArea() << endl;
Line l1(o,Point(0,1));
Line l2(o,Point(0,2));
cout << l1.IsParallel(l2) << " " << l1.IsVertical(l2) << endl;
//Triangle t2(o,Point(0,1),Point(0,2));
RightAngleTriangle t3(o,p,Point(3,0));
cout << t3.GetArea() << endl;
//多态
/* //不常用写法
Triangle& t4 = t3;
cout << t4.GetArea() << endl;
*/
//常用写法
PrintTriangle(t3);
}
(3,4)
(1,2,3)
(0,0)~(3,4):5
[(3,4),(0,0),(3,0)]: Length12 Area:8.48528
1 0
直角三角形面积:
6
12 直角三角形面积:
6
改进:加入抽象类Shape统一所有类
#include
#include
#include
using namespace std;
class Point; //前置声明,因为抽象类中用到Point
//包含纯虚函数的类成为抽象类,抽象类只定义不实现
class Shape {
public:
//纯虚函数
virtual bool Contained(const Point& p) const = 0;
};
//定义抽象类的目的是把下面所有类进行统一
class Point:public Shape {
protected:
int x,y;
public:
Point(int x,int y):x(x),y(y) {}
int GetX()const {
return x;
}
int GetY()const {
return y;
}
friend ostream& operator<<(ostream& os,const Point& p) { //流对象不能拷贝构造,所以要加引用
return os << "(" << p.x << "," << p.y << ")";
}
bool Contained(const Point& p)const override {
return p.x == x && p.y == y;
}
};
// 继承的本质是A is a B
// Circle is a Point ???
// 但是圆本身不是点
// 说圆包含一个点更合适
// A has a B
// Circle has a Point
// 可以说Circle is a Shape
class Line:public Shape {
Point a;
Point b;
public:
Line(const Point& a,const Point& b):a(a),b(b) {}
float GetLength()const {
int w = a.GetX() - b.GetX();
int h = a.GetY() - b.GetY();
return sqrt(w*w+h*h);
}
bool IsParallel(const Line& line)const {
int x1 = a.GetX() - b.GetX();
int y1 = a.GetY() - b.GetY();
int x2 = line.a.GetX() - line.b.GetX();
int y2 = line.a.GetY() - line.b.GetY();
return x1*y2 == x2*y1;
}
bool IsVertical(const Line& line)const {
int x1 = a.GetX() - b.GetX();
int y1 = a.GetY() - b.GetY();
int x2 = line.a.GetX() - line.b.GetX();
int y2 = line.a.GetY() - line.b.GetY();
return x1*x2 + y2*y1 == 0;
}
friend ostream& operator<<(ostream& os,const Line& l) {
return os << l.a << "~" << l.b;
}
bool Contained(const Point& p) const override {
bool parallel = Line(a,p).IsParallel(Line(b,p));
return parallel && (min(a.GetX(),b.GetX()) <= p.GetX() && p.GetX() <= max(a.GetX(),b.GetX()))
&& (min(a.GetY(),b.GetY()) <= p.GetY() && p.GetY() <= max(a.GetY(),b.GetY()));
}
};
class Circle:public Shape {
int r;
Point center;
public:
Circle(int x,int y,int r):center(x,y),r(r) {}
int GetR()const {
return r;
}
Point GetCenter()const {
return center;
}
friend ostream& operator<<(ostream& os,const Circle& c) {
return os << "(" << c.center.GetX() << "," << c.center.GetY() << "," << c.r << ")";
}
bool Contained(const Point& p)const override {
return Line(p,center).GetLength() <= r;
}
};
class Triangle :public Shape {
protected:
Point a;
Point b;
Point c;
public:
Triangle(Point a,Point b,Point c):a(a),b(b),c(c) {
ostringstream oss;
oss << a << b << c;
if(Line(a,b).IsParallel(Line(a,c))) throw invalid_argument(oss.str()+"三点不能共线");
}
float GetLength()const {
return Line(a,b).GetLength() + Line(a,c).GetLength() + Line(b,c).GetLength();
}
virtual float GetArea()const {
float p = GetLength()/2.0;
return sqrt(p*Line(a,b).GetLength()+p*Line(a,c).GetLength()+p*Line(b,c).GetLength());
}
friend ostream& operator<<(ostream& os,const Triangle& t) {
return os << "[" << t.a << "," << t.b << "," << t.c << "]";
}
bool Contained(const Point& p) const override {
// TODO:
return false;
}
};
class RightAngleTriangle:public Triangle {
public:
RightAngleTriangle(const Point& a,const Point& b,const Point& c):Triangle(a,b,c) {
bool vertical = Line(a,b).IsVertical(Line(a,c)) || Line(a,c).IsVertical(Line(b,c)) || Line(a,b).IsVertical(Line(b,c));
if(!vertical) throw invalid_argument("不能构成直角三角形");
}
float GetArea()const override {
cout << "直角三角形面积:" << endl;
Line l1(a,b);
Line l2(a,c);
Line l3(b,c);
if(l1.IsVertical(l2)) {
return l1.GetLength()*l2.GetLength()/2.0;
} else if(l1.IsVertical(l3)) {
return l1.GetLength()*l3.GetLength()/2.0;
} else if(l2.IsVertical(l3)) {
return l2.GetLength()*l3.GetLength()/2.0;
} else {
return 0;
}
}
};
void PrintTriangle(const Triangle& t) {
cout << t.GetLength() << " " << t.GetArea() << endl;
}
int main() {
Point p(3,4);
cout << p << endl;
Circle c(1,2,3);
cout << c << endl;
Point o(0,0);
Line l(o,p);
cout << l << ":" << l.GetLength() << endl;
Triangle t(p,o,Point(3,0));
cout << t << ": Length" << t.GetLength() << " Area:" << t.GetArea() << endl;
Line l1(o,Point(0,1));
Line l2(o,Point(0,2));
cout << l1.IsParallel(l2) << " " << l1.IsVertical(l2) << endl;
//Triangle t2(o,Point(0,1),Point(0,2));
RightAngleTriangle t3(o,p,Point(3,0));
cout << t3.GetArea() << endl;
//多态
/* //不常用写法
Triangle& t4 = t3;
cout << t4.GetArea() << endl;
*/
//常用写法
PrintTriangle(t3);
Shape* arr[] = {&p,&c,&o,&l,&l1,&l2,&t,&t3};
Point point(0,0);
cout << "相交图形";
for(int i = 0;i<8;++i){
if(arr[i]->Contained(point)){
cout << i << endl;
}
}
}
(3,4)
(1,2,3)
(0,0)~(3,4):5
[(3,4),(0,0),(3,0)]: Length12 Area:8.48528
1 0
直角三角形面积:
6
12 直角三角形面积:
6
相交图形1
2
3
4
5