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



