C++提高编程 模板
函数模板
函数模板语法
普通函数实现交换
void swap1(int &a,int &b)
{
int t=a;
a=b;
b=t;
}
void swap2(double &a,double &b)
{
double t=a;
a=b;
b=t;
}
void test()
{
int a=10,b=20;
double c=-10,d=-20;
swap1(a,b);
swap2(c,d);
cout<<"a= "<<a<<" b= "<<b<<endl;
cout<<"c= "<<c<<" d= "<<d<<endl;
myswap(a,b);
myswap(c,d);
cout<<"a="<<a<<" b="<<b<<endl;
cout<<"c="<<c<<" d="<<d<<endl;
}函数模板写法
template<typename T>
void myswap(T &t1,T &t2)
{
T t=t1;
t1=t2;
t2=t;
}

函数模板有两种使用方式,自动类型推导和显示指示类型; 显示指示类型就是要在使用时候加上关键字 比如 myswap<int>(a,b);
函数模板的注意事项
使用自动类型推导时,一定要推导出一直的数据类型
下面这种使用方法报错

模板必须确定T的数据类型 模板不能独立使用,必须确定通用数据类型,并且能推导出一致的类型
这个可以理解为。 看下面这里的函数根本没有调用T,但是还是会报错。所以必须要指定一个T,他才会运行

下面这串代码成功运行 无报错
template<class T>
void func()
{
cout<<"func函数调用"<<endl;
}
void test()
{
func<int>();
}函数模板案例
使用选择排序,写出一个从小到大的选择排序模板
template<class T>
void SWAP(T &a,T &b)
{
T t=a;
a=b;
b=t;
}
template<class T>
void SORT(T a[],int len)
{
for(int i=0;i<len;i++)
{
int max=i;
for(int j=i+1;j<len;j++)
{
if(a[j]>a[max])
{
max=j;
}
}
if(max!=i)
{
SWAP(a[max],a[i]);
}
}
}
void test()
{
int a[]={1,2,3,4,5,6,7,8};
SORT(a,8);
for(int i=0;i<8;i++)
{
cout << a[i] << " ";
}
cout<<endl;
char b[]="abcdefghi";
SORT(b,9);
for(int i=0;i<9;i++)
{
cout << b[i] << " ";
}
cout<<endl;
}
普通函数与函数模板的区别
普通函数与函数模板区别:
普通函数调用时可以发生自动类型转换(隐式类型转换)
函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
如果利用显示指定类型的方式,可以发生隐式类型转换 直接转换为你指定的类型,如果不行就报错
可以看到普通函数不报错但是类模板报错

