C++模板

一,函数模板

1,函数模板语法规则

template<typename T>或template<class T>

template:告诉编译器开始泛型编程

typename:告诉编译器T是一个泛指类型

2,函数调用

自动类型推导调用 :根据参数的类型推导

具体类型显示调用:函数名<T的具体类型>(实参1,实参2)

3,函数模板深入理解

  • 编译器并不是把函数模板处理成能够处理任意类型的函数
  • 编译器从函数模板通过具体类型产生不同的函数
  • 编译器会对函数模板进行两次编译
    1. 在声明的地方对模板代码本身进行编译
    2. 在调用的地方对参数替换后的代码进行编译

4,函数模板与重载

函数模板可以像普通函数一样可以重载

函数模板不允许自动类型转换,普通函数可以

三条匹配规则:

  1. C++编译器优先考虑普通函数
  2. 如果函数模板可以产生一个更好的匹配,那么选择模板
    1. 如果普通函数和模板都可以调用,但是普通函数参数类型要自动转换
  3. 可以通过空模板实参列表的语法限定编译器只通过模板匹配
    1. 函数名<>(参数1,参数2)

5,多参数函数模板

当声明的类型参数为返回值类型时无法进行自动类型推导。

不完美的解决办法:返回参数的类型申明到第一个位置,调用时只需显示声明返回参数类型即可

例子:

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);
}
cout<<Max(a, b)<<endl;//优先考虑普通函数 
cout<<Max<>(a, b)<<endl;//空模板实参列表,所以调用函数模板    
cout<<Max(3.0, 4.0)<<endl;//因为是float型,不可能调用那个int的函数,而是函数模板。可以产生更好的匹配    
cout<<Max(5.0, 6.0, 7.0)<<endl;//函数模板 
cout<<Max('a', 100)<<endl;//那个普通函数,因为函数模板不能自动类型转换 

二,类模板

1,template<typename T>也可以用类上,称为类模板,和函数模板用法类似

2,声明的泛指类型 T 可用于声明成员变量和成员函数

3,类模板只能利用具体类型定义对象,不能自动类型推导。

4,类模板在工程上的使用

由于类模板的编译机制不同 所以不能像普通类样分开实现后在使用时只包含头文件 。一般是在一个叫.hpp的头文件实现。

格式:

#ifndef _ARRAY_DEF_H_

#define _ARRAY_DEF_H_

#endif

在模板类外实现成员函数时,需要加上template<typename T>

5,类模板可以被特化

使用template<>申明一个类时,表示这是一个特化类。

template<>

class test<int>//test类的int特化

特化类模板意义:

当类模板在处理某种特定类型有缺陷时 ,可以通过类模板的特化来克服处理这种特定类型带来的不足 。优先匹配特化

6,类模板可以定义多个类型参数

类模板可以被局部特化

可以指定类模板的特定实现,并要求某些类型参数仍然必须得模板的用户指定

 

 

7,为什么需要特化 而不重新定义新类?

  • 特化和重新定义新类看上去本质没什么区别,如果定义了一个新类,就有一个模板类和新类,在使用时就会考虑是使用新类还是模板类。
  • 如果使用特化类,编译器优先使用特化类。

8,函数模板和类模板的模板参数可以是普通数值

template<typename T,int N>

非类型模板参数的限制:

  • 变量不能作为模板参数
  • 浮点数和类对象不能作为模板参数
  • 全局指针不能作为模板参数

三,工程问题

对于堆内存来说

1,内存越界常发生在数组

解决方法:数组类

2,内存泄漏和内存多次释放常发生于指针的使用过程中

解决方法:智能指针

工程中的智能指针是一个类模板

  • 通过构造函数接管申请的堆内存
  • 通过析构函数确保堆内存被及时释放
  • 通过重载指针运算符 * --> 模拟指针的行为
  • 通过重载比较运算符 == 和!= 模拟指针的比较

 

数组类:

 

