目录
一、模板函数(compare)
1.1 一般模板函数
下面定义了一个名叫compare的函数模板,支持多种类型的通用比较逻辑:
template<typename T>
int compare(const T& left, const T& right) {
if (left < right) {
return -1;
}
if (right < left) {
return 1;
}
return 0;
}
compare<int>(1, 2); //使用模板函数
1.2 特化模板函数
有时通用的函数模板不能解决个别类型的问题,我们必须对此进行定制,这就是函数模板的特化。函数模板的特化必须把所有的模版参数全部指定。
template<>
void func(int i) {
cout << "In special version for int "<< i << endl;
}
int i = 10;
func(i); //调用特化版本
二、模板类(Stack)
2.1 模板类
所谓模板类,实际上是建立一个通用的类模板后,实例化出的类。其数据成员、成员函数的返回值类型和形参类型不具体指定,用一个虚拟的类型来代表。使用类模板定义对象时,系统会实参的类型来取代类模板中虚拟类型从而实现了不同类的功能。
定义一个类模板与定义函数模板的格式类似,必须以关键字template开始,后面是尖括号括起来的模板参数,然后是类名,其格式如下:
template <typename 类型参数>
class 类名{
类成员声明
};
或者
template <class 类型参数>
class 类名{
类成员声明
};
类模板Stack的使用举例
#include<iostream.h>
const int size=10;
template<class T> //模板声明,其中T为类型参数
class Stack{ //类模板为Stack
public:
void init()
{
tos=0;
}
void push(T ob); //声明成员函数push的原型,函数参数类型为T类型
T pop(); //声明成员函数pop的原型,其返回值类型为T类型
private:
T stack[size]; //数组类型为T,即是自可取任意类型
int tos;
};
template<class T> //模板声明
void Stack<T>::push(T ob) //在类模板体外定义成员函数push
{
if(tos==size)
{
cout<<"Stack is full"<<endl;
return;
}
stack[tos]=ob;
tos++;
}
template<typename T> //模板声明
T Stack<T>::pop() //在类模板体外定义成员函数push
{
if(tos==0)
{
cout<<"Stack is empty"<<endl;
return 0;
}
tos--;
return stack[tos];
}
int main()
{
//定义字符堆栈
Stack<char> s1; //用类模板定义对象s,此时T被char取代
s1.init();
s1.push('a');
s1.push('b');
s1.push('c');
for(int i=0;i<3;i++){cout<<"pop s1:"<<s1.pop()<<endl;}
//定义整型堆栈
Stack<int> s2; //用类模板定义对象s,此时T被int取代
s2.init();
s2.push(1);
s2.push(3);
s2.push(5);
for(int i=0;i<3;i++){cout<<"pop s2:"<<s2.pop()<<endl;}
return 0;
}
说明:
在每一个类模板定义之前,都需要在前面加上模板声明,并且,类模板在使用时,必须在模板类名字后面缀上<类型参数> ;如同模板函数一样,模板类也可以有多个类型参数。
2.2 成员模板函数
不仅普通函数可以定义为模板,类的成员函数也可以定义为模板
class Printer {
public:
template<typename T>
void print(const T& t) {
cout << t <<endl;
}
};
Printer p;
p.print<const char*>("abc"); //打印abc
2.3 模板特化
2.3.1 模板函数特化
有时通用的函数模板不能解决个别类型的问题,我们必须对此进行定制,这就是函数模板的特化。函数模板的特化必须把所有的模版参数全部指定。
template<>
void func(int i) {
cout << "In special version for int "<< i << endl;
}
int i = 10;
func(i); //调用特化版本
2.3.2 模板成员函数特化
#include <stdio.h>
class A{
public:
template <class T>
void Print(){
printf("A template\n");
}
};
template<>
void A::Print<int>(){
printf("int\n");
}
int main(){
A a;
a.Print<double>();
a.Print<int>();
return 0;
}
2.3.3 模板类特化
进行类模板的特化时,每个成员函数都必须重新定义为普通函数,原来模板函数中的每个T也相应地被进行特化的类型取代:
类模板特化的使用示例:
//stack2.h
#ifndef STACK2_H
#define STACK2_H
#include <deque>
#include <string>
#include <stdexcept>
#include "stack1.h"
template<>
class Stack<std::string>{
private:
std::deque<std::string> elems; //元素
public:
void push(std::string const &elem); //在尾部添加元素
void pop(); //在尾部删除一个元素
std::string top() const; //栈顶元素
bool empty() const {
return elems.empty();
}
};
void Stack<std::string>::push(std::string const &elem)
{
elems.push_back(elem); //添加一个元素
}
void Stack<std::string>::pop()
{
if(elems.empty()) {
throw std::out_of_range("Stack<std::string>::pop(): empty stack");
}
elems.pop_back(); //删除一个元素
}
std::string Stack<std::string>::top() const
{
if(elems.empty()) {
throw std::out_of_range("Stack<std::string>::top():empty stack");
}
return elems.back();
}
#endif // STACK2_H
三、模板类(AutoPtr)
3.1 构造函数
//构造函数,创建一个T类型的指针并赋值n
autoPtr(T n)
{
ptr = new T;//去掉T*,直接用Private里T*好的ptr
*ptr = n;
cout << "new pointer created." << endl;
cout << *ptr << endl; //这个地方可以输出ptr指向的值
}
3.2 析构函数
~autoPtr()
3.3 拷贝构造函数
SmartPointer(const SmartPointer<T>& obj) //重新实现拷贝构造函数
{
m_pointer = obj.m_pointer;
const_cast<SmartPointer<T>&>(obj).m_pointer = NULL;
}
3.4 等号、->、*等运算符重载
T* operator ->() //重载->操作符
{
return m_pointer;
}
T& operator *() //重载*操作符
{
return *(m_pointer);
}
3.5 主函数调用AutoPtr
int main()
{
{
SmartPointer<int> p1 = new int(12); //指向一个int堆变量
cout << "(*p1) = " << (*p1) << endl;
SmartPointer<int> p2 = p1;
cout << "p1.isNull() = " << p1.isNull() << endl;
cout << "*p2.get() = " << *p2.get() << endl;
SmartPointer<Test> pt = new Test; //指向一个Test堆对象
}
return 0;
}
main函数中定义的三个智能指针 p1、p2、pt,在它们的生命周期结束后,会自动执行智能指针类中的析构函数,释放其指向的堆空间。
智能指针的使用规则:只能用来指向堆空间的单个对象或变量。
四、实验总结
4.1 泛型编程
为什么要有泛型编程?C++是一门强类型语言,所以无法做到像动态语言(python javascript)那样,编写一段通用的逻辑,可以把任意类型的变量传进去处理。泛型编程弥补了这个缺点,通过把通用逻辑设计为模板,摆脱了类型的限制,提供了继承机制以外的另一种抽象机制,极大地提升了代码的可重用性。注意:模板定义本身不参与编译,而是编译器根据模板的用户使用模板时提供的类型参数生成代码,再进行编译,这一过程被称为模板实例化。用户提供不同的类型参数,就会实例化出不同的代码。
函数模板的定义:把处理不同类型的公共逻辑抽象成函数,就得到了函数模板。函数模板可以声明为inline或者constexpr的,将它们放在template之后,返回值之前即可。
4.2 模板函数和模板类
所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表。这个通用函数就称为函数模板。凡是函数体相同的函数都可以用这个模板来代替,不必定义多个函数,只需在模板中定义一次即可。在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现了不同函数的功能。
1)C++提供两种模板机制:函数模板、类模板
2)类属—— 类型参数化,又称参数模板
使得程序(算法)可以从逻辑功能上抽象,把被处理的对象(数据)类型作为参数传递。
模板把函数或类要处理的数据类型参数化,表现为参数的多态性,称为类属; 模板用于表达逻辑结构相同,但具体数据元素类型不同的数据对象的通用行为。
4.3 智能指针
智能指针的实现原理就是资源分配,它定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放。智能指针的实现机制就是利用类的构造和析构函数(释放资源)是由编译器自动调用的。
智能指针:
1.管理指针执行对象的释放问题。
2.可以像指针一样使用。
如何选择智能指针?
1、如果要使用多个指向同一个对象的指针,应选择shared_ptr。如果编译器没有shared_ptr,可使用Boost库提供的shared_ptr。
2、如果不需要多个指向同一个对象的指针,则使用unique_ptr。如果编译器没有unique_ptr,可使用Boost库提供的scoped_ptr。
3、如果需要使用new[ ],则使用unique_ptr。
4、尽量避免使用auto_ptr。