但是如果我们给他显示指示类型,
int add1(int a,int b)
{
return a+b;
}
template<class T>
T add2(T a,T b)
{
return a+b;
}
void test()
{
int a=1;
char c='a';
cout<<add1(a,c)<<endl;
cout<<add2<int>(a,c)<<endl;
}
普通函数与模板函数的调用规则
如果函数模板和普通函数都可以实现,优先调用普通函数
可以通过空模板参数列表来强制调用函数模板
函数模板也可以发生重载
如果函数模板可以产生更好的匹配,优先调用函数模板
可以看到第四个重载模板函数调用,调用普通函数要发生隐式类型转换,而模板不用
void Print(int a)
{
cout<<"普通函数调用"<<endl;
}
template<class T>
void Print(T a)
{
cout<<"模板函数调用"<<endl;
}
template<class T>
void Print(T a,T b,T c=10)
{
cout<<"重载模板函数调用"<<endl;
}
void test()
{
int a=10;
Print(a);
Print<>(a);
int b=10;
Print(a,b);
char c,d;
Print(c,d);
}
模板函数的局限性
提供一个专供person版本的mycompare函数
注意写法 template<> bool mycompare(person &a,person &b);括号里直接给他指明是传入person用这个函数
这是一种模板函数的重载,只不过是提供给特定类型,的具体化模板
class person
{
public:
person(string a,int b)
{
name=a;
age=b;
}
string name;
int age;
};
template<class T>
bool mycompare(T &a,T &b)
{
if(a==b) return 1;
else return 0;
}
template<>bool mycompare(person &a,person &b)
{
if(a.name==b.name&&a.age==b.age)
return 1;
else return 0;
}
void test()
{
person a("刘帅",10);
person b("张帅",20);
if(mycompare(a,b))
{
cout<<"相等"<<endl;
}
else cout<<"不相等"<<endl;
}
类模板
类模板语法
这个可以简单理解为 自定义类里面的参数类型,但是在传入的时候要指定
template<class NAME,class AGE>
class person
{
public:
person(NAME a,AGE b)
{
name=a;
age=b;
}
void show()
{
cout<<name<<" "<<age<<endl;
}
NAME name;
AGE age;
};
void test()
{
person<string,int>p1("刘帅",22);
person<char,float>p2('A',22.2);
p1.show();
p2.show();
}
类模板和函数模板的区别
类模板没有自动类型推导的使用方式
这个可以在使用的时候可以看出来,类模板使用必须使用 显示指示类型
类模板在模板参数列表中可以有默认参数
template<class NAME,class AGE=int>
class person
{
public:
person(NAME a,AGE b)
{
name=a;
age=b;
}
void show()
{
cout<<name<<" "<<age<<endl;
}
NAME name;
AGE age;
};
void test()
{
person<string,int>p1("刘帅",22);
person<char,float>p2('A',22.2);
person<string>p3("帅帅",22);
p1.show();
p2.show();
p3.show();
}
类模板成员函数创建时机
在这里我们创建2个类,然后再创建一个类模板分别调用这两个类中的成员函数
class person1
{
public:
void showperson1()
{
cout<<"person1 show"<<endl;
}
};
class person2
{
public:
void showperson2()
{
cout<<"person2 show"<<endl;
}
};
template<class T>
class Myclass
{
public:
T obj;
void fun1(){obj.showperson1();}
void fun2(){obj.showperson2();}
};可以看到出现报错,这里因为我们使用的类模板指定用person1的类,
类模板中的成员函数,并不是一开始就创建的,而是在模板调用时再生成 调用时候再去创建成员函数
类模板对象作函数参数
1,指定传入类型 直接在函数传参里面传入模板 告诉他模板的参数类型
template<class Nanmetype,class Agetype>
class person
{
public:
person(Nanmetype a,Agetype b)
{
name=a;
age=b;
}
void show()
{
cout<<"name:"<<name<<" age:"<<age<<endl;
}
Nanmetype name;
Agetype age;
};
//1 指定传入类型
void Print(person<string,int>&p)
{
p.show();
}
void test()
{
person<string,int>p("刘帅",22);
Print(p);
}
2,参数模板化
上面是将指定参数类型的类模板传进函数,这里是直接将参数也变成模板 但是要在上面加一个模板
typeid().name是将参数名输出
template <class T1,class T2>
void Print(person<T1,T2>&p)
{
p.show();
cout<<"T1的数据类型为:"<< typeid(T1).name()<<endl;
cout<<"T2的数据类型为:"<< typeid(T2).name()<<endl;
}
3,整个类模板化
template<class T>
void Print(T &p)
{
p.show();
cout<<"T的类型为:"<<typeid(T).name()<<endl;
}
后面2种相当于函数模板配合类模板 有点复杂搞不清 反正一般用第一种
类模板与继承
这里继承Base打完马上会自动弹出<>,要求你指定父类T中的数据类型
c++编译需要给子类分配内存,必须知道父类中T的类型才可以向下继承

