欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++模板

程序员文章站 2022-06-01 14:23:50
...

一,函数模板

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,类模板可以定义多个类型参数

类模板可以被局部特化

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

 C++模板

 

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;
}