为什么要有函数模板
项目需求: 实现多个函数用来返回两个数的最大值,要求能支持char类型、int类型、double类型变量
#include <iostream>
using namespace std;
int Max(int a, int b)
{
return a>b ? a:b;
}
char Max(char a, char b)
{
return a>b ? a:b;
}
float Max(float a, float b)
{
return a>b ? a:b;
}
void main()
{
int x = 1;
int y = 2;
cout<<"max(1, 2) = "<<Max(x, y)<<endl;
float a = 2.0;
float b = 3.0;
cout<<"max(2.0, 3.0) = "<<Max(a, b)<<endl;
system("pause");
return ;
}
实际上,以上程序,只需要一个“函数”就可以搞定!
#include <iostream>
using namespace std;
template <typename T>
T Max(T a, T b){
return a>b ? a:b;
}
void main()
{
int x = 1;
int y = 2;
cout<<"max(1, 2) = "<<Max(x, y)<<endl;
cout<<"max(1, 2) = "<<Max<int>(x,y)<<endl;
float a = 2.0;
float b = 3.0;
cout<<"max(2.0, 3.0) = "<<Max(a, b)<<endl;
system("pause");
return ;
}
函数模板语法
所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。凡是函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只需在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现了不同函数的功能。
函数模板定义形式
由以下三部分组成: 模板说明 + 函数定义 + 函数模板调用
template < 类型形式参数表 > 类型 函数名 (形式参数表) { //语句序列 }
-
模板说明 template < 类型形式参数表 > 类型形式参数的形式: typename T1 , typename T2 , …… , typename Tn 或 class T1 , class T2 , …… , class Tn (注:typename 和 class 的效果完全等同) -
函数定义 类型 函数名 (形式参数表) { } 注意:模板说明的类属参数必须在函数定义中出现一次 函数参数表中可以使用类属类型参数,也可以使用一般类型参数 -
函数模板调用 max(a, b); //显式类型调用 max(a, b); //自动数据类型推导
4.模板函数
函数模板和函数重载
5.函数模板和函数重载
#include <iostream>
using namespace std;
template <typename T>
void Swap(T &a, T &b){
T t;
t = a;
a = b;
b = t;
cout<<"Swap 模板函数被调用了"<<endl;
}
void main(void){
char cNum = 'c';
int iNum = 65;
system("pause");
return ;
}
函数模板和普通函数区别结论: 两者允许并存 函数模板不允许自动类型转化 普通函数能够进行自动类型转换
#include <iostream>
using namespace std;
int Max(int a, int b)
{
cout<<"调用 int Max(int a, int b)"<<endl;
return a>b ? a:b;
}
template<typename T>
T Max(T a, T b)
{
cout<<"调用 T Max(T a, T b)"<<endl;
return a>b ? a:b;
}
template <typename T>
T Max(T a, T b, T c){
cout<<"调用 T Max(T a, T b, T c)"<<endl;
return Max(Max(a, b), c);
}
int Max1(int a, int b)
{
cout<<"调用 int Max(int a, int b)"<<endl;
return a>b ? a:b;
}
template<typename T1, typename T2>
T1 Max1(T1 a, T2 b)
{
cout<<"调用 T Max1(T1 a, T2 b)"<<endl;
return a>b ? a:b;
}
void main(void){
int a = 1;
int b = 2;
char c = 'a';
Max(3.0, 4.0, 5.0);
system("pause");
return ;
}
函数模板和普通函数在一起,调用规则: 1 函数模板可以像普通函数一样被重载 2 C++编译器优先考虑普通函数 3 如果函数模板可以产生一个更好的匹配,那么选择模板 4 可以通过空模板实参列表的语法限定编译器只通过模板匹配 5. 编译器并不是把函数模板处理成能够处理任意类型的函数 6. 编译器从函数模板通过具体类型产生不同的函数
类模板的使用
1.为什么需要类模板 类模板与函数模板的定义和使用类似,有时,有两个或多个类,其功能是相同的,仅仅是数据类型不同,我们可以通过如下面语句声明了一个类模板: 2.类模板用于实现类所需数据的类型参数化 3.类模板在表示支持多种数据结构显得特别重要,这些数据结构的表示和算法不受所包含的元素类型的影响
类模板定义
类模板由模板说明和类说明构成 模板说明同函数模板,如下: template <类型形式参数表> 类声明
例如: template class ClassName { //ClassName 的成员函数 private : Type DataMember; }
单个类模板使用
#include <iostream>
using namespace std;
template <typename T>
class A
{
public:
A(T t=0)
{
this->t = t;
}
T &getT()
{
return t;
}
private:
T t;
};
void printA(A<int> &a){
cout<<a.getT()<<endl;
}
int main(void){
A<int> a(666);
cout<<a.getT()<<endl;
printA(a);
system("pause");
return 0;
}
继承类模板使用
#include <iostream>
using namespace std;
template <typename T>
class A
{
public:
A(T t)
{
this->t = t;
}
T &getT()
{
return t;
}
private:
T t;
};
template <typename Tb>
class B: public A<int>
{
public:
B(Tb b):A<Tb>(b)
{
this->b = b;
}
private:
Tb b;
};
void printA(A<int> &a){
cout<<a.getT()<<endl;
}
int main(void){
A<int> a(666);
cout<<a.getT()<<endl;
B<int> b(888);
cout<<"b(888): "<<b.getT()<<endl;
printA(a);
system("pause");
return 0;
}
结论: 子类从模板类继承的时候,需要让编译器知道 父类的数据类型具体是什么
1.父类一般类,子类是模板类, 和普通继承的玩法类似 2.子类是一般类,父类是模板类,继承时必须在子类里实例化父类的类型参数 3.父类和子类都时模板类时,子类的虚拟的类型可以传递到父类中 4.所有的类模板函数写在类的外部,在一个cpp中
#include <iostream>
using namespace std;
template <typename T>
class A
{
public:
A(T t=0);
T &getT();
A operator +(const A &other);
void print();
private:
T t;
};
template <typename T>
A<T>::A(T t)
{
this->t = t;
}
template <typename T>
T &A<T>::getT()
{
return t;
}
template <typename T>
A<T> A<T>::operator+(const A<T> &other){
A<T> tmp;
tmp.t =this->t + other.t;
return tmp;
}
template <typename T>
void A<T>::print(){
cout<<this->t<<endl;
}
int main(void){
A<int> a(666), b(888);
A<int> tmp = a + b;
tmp.print();
system("pause");
return 0;
}
总结: 在同一个cpp 文件中把模板类的成员函数放到类的外部,需要注意以下几点
1.函数前声明 template <类型形式参数表> 2.类的成员函数前的类限定域说明必须要带上虚拟参数列表 3.返回的变量是模板类的对象时必须带上虚拟参数列表 4.成员函数参数中出现模板类的对象时必须带上虚拟参数列表 5.成员函数内部没有限定
#pragma once
template <typename T>
class A
{
public:
A(T t=0);
T &getT();
A operator +(const A &other);
void print();
private:
T t;
};
#include "demo.h"
#include <iostream>
using namespace std;
template <typename T>
A<T>::A(T t)
{
this->t = t;
}
template <typename T>
T &A<T>::getT()
{
return t;
}
template <typename T>
A<T> A<T>::operator+(const A<T> &other){
A<T> tmp;
tmp.t =this->t + other.t;
return tmp;
}
template <typename T>
void A<T>::print(){
cout<<this->t<<endl;
}
int main(void){
A<int> a(666), b(888);
A<int> tmp = a + b;
tmp.print();
system("pause");
return 0;
}
注意:当类模板的声明(.h文件)和实现(.cpp 或.hpp文件)完全分离,因为类模板的特殊实现,我们应在使用类模板时使用#include 包含 实现部分的.cpp 或.hpp文件。
特殊情况 友元函数
#include <iostream>
using namespace std;
template <typename T>
class A
{
public:
A(T t=0);
template <typename T>
friend A<T> addA(const A<T> &a, const A<T> &b);
T &getT();
A operator +(const A &other);
void print();
private:
T t;
};
template <typename T>
A<T>::A(T t)
{
this->t = t;
}
template <typename T>
T &A<T>::getT()
{
return t;
}
template <typename T>
A<T> A<T>::operator+(const A<T> &other){
A tmp;
tmp.t =this->t + other.t;
return tmp;
}
template <typename T>
void A<T>::print(){
cout<<this->t<<endl;
}
template <typename T>
A<T> addA(const A<T> &a, const A<T> &b){
A<T> tmp;
cout<<"call addA()..."<<endl;
tmp.t = a.t + b.t;
return tmp;
}
int main(void){
A<int> a(666), b(888);
A<int> tmp = a + b;
A<int> tmp1 = addA<int>(a, b);
tmp.print();
tmp1.print();
system("pause");
return 0;
}
结论: (1)类内部声明友元函数,必须写成一下形式 template friend A addA (A &a, A &b);
(2)友元函数实现 必须写成 template A add(A &a, A &b) { //… } (3)友元函数调用 必须写成 A c4 = addA(c1, c2);
模板类和静态成员
#include <iostream>
using namespace std;
template <typename T>
class A
{
public:
A(T t=0);
T &getT();
A operator +(const A &other);
void print();
public:
static int count;
private:
T t;
};
template <typename T> int A<T>::count = 666;
template <typename T>
A<T>::A(T t)
{
this->t = t;
}
template <typename T>
T &A<T>::getT()
{
return t;
}
template <typename T>
A<T> A<T>::operator+(const A<T> &other){
A tmp;
tmp.t =this->t + other.t;
return tmp;
}
template <typename T>
void A<T>::print(){
cout<<this->t<<endl;
}
int main(void){
A<int> a(666), b(888);
A<int> tmp = a + b;
A<float> c(777), d(999);
a.count = 888;
cout<<"b.count:"<<b.count<<endl;
cout<<"c.count:"<<c.count<<endl;
cout<<"d.count:"<<d.count<<endl;
c.count = 1000;
cout<<"修改后, d.count:"<<d.count<<endl;
system("pause");
return 0;
}
总结:
1.从类模板实例化的每个模板类有自己的类模板数据成员,该模板类的所有对象共享一个static数据成员 2. 和非模板类的static数据成员一样,模板类的static数据成员也应该在文件范围定义和初始化 3.static 数据成员也可以使用虚拟类型参数T
类模板使用总结
归纳以上的介绍,可以这样声明和使用类模板:
- 先写出一个实际的类。
- 将此类中准备改变的类型名(如int要改变为float或char)改用一个自己指定的虚拟类型名(如上例中的T)。
- 在类声明前面加入一行,格式为:
template <typename 虚拟类型参数> 如: template class A {…}; //类体 - 用类模板定义对象时用以下形式:
类模板名<实际类型名> 对象名; 或 类模板名<实际类型名> 对象名(实参表列); 如: A cmp; A cmp(3,7); - 如果在类模板外定义成员函数,应写成类模板形式:
template <typename 虚拟类型参数> 函数类型 类模板名<虚拟类型参数>::成员函数名(函数形参表列) {…} 关于类模板的几点补充: - 类模板的类型参数可以有一个或多个,每个类型前面都必须加typename 或class,如:
template <typename T1,typename T2> class someclass {…}; 在定义对象时分别代入实际的类型名,如: someclass<int, char> object; - 和使用类一样,使用类模板时要注意其作用域,只有在它的有效作用域内用使用它定义对象。
- 模板类也可以有支持继承,有层次关系,一个类模板可以作为基类,派生出派生模板类。
类模板实战
1)请设计一个数组模板类( Vector ),完成对int、char、float、double 以及任意的自定义类等类型元素进行管理。 需求 a.实现构造函数 b.实现拷贝构造函数 c.实现cout << 操作 d.实现下标访问符[] 的重载操作 e.实现 = 号操作符重载
#include <iostream>
using namespace std;
template <typename T>
class Vector
{
friend ostream &operator<< <T> (ostream &out, const Vector &object);
public:
Vector(int size = 128);
Vector(const Vector &object);
int getLength();
T& operator[](int index);
Vector &operator=(const Vector &object);
~Vector();
private:
T *m_base;
int m_len;
};
#include <iostream>
using namespace std;
#include "Vector.h"
template<typename T>
ostream &operator<<(ostream &out, const Vector<T> &object){
for(int i=0; i<object.m_len; i++){
out << object.m_base[i] << " ";
}
out<<endl;
return out;
}
template <typename T>
Vector<T>::Vector(int size){
if(size > 0){
m_len = size;
m_base = new T[m_len];
}
}
template <typename T>
Vector<T>::Vector(const Vector<T> &object){
m_len = object.m_len;
m_base = new T[m_len];
for(int i=0; i<m_len; i++){
m_base[i] = object.m_base[i];
}
}
template <typename T>
int Vector<T>::getLength(){
return m_len;
}
template <typename T>
T& Vector<T>::operator[](int index){
return m_base[index];
}
template <typename T>
Vector<T> &Vector<T>::operator=(const Vector<T> &object){
if(m_base != NULL){
delete[] m_base;
m_base = NULL;
m_len = 0;
}
m_len = object.m_len;
m_base = new T[m_len];
for(int i=0; i<m_len; i++){
m_base[i] = object.m_base[i];
}
return *this;
}
template <typename T>
Vector<T>::~Vector(){
if(m_base != NULL){
delete[] m_base;
m_base = NULL;
m_len = 0;
}
}
#include <iostream>
using namespace std;
#include "Vector.cpp"
class Student{
friend ostream &operator<<(ostream &out, const Student &object);
public:
Student(){
age = 0;
name[0] = '\0';
}
Student(int _age, char *_name){
age = _age;
strcpy_s(name, 64, _name);
}
void print(){
cout<<name<<", "<<age<<endl;
}
~Student(){
}
private:
int age;
char name[64];
};
ostream &operator<<(ostream &out, const Student &object){
out<<"("<<object.name<<" , "<<object.age<<")";
return out;
}
int main(){
Student s1(18, "李小花");
Student s2(19, "王大炮");
Vector<Student *> studentVector(2);
studentVector[0] = &s1;
studentVector[1] = &s2;
cout<<studentVector<<endl;
system("pause");
Vector<int> myVector(10);
for(int i=0; i<myVector.getLength(); i++){
myVector[i] = i;
}
cout<<myVector<<endl;
system("pause");
for(int i=0; i<myVector.getLength(); i++){
cout<<myVector[i]<<endl;
}
Vector<int> myIntVector1(myVector);
cout<<"myIntVector1 中的元素如下:"<<endl;
for(int i=0; i<myIntVector1.getLength(); i++){
cout<<myIntVector1[i]<<endl;
}
cout<<"---end---"<<endl;
Vector<int> myIntVector2(1);
myIntVector2 = myIntVector1;
cout<<"myIntVector2 中的元素如下:"<<endl;
for(int i=0; i<myIntVector1.getLength(); i++){
cout<<myIntVector1[i]<<endl;
}
cout<<"---end---"<<endl;
Vector<float> myVector1(10);
for(int i=0; i<myVector1.getLength(); i++){
myVector1[i] = i*0.1f;
}
for(int i=0; i<myVector1.getLength(); i++){
cout<<myVector1[i]<<endl;
}
system("pause");
return 0;
}
|