1.模板
1.1 模板概论
c++提供了函数模板,即是建立一个通用函数,其函数类型和形参类型不具体制定,用
一个虚拟的类型来代表。这个通用函数就成为函数模板。凡是函数体相同的函数都可以
用这个模板代替,不必定义多个函数,只需在模板中定义一次即可。在调用函数时系统
会根据实参的类型来取代模板中的虚拟类型,从而实现不同函数的功能。
c++提供两种模板机制:函数模板和类模板
总结:
1.模板把函数或类要处理的数据类型参数化,表现为参数的多态性,成为类属。
2.模板用于表达逻辑结构相同,但具体数据元素类型不同的数据对象的通用行为。
1.2 函数模板
函数模板的形式如下所示:
template<class T>
void swapT(T& a, T& b);
其中template<class T>,也可写为template<typename T>
注:1.编译器会根据实参自动推导数据类型
2.显示的知道数据类型(指定了类型不能传入非指定类型的数据,必须要让编译器
知道你要传入的数据类型)
3.隐式转换:如果参数列表指定数据类型,那么实参中可以隐式转换
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
template < class T >
void swapT ( T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
template < class T >
void swapT2 ( )
{
}
void test01 ( )
{
int a = 10 ;
int b = 20 ;
swapT ( a, b) ;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
swapT< int > ( a, b) ;
}
template < class T >
int func ( T a, T b)
{
return a + b;
}
template < typename T>
int func1 ( T a, T b)
{
return a + b;
}
void test02 ( )
{
int a = 10 ;
double b = 20.2 ;
cout << func< int > ( a, b) << endl;
}
int main ( )
{
test02 ( ) ;
system ( "pause" ) ;
return 0 ;
}
1.3 案例:函数模板实现数组排序
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
template < class T >
void swapArr ( T& a, T& b)
{
T temp = a;
a = b;
b = temp;
}
template < class T >
void sortArr ( T arr[ ] , int len)
{
for ( int i = 0 ; i < len- 1 ; i++ )
{
for ( int j = 0 ; j < len - 1 - i; j++ )
{
if ( arr[ j] > arr[ j + 1 ] )
{
swapArr< T> ( arr[ j] , arr[ j + 1 ] ) ;
}
}
}
}
template < class T >
void printArr ( T arr[ ] , int len)
{
for ( int i = 0 ; i < len; i++ )
{
cout << arr[ i] << endl;
}
}
void test01 ( )
{
int arr[ 10 ] = { 8 , 6 , 9 , 2 , 1 , 4 , 5 , 3 , 7 , 0 } ;
int len = sizeof ( arr) / sizeof ( arr[ 0 ] ) ;
sortArr< int > ( arr, len) ;
printArr< int > ( arr, len) ;
}
int main ( )
{
test01 ( ) ;
system ( "pause" ) ;
return 0 ;
}
1.4 函数模板和普通函数的区别
1.函数模板不允许自动类型转化
2.普通函数能够自动进行类型转化
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
int myAdd ( int a, int b)
{
return a + b;
}
template < class T >
int myAdd1 ( T a, T b)
{
return a + b;
}
void test ( )
{
int a = 10 ;
int b = 20 ;
char c = 'c' ;
myAdd ( a, c) ;
myAdd1< int > ( a, c) ;
}
int main ( )
{
test ( ) ;
system ( "pause" ) ;
return 0 ;
}
1.5 函数模板和普通函数调用规则
1.c++编译器优先考虑普通函数
2.可以通过空模板实参列表的语法限定编译器只能通过模板匹配
3.函数模板可以像普通函数那样可以被重载
4.如果函数模板可以产生一个更好的匹配,那么选择模板
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
void myAdd ( int a, int b)
{
cout << "普通函数的调用!" << endl;
}
template < class T >
void myAdd ( T a, T b)
{
cout << "函数模板1的调用!" << endl;
}
template < class T >
void myAdd ( T a, T b, T c)
{
cout << "函数模板2的调用!" << endl;
}
void test ( )
{
int a = 10 ;
int b = 20 ;
myAdd ( 10 , 20 ) ;
myAdd< > ( 10 , 20 ) ;
myAdd ( 10 , 20 , 3 ) ;
char a1 = 'a' ;
char b1 = 'b' ;
myAdd ( a1, b1) ;
}
int main ( )
{
test ( ) ;
system ( "pause" ) ;
return 0 ;
}
1.6 模板实现机制
函数模板机制总结:
1.编译器并不是把函数模板处理成能够处理任何类型的函数
2.函数模板通过具体类型产生不同的函数
3.编译器会对函数模板进行两次编译,在声明的地方对模板代码本身进行编译,在调用的地方对参数替换后的代码进行编译。
4.函数模板具有局限性,例如对于一个自定义数据类型进行排序时,模板就不能起
到预期的作用。(解决办法:模板具体化,不建议使用)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
class A
{
public :
A ( )
{
this - > name = "张三" ;
this - > age = 0 ;
}
public :
string name;
int age;
} ;
template < class T >
void comPare ( T& a, T& b)
{
if ( a > b)
{
cout << "a>b" << endl;
}
else
{
cout << "a<=b" << endl;
}
}
template < > void comPare< A> ( A& a, A& b)
{
cout << "函数模板的具体化" << endl;
if ( a. age > b. age)
{
cout << "a>b" << endl;
}
else
{
cout << "a<=b" << endl;
}
}
void test ( )
{
A a1;
A a2;
comPare ( a1, a2) ;
}
int main ( )
{
test ( ) ;
system ( "pause" ) ;
return 0 ;
}
1.7 类模板基本概念
类模板和函数模板的定义和使用类似,我们已经进行了介绍。有时,有两个或多个
类,其功能是相同的,仅仅是数据类型不同。
类模板用于实现类所需数据的类型参数化
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
template < class Nametype , class Agetype >
class A
{
public :
A ( Nametype name, Agetype age)
{
this - > name = name;
this - > age = age;
}
void myPrint ( )
{
cout << this - > name << " " << this - > age << endl;
}
public :
Nametype name;
Agetype age;
} ;
void test ( )
{
A< string, int > a ( "张三" , 18 ) ;
a. myPrint ( ) ;
A< int , int > b ( 20 , 18 ) ;
b. myPrint ( ) ;
}
template < class Nametype , class Agetype = int >
class B
{
public :
B ( Nametype name, Agetype age)
{
this - > name = name;
this - > age = age;
}
void myPrint ( )
{
cout << this - > name << " " << this - > age << endl;
}
public :
Nametype name;
Agetype age;
} ;
void test2 ( )
{
B< string> b ( "张三" , 18 ) ;
b. myPrint ( ) ;
B< string, double > b1 ( "张三" , 18.12 ) ;
b1. myPrint ( ) ;
}
int main ( )
{
test2 ( ) ;
system ( "pause" ) ;
return 0 ;
}
1.8 案例:复数的模板类及重载
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
template < class Idtype , class Agetype >
class A
{
public :
A ( Idtype id, Agetype age)
{
this - > id = id;
this - > age = age;
}
A& operator + ( A& a)
{
this - > id = this - > id + a. id;
this - > age = this - > age + a. age;
return * this ;
}
void myPrint ( )
{
cout << this - > id << " " << this - > age << endl;
}
public :
Idtype id;
Agetype age;
} ;
void test ( )
{
A< int , int > a ( 3 , 4 ) ;
A< int , int > b ( 5 , - 10 ) ;
A< int , int > c = a + b;
c. myPrint ( ) ;
}
int main ( )
{
test ( ) ;
system ( "pause" ) ;
return 0 ;
}
1.9 类模板做函数参数
有以下三种类型:
1.指定传入的数据类型
2.把参数模板化
3.把整个类模板化
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
template < class Nametype , class Agetype >
class A
{
public :
A ( Nametype name, Agetype age)
{
this - > name = name;
this - > age = age;
}
void myPrint ( )
{
cout << this - > name << " " << this - > age << endl;
}
public :
Nametype name;
Agetype age;
} ;
void func1 ( A< string, int > & a)
{
a. myPrint ( ) ;
}
template < class T1 , class T2 >
void func2 ( A< T1, T2> & a)
{
a. myPrint ( ) ;
}
template < class T1 >
void func3 ( T1& a)
{
a. myPrint ( ) ;
}
void test ( )
{
A< string, int > a ( "张三" , 4 ) ;
func1 ( a) ;
func2 ( a) ;
func3 ( a) ;
}
int main ( )
{
test ( ) ;
system ( "pause" ) ;
return 0 ;
}
1.10 类模板的继承
1.普通类继承类模板(注:不能使用普通的继承写法,继承类模板的时候要告诉编译
器父类的泛型数据是什么类型)
2.类模板继承类模板(注:必须在实例化的时候告诉父类是什么数据类型)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
template < class T >
class Father
{
public :
Father ( )
{
m = 20 ;
}
public :
T m;
} ;
class Son : public Father< int >
{
public :
} ;
template < class T1 , class T2 >
class Son2 : public Father< T2>
{
} ;
void test01 ( )
{
Son2< int , int > a;
cout << a. m << endl;
}
int main ( )
{
test01 ( ) ;
system ( "pause" ) ;
return 0 ;
}
1.11 类模板成员类外实现
类模板的成员函数在类外实现时,要写出函数模板
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
template < class Nametype , class Agetype >
class A
{
public :
A ( Nametype name, Agetype age) ;
void myPrint ( ) ;
public :
Nametype name;
Agetype age;
} ;
template < class Nametype , class Agetype >
A< Nametype, Agetype> :: A ( Nametype name, Agetype age)
{
cout << "构造函数" << endl;
this - > age = age;
this - > name = name;
}
template < class Nametype , class Agetype >
void A< Nametype, Agetype> :: myPrint ( )
{
cout << this - > name << " " << this - > age << endl;
}
void test01 ( )
{
A< string, int > a ( "张三" , 11 ) ;
a. myPrint ( ) ;
}
int main ( )
{
test01 ( ) ;
system ( "pause" ) ;
return 0 ;
}
1.12 类模板的分文件编写
1.调用类模板的时候,要进行二次编译,要把泛型的数据类型转化为具体的类型,这
时需要知道函数体,但是函数的实现在.cpp中,那么调用类模板的.CPP没有引入实现
的.cpp,只引入了.h文件,因此会报错
2.解决方法,将类模板的声明于实现放在一个.hpp文件中,这样函数在二次编译的时
候,仅引入一个文件就可以知道函数的实现了
3.为什么.hpp有类成员的实现,在调用类模板的地方引入.hpp,不会报重定义?类的成
员函数默认申请为内联函数,在链接的时候,链接器会对重名的成员函数进行处理,只
保留一份成员函数,所以不会报错
1.13 类模板和友元
在类模板中,友元的实现有两种方式:
1.友元在类模板类内实现
2.友元在类模板类外实现
2.1.在函数名和括号中间加<>表示空参数列表,以此来调用函数模板
2.2.编译器不知道下面有没有这个友元函数模板的实现,需要知道函数的结构
2.3 告诉编译器下面有类外友元函数的实现
2.4 类外友元函数声明需要声明友元所在的类
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
template < class Nametype , class Agetype >
class A ;
template < class Nametype , class Agetype >
void myprintA ( A< Nametype, Agetype> & a) ;
template < class Nametype , class Agetype >
class A
{
friend void printA ( A< Nametype, Agetype> & a)
{
cout << "类内实现" << a. name << " " << a. age << endl;
}
friend void myprintA< > ( A< Nametype, Agetype> & a) ;
public :
A ( Nametype name, Agetype age)
{
this - > name = name;
this - > age = age;
}
void myPrint ( )
{
cout << "打印成员函数" << endl;
}
private :
Nametype name;
Agetype age;
} ;
void test01 ( )
{
A< string, int > a ( "张三" , 2 ) ;
printA ( a) ;
}
template < class Nametype , class Agetype >
void myprintA ( A< Nametype, Agetype> & a)
{
cout << "类外实现" << a. name << " " << a. age << endl;
}
void test02 ( )
{
A< string, int > a ( "张三" , 2 ) ;
myprintA ( a) ;
}
int main ( )
{
test01 ( ) ;
test02 ( ) ;
system ( "pause" ) ;
return 0 ;
}
1.14 案例:类模板实现数组
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
template < class T >
class MyArr
{
public :
MyArr ( )
{
}
MyArr ( int capacity)
{
this - > capacity = capacity;
this - > size = 0 ;
this - > p = new T[ this - > capacity] ;
memset ( this - > p, 0 , this - > capacity) ;
}
MyArr ( const MyArr& a)
{
this - > capacity = a. capacity;
this - > size = a. size;
this - > p = new T[ this - > capacity] ;
memset ( this - > p, 0 , this - > capacity) ;
for ( int i = 0 ; i < this - > size; i++ )
{
this - > p[ i] = a. p[ i] ;
}
}
MyArr& operator = ( const MyArr& a)
{
if ( this - > p != NULL )
{
delete [ ] p;
p = NULL ;
}
this - > capacity = a. capacity;
this - > size = a. size;
this - > p = new T[ this - > capacity] ;
memset ( this - > p, 0 , this - > capacity) ;
for ( int i = 0 ; i < this - > size; i++ )
{
this - > p[ i] = a. p[ i] ;
}
return * this ;
}
T& operator [ ] ( int index)
{
return this - > p[ index] ;
}
void pushmyBack ( const T& val)
{
if ( this - > capacity == this - > size)
return ;
p[ this - > size] = val;
this - > size++ ;
}
void popmyBack ( )
{
if ( this - > size == 0 )
return ;
this - > size-- ;
}
int getSize ( )
{
return this - > size;
}
~ MyArr ( )
{
if ( p != NULL )
{
delete [ ] p;
p = NULL ;
}
}
private :
T* p;
int capacity;
int size;
} ;
template < class Nametype , class Agetype >
class Student
{
public :
Student ( )
{
}
Student ( Nametype name, Agetype age)
{
this - > name = name;
this - > age = age;
}
public :
Nametype name;
Agetype age;
} ;
void printMyarr ( MyArr< Student< string, int >> arr, int len)
{
for ( int i = 0 ; i < len; i++ )
{
cout << "姓名:" << arr[ i] . name << " 年龄:" << arr[ i] . age << endl;
}
}
void test01 ( )
{
MyArr< Student< string, int >> arr ( 4 ) ;
Student< string, int > s1 ( "aaa" , 11 ) ;
Student< string, int > s2 ( "bbb" , 22 ) ;
Student< string, int > s3 ( "ccc" , 33 ) ;
Student< string, int > s4 ( "ddd" , 44 ) ;
arr. pushmyBack ( s1) ;
arr. pushmyBack ( s2) ;
arr. pushmyBack ( s3) ;
arr. pushmyBack ( s4) ;
int len = arr. getSize ( ) ;
printMyarr ( arr, len) ;
cout << "----------------------" << endl;
arr. popmyBack ( ) ;
len = arr. getSize ( ) ;
printMyarr ( arr, len) ;
}
int main ( )
{
test01 ( ) ;
system ( "pause" ) ;
return 0 ;
}