父类是类模板,子类要继承,也可以把子类变成一个类模板,在这里继承的时候将继承过来的BASE中的数据类型也当作模板
template<class T>
class Base
{
public:
T m;
};
class Son:public Base<int>
{
};
template<class T1,class T2>
class Son2:public Base<T2>
{
public:
Son2()
{
cout<< typeid(T1).name()<<endl;
cout<< typeid(T2).name()<<endl;
}
};
void test()
{
Son2<int,string> s;
}
类模板成员函数类外实现
这里想到类里面的成员函数类外实现,掌握语法怎么写
其实这里和普通类实现差不多,类内都是只留下声明; 类外实现因为模板有T1,T2,所以要在上面加一行template<class T1,class T2>,告诉函数这是类模板里面的参数。 然后普通作用域直接person::完事,这里要写清楚person<T1,T2>::
template<class T1,class T2>
class person
{
public:
person(T1 n,T2 age);
void show();
T1 name;
T2 age;
};
template<class T1,class T2>
person<T1,T2>::person(T1 n,T2 a)
{
name=n;
age=a;
}
template<class T1,class T2>
void person<T1, T2>::show()
{
cout<<name<<" "<<age<<endl;
}
void test()
{
person<string,int>p("刘帅",22);
p.show();
}类模板分文件编写
如果我们和之前一样设置一个person.h和person.cpp,然后用h内只声明,.cpp写实现,在代码写好后编译器不会出现任何报错,但是点构建或者运行会报错。
解决方法1 这样正常输出
#include "person.cpp"解决方法2 将声明和实现写在一起 改后缀名hpp 在包含
为什么会这样? 因为类模板的函数是在编译的时候在去实现,正常分文件写,编译器读到头文件之后没读到cpp就出错了,不知道这些函数用什么,但是直接包含cpp,直接读类模板的函数。
类模板与友元
案例
记得深浅拷贝问题
template<class T>
class Myarr
{
public:
//有参构造
Myarr(int max)
{
this->max_num=max;
this->len=0;
this->p=new T[max];
cout<<"有参构造函数"<<endl;
}
//析构
~Myarr()
{
if(this->p!=NULL)
delete[] this->p;
this->p=NULL;
this->len=0;this->max_num=0;
cout<<"析构函数"<<endl;
}
//拷贝构造
Myarr(const Myarr &arr)
{
this->max_num=arr.max_num;
this->len=arr.len;
this->p=new T[arr.max_num];
for(int i=0;i<arr.len;i++)
{
this->p[i]=arr.p[i];
}
cout<<"拷贝函数"<<endl;
}
//重载=
Myarr& operator=(const Myarr &arr)
{
if(this->p!=NULL)
{
delete[] this->p;
this->p=NULL;
this->len=0;this->max_num=0;
}
this->max_num=arr.max_num;
this->len=arr.len;
this->p=new T[arr.max_num];
for(int i=0;i<arr.len;i++)
{
this->p[i]=arr.p[i];
}
cout<<"=重载函数"<<endl;
//!!!
return *this;
}
//尾插法
void push_back(const T &v)
{
if(this->max_num==this->len)
{
cout<<"容量已满"<<endl;
return;
}
this->p[this->len++]=v;
}
//尾删法
void pop_back()
{
if(this->len==0)
{
return;
}
this->len--;
}
//访问元素
T& operator[](int index)
{
return this->p[index];
}
//返回容量
int getmax_num()
{
return this->max_num;
}
//返回长度
int get_len()
{
return this->len;
}
private:
T *p;
int max_num;
int len;
};void test()
{
Myarr<int>arr(5);
Myarr<int>arr1(arr);
Myarr<int>arr3(100);
arr3=arr;
}
template<class T>
class Myarr
{
public:
//有参构造
Myarr(int max)
{
this->max_num=max;
this->len=0;
this->p=new T[max];
cout<<"有参构造函数"<<endl;
}
//析构
~Myarr()
{
if(this->p!=NULL)
delete[] this->p;
this->p=NULL;
this->len=0;this->max_num=0;
cout<<"析构函数"<<endl;
}
//拷贝构造
Myarr(const Myarr &arr)
{
this->max_num=arr.max_num;
this->len=arr.len;
this->p=new T[arr.max_num];
for(int i=0;i<arr.len;i++)
{
this->p[i]=arr.p[i];
}
cout<<"拷贝函数"<<endl;
}
//重载=
Myarr& operator=(const Myarr &arr)
{
if(this->p!=NULL)
{
delete[] this->p;
this->p=NULL;
this->len=0;this->max_num=0;
}
this->max_num=arr.max_num;
this->len=arr.len;
this->p=new T[arr.max_num];
for(int i=0;i<arr.len;i++)
{
this->p[i]=arr.p[i];
}
cout<<"=重载函数"<<endl;
//!!!
return *this;
}
//尾插法
void push_back(const T &v)
{
if(this->max_num==this->len)
{
cout<<"容量已满"<<endl;
return;
}
this->p[this->len++]=v;
}
//尾删法
void pop_back()
{
if(this->len==0)
{
return;
}
this->len--;
}
//访问元素
T& operator[](int index)
{
return this->p[index];
}
//返回容量
int getmax_num()
{
return this->max_num;
}
//返回长度
int get_len()
{
return this->len;
}
private:
T *p;
int max_num;
int len;
};void test()
{
Myarr<int>arr(5);
for(int i=0;i<5;i++)
{
arr.push_back(i);
}
for(int i=0;i<5;i++) cout<<arr[i]<<" ";
cout<<endl;
cout<<"arr的长度为"<<arr.get_len()<<endl;
cout<<"arr的大小为"<<arr.getmax_num()<<endl;
arr.pop_back();
for(int i=0;i<arr.get_len();i++) cout<<arr[i]<<" ";
cout<<endl;
cout<<"arr的长度为"<<arr.get_len()<<endl;
cout<<"arr的大小为"<<arr.getmax_num()<<endl;
}
测试自定义数据类型
class Person {
public:
Person() {}
Person(string name, int age) {
this->m_Name = name;
this->m_Age = age;
}
public:
string m_Name;
int m_Age;
};
void printPersonArray(Myarr<Person>& personArr)
{
for (int i = 0; i < personArr.get_len(); i++) {
cout << "姓名:" << personArr[i].m_Name << " 年龄: " << personArr[i].m_Age << endl;
}
}
void test()
{
Myarr<Person> pArray(10);
Person p1("孙悟空", 30);
Person p2("韩信", 20);
Person p3("妲己", 18);
Person p4("王昭君", 15);
Person p5("赵云", 24);
pArray.push_back(p1);
pArray.push_back(p2);
pArray.push_back(p3);
pArray.push_back(p4);
pArray.push_back(p5);
printPersonArray(pArray);
cout << "pArray的大小:" << pArray.get_len() << endl;
cout << "pArray的容量:" << pArray.getmax_num() << endl;
}