目录
实验内容
实验过程
一、模板函数
1.一般模板函数
2.特化模板函数(模板函数只有全特化,没有偏特化)
?二、模板类
三、模板类AutoPtr
1.构造函数
2.析构函数
3.拷贝构造函数
4.等号、->、*等运算符重载
5.主函数调用AutoPtr
总结?
一、函数模板
二、类模板
三、模板类AutoPtr
实验内容
一、模板函数 一般模板函数 特化模板函数
二、模板类 模板类 成员模板函数 模板特化:模板成员函数特化、模板类特化
三、模板类AutoPtr 构造函数 析构函数 拷贝构造函数 等号、->、*等运算符重载 主函数调用AutoPtr
实验过程
一、模板函数
1.一般模板函数
模板函数定义:所谓模板函数,实际上是建立一个通用函数,它所用到的数据的类型(包括返回值类型、形参类型、局部变量类型)可以不具体指定,而是用一个虚拟的类型来代替(实际上是用一个标识符来占位),等发生函数调用时再根据传入的实参来逆推出真正的类型。这个通用函数就称为函数模板(Function Template)。
模板函数的写法
template <class/typename 类型参数1, class/typename 类型参数2, ...>
返回值类型 模板函数名(形参表)
{
函数体
}
一个模板函数例子(交换参量的Swap模板函数)
#include <iostream>
using namespace std;
template<class T>
void Swap(T &x, T &y)
{
T tmp = x;
x = y;
y = tmp;
}
int main()
{
int n = 1, m = 2;
Swap(n, m); //编译器自动生成 void Swap (int &, int &)函数
cout<<n<<"\n"<<m<<endl;
double f = 1.1, g = 2.2;
Swap(f, g); //编译器自动生成 void Swap (double &, double &)函数
cout<<f<<"\n"<<g<<endl;
return 0;
}
T 是类型参数,代表类型。编译器由模板自动生成函数时,会用具体的类型名对模板中所有的类型参数进行替换,其他部分则原封不动地保留。同一个类型参数只能替换为同一种类型。编译器在编译到调用函数模板的语句时,会根据实参的类型判断该如何替换模板中的类型参数。
运行结果
函数模板是用于生成函数的,像上面的例子,可以只写一个 Swap 模板,编译器会根据 Swap 模板自动生成多个 Sawp 函数,用以交换不同类型变量的值。
2.特化模板函数(模板函数只有全特化,没有偏特化)
定义:当函数模板需要对某些类型进行特化处理,称为函数模板的特化。
我们先写一个模板函数compare()
template <class T>
int compare(const T &left, const T&right)
{
cout <<"in template<class T>..." <<endl;
return (left - right);
}
这个模板函数它支持常见int, double,float等类型的数据的比较,但是它不支持char*(string)类型的比较,因此我们必须对其进行特化,以让它支持两个字符串的比较,因此我们实现了如下的特化函数。
template < >
int compare(const char* left, const char* right)
{
cout <<"in special template< >..." <<endl;
return strcmp(left, right);
}
特化后的模版函数compare()现在就支持char*(string)类型的比较。
测试代码
#include <iostream>
#include <string.h>
using namespace std;
//函数模板
template <class T>
int compare(const T left, const T right)
{
cout <<"in template<class T>..." <<endl;
return (left - right);
}
// 这个是一个特化的函数模版
template < >
int compare(const char* left, const char* right)
{
cout <<"in special template< >..." <<endl;
return strcmp(left, right);
}
int main( )
{
cout<<compare(1, 4)<<endl; //int 类型的比较
cout<<compare(8.8,1.1)<<endl; //double 类型的比较
const char *left = "longer";
const char *right = "long";
cout<<compare(left, right)<<endl; //string 类型的比较
return 0;
}
?运行结果
函数模版的特化,当函数调用发现有特化后的匹配函数时,会优先调用特化的函数,而不再通过函数模版来进行实例化。
?二、模板类
模板类的定义:类模板,可以定义相同的操作,拥有不同数据类型的成员属性。 通常使用 template 来声明。 告诉编译器,碰到 T 不要报错,表示一种泛型. 普通模板函数和友元模板函数,声明和定义都写在类的内部,也不会有什么报错。
模板类的写法?
template <类型形参表>
class <类名>
{ //类说明体 };
template <类型形参表>
<返回类型> <类名> <类型名表>::<成员函数1>(形参表)
{ //成员函数定义体 }
template <类型形参表>
<返回类型> <类名> <类型名表>::<成员函数2>(形参表)
{ //成员函数定义体 }
…
template <类型形参表>
<返回类型> <类名> <类型名表>::<成员函数n>(形参表)
{ //成员函数定义体 }
?模板类的全特化与偏特化
#include <iostream>
using namespace std;
template<typename T1, typename T2>
class Test
{
public:
Test(T1 i,T2 j):a(i),b(j){cout<<"模板类"<<endl<<a<<" "<<b<<endl;}
private:
T1 a;
T2 b;
};
template<>
class Test<int , char>
{
public:
Test(int i, char j):a(i),b(j){cout<<"全特化"<<endl<<a<<" "<<b<<endl;}
private:
int a;
char b;
};
template <typename T2>
class Test<char, T2>
{
public:
Test(char i, T2 j):a(i),b(j){cout<<"偏特化"<<endl<<a<<" "<<b<<endl;}
private:
char a;
T2 b;
};
int main()
{
Test<double,int> t1(1.1,2); //调用一般模板类
Test<int , char> t2(1,'A'); //调用全特化模板类
Test<char, bool> t3('A',false); //调用偏特化模板类
cout<<a<<b<<endl;
return 0;
}
运行结果
成员模板函数
定义:成员函数模板是类或类模板的成员的模板函数。 成员函数可以是多个环境中的函数模板。 类模板的所有函数都是泛型的,但却不称为成员模板或成员函数模板。 如果这些成员函数采用其自己的模板自变量,则将它们视为成员函数模板。
#include <iostream>
using namespace std;
template <typename T>
class Test
{
public:
template <typename R>
void Swap(R &i, R &j){R tmp; tmp = i;i = j;j = tmp;} //模板成员函数
};
int main()
{
Test <int> t;
double m=1.1,n=2.2;
t.Swap(m,n);
cout<<m<<" "<<n<<endl;
return 0;
}
模板成员函数特化
#include <iostream>
using namespace std;
template <typename T>
class Test
{
public:
template <typename R>
void Swap(R &i, R &j){R tmp; tmp = i;i = j;j = tmp;} //模板成员函数
void Swap(int &i, double &j){int tmp;tmp=i;i=j;j=tmp;cout<<"特化的Swap"<<endl;} //模板成员函数特化
};
int main()
{
Test <int> t;
double m=1.1,n=2.2;
t.Swap(m,n); //调用一般的模板成员函数
cout<<m<<" "<<n<<endl; //调用特化的模板成员函数
int a=10;
double b=1.33;
t.Swap(a,b);
cout<<a<<" "<<b<<endl;
}
运行结果
三、模板类AutoPtr
1.构造函数
template<class T>
AutoPtr<T>::AutoPtr(T* pData)
{
m_pData = pData;
m_nUser = new int(1);
}
2.析构函数
~AutoPtr()
{
decrUser();
}
void decrUser();
template<class T>
void AutoPtr<T>::decrUser()
{
--(*m_nUser);
if ((*m_nUser) == 0)
{
delete m_pData;
m_pData = 0;
delete m_nUser;
m_nUser = 0;
}
}
3.拷贝构造函数
template<class T>
AutoPtr<T>::AutoPtr(const AutoPtr<T>& h)
{
m_pData = h.m_pData;
m_nUser = h.m_nUser;
(*m_nUser)++;
}
4.等号、->、*等运算符重载
AutoPtr<T>& operator=(const AutoPtr<T>& h);
T* operator->()
{
return m_pData;
}
T& operator*()
{
return *m_pData;
}
const T& operator *()const
{
return *m_pData;
}
const T* operator ->()const
{
return m_pData;
}
template<class T>
AutoPtr<T>& AutoPtr<T>::operator=(const AutoPtr<T>& h)
{
decrUser();
m_pData = h.m_pData;
m_nUser = h.m_nUser;
(*m_nUser)++;
}
5.主函数调用AutoPtr
#include<iostream>
#include <vector>
#include "autoptr.h"
#include "CMatrix.h"
using namespace std;
int main()
{
AutoPtr<CMatrix> h1;
double data[6] = {1,2,3,4,5,6};
h1->Create(2,3,data);
cout << *h1 << endl;
AutoPtr<CMatrix> h2(h1);
(*h2).Set(0,1,10);
cout << *h1 << endl << *h2;
}
运行结果
总结?
一、函数模板
1、函数模板利用关键字template 2、使用函数模板有两种方式:自动类型推到、显示指定类型 3、模板的目的是为了提高复用性,将类型参数化
注意事项: 1、自动类型推导,必须推导出一致的数据类型T,才可以使用 2、模板必须要确定出T的数据类型,才可以使用
普通函数和函数模板的调用规则: 1、如果函数模板和普通函数都可以实现,优先调用普通函数 2、可以通过空模板参数列表来强制调用函数模板 。 如:myPrint<>(a, b); //<>里不用写东西 3、函数模板也可以发生重载。 重载后不加<>直接调用 如:myPrint(a, b, c); 4、如果函数模板可以产生更好的匹配,优先调用函数模板
一般来说,既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性。
二、类模板
类模板和函数模板区别主要有两点: 1、类模板没有自动类型推导的使用方式 2、类模板在模板参数列表中可以有默认参数 如:template<class NameType,class AgeType = int> 那么在创建对象的时候就不用指定int类型,如:Person< string > p2(“张三”, 99);
类模板中成员函数和普通类中成员函数创建时机的区别: 1、普通类中的成员函数一开始就创建 2、类模板中的成员函数在调用时才创建
类模板对象做函数参数,即类模板实例化出对象,向函数传参的方式,一共有三种传入方式: 1、指定传入的类型:直接显示对象的数据类型 ( 最常用第一种) 2、参数模板化:将对象中的参数变为模板进行传递 3、整个类模板化:将这个对象类型 模板化进行传递
类模板与继承 1、当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型 2、如果不指定,编译器无法给子类分配内存 3、如果想灵活指定出父类中T的类型,子类也需要变为类模板
类模板分文件编写 问题:类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到 解决: 1、直接包含.cpp文件 2、将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制 ?
三、模板类AutoPtr
new和delete的作用:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????new:在动态内存中为对象分配一块空间并返回一个指向该对象的指针,delete:指向一个动态独享的指针,销毁对象,并释放与之关联的内存。
动态内存管理经常会出现两种问题:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????一种是忘记释放内存,会造成内存泄漏;一种是尚有指针引用内存的情况下就释放了它,就会产生引用非法内存的指针。? ? ? ? ? ? ? ? ? ? ??
智能指针的概念:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????为了更加容易(更加安全)的使用动态内存,引入了智能指针的概念。智能指针的行为类似常规指针,重要的区别是它负责自动释放所指向的对象。?
|