array.h
#ifndef _ARRAY_H_
#define  _ARRAY_H_
template<typename T> 
class Array
{
private:
	int mLength;
	T* mSpace;
public:
	Array(int length);
	Array(const Array& obj);
	int length();
	~Array();
	T& operator [](int i);
	Array& operator =(const Array& obj);
	bool operator ==(const Array& obj);
	bool operator !=(const Array& obj); 
};
#endif

array.hpp
#ifndef _ARRAY_DEF_H_
#define _ARRAY_DEF_H_
#include <iostream>
#include "array.h"
using namespace std;
template<typename T>
Array<T>::Array(int length)
{
	if(length<0){
		mLength=0;
	}
	mLength=length;
	mSpace=new T[mLength];
}
template<typename T>
Array<T>::Array(const Array& obj)
{
	mLength=obj.mLength;
	mSpace=new T [mLength];
	for(int i=0;i<mLength;i++){
		mSpace[i]=obj.mSpace[i];
	}
}

template<typename T>
int Array<T>::length()
{
	return mLength;
}
template<typename T>
Array<T>::~Array()
{
	mLength=-1;
	delete[] mSpace;
}
template<typename T>
T& Array<T>::operator [](int i)//引用可以作为左值  mSpace[i]=i,这样的形式允许 
{
	if(i>=mLength) {
		cout<<"数组越界"<<endl;
	}
	return mSpace[i];
}
template<typename T>
Array<T>& Array<T>::operator =(const Array& obj)//引用的目的是,为了连续赋值 a=b=c 
{
	delete[] mSpace;
	mLength=obj.mLength;
	mSpace=new T[mLength];
	for(int i=0;i<mLength;i++){
		mSpace[i]=obj.mSpace[i];
	}
	return *this;
}
template<typename T>
bool Array<T>::operator ==(const Array& obj)
{
	bool ret=true;
	if(mLength==obj.mLength){
		for(int i=0;i<mLength;i++){
			if(mSpace[i]!=obj.mSpace[i]){
				ret=false;
				break;
			}
		}
	}
	else{
		ret=false;
	}
	return ret;
}
template<typename T>
bool Array<T>::operator !=(const Array& obj)
{
	bool ret=true;
	return !(*this==obj);
}
#endif

//测试
int main(int argc, char *argv[])
{
    Array<int> ai(5);
    
    for(int i=0; i<ai.length(); i++)
    {
        ai[i] = i + 1;
    }
    
    for(int i=0; i<ai.length(); i++)
    {
        cout<<ai[i]<<endl;
    }
    
    Array<double> ad(10);
    
    for(int i=0; i<ad.length(); i++)
    {
        ad[i] = (i + 1) / 100.0;
    }
    
    for(int i=0; i<ad.length(); i++)
    {
        cout<<ad[i]<<endl;
    }
    
    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

 智能指针

SmartPointer.h
#ifndef _SMARTPOINTER_H_
#define _SMARTPOINTER_H_
template<typename T>
class SmartPointer
{
protected:
	T* m_pointer;
public:
	SmartPointer();
	SmartPointer(const T* pointer);
	~SmartPointer();
	T* operator ->();
	T& operator *();
};

#endif

SmartPointer.hpp
#ifndef _SMARTPOINTER_DEF_H_
#define _SMARTPOINTER_DEF_H_
#include "smartpointer.h"
template<typename T>
SmartPointer<T>::SmartPointer()
{
	m_pointer=NULL;
}
template<typename T>
SmartPointer<T>::SmartPointer(const T* pointer)
{
	m_pointer=const_cast<T*>(pointer);
}
template<typename T>
SmartPointer<T>::~SmartPointer()
{
	delete m_pointer;
}
template<typename T>
T* SmartPointer<T>::operator ->()
{
	return m_pointer;
}
template<typename T>
T& SmartPointer<T>::operator *()
{
	return *m_pointer;
}
#endif

//测试
class Test
{
public:
    int i;
    void print()
    {
        cout<<i<<endl;
    }
};

int main(int argc, char *argv[])
{
    SmartPointer<int> pi = new int(5);
    SmartPointer<Test> pt = new Test();
    
    cout<<*pi<<endl;
    
    *pi = 10;
    
    cout<<*pi<<endl;
    
    pt->i = 20;
    pt->print();
    
    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值