C++_模板类与类型萃取技术

    在声明变量,函数,和大多数其余类型实体的时候,C++要求咱们使用指定的类型。然而,有许多代码,除了类型不一样以外,其他部分看起来都是相同的,好比,下面这个例子:
ios

bool IsEqual (int left, int right)
{
    return left == right;
}

bool IsEqual (const string& left , const string& right)
{
    return left == right;
}

void test()
{
    string s1 ("s1"), s2("s2");
    cout<<IsEqual (s1, s2)<<endl;
    cout<<IsEqual (1,1)<<endl;
}

    上面这个例子,是为了比较两个变量是否相等的重载函数。这两个函数功能相同,只是处理的参数类型不一样,那若是你须要处理float,double,等一系列类型时,你就要一一写出这些类型的重载函数,这样代码会显得十分繁琐,这时,就须要使用模板函数来处理了,模板函数只须要写一个就能够处理上面这种问题。
c++

template<typename T>
bool IsEqual (const T& left , const T& right )
{
    return left == right;
}

void test1 ()
{
    string s1 ("s1"), s2("s2" );
    cout<<IsEqual (s1, s2)<<endl ;
    cout<<IsEqual (1,1)<<endl;
}

    在编译模板函数时,编译器会根据传入的参数,自动推演出模板形参类型,并自动生成相应的代码,这样就相对于上面使用函数重载方式,代码量就大大减小,由于编译器会帮助你推演出相应代码。编程


当上面处理left,right类型不一样时,使用模板函数时就须要做以下处理:数组

template <typename T>
bool IsEqual (const T& left , const T& right )
{
    return left == right;
}

void test2()
{
    cout<<IsEqual (1,1)<<endl;
    //cout<<IsEqual(1,1.2)<<endl; // 模板参数不匹配
    cout<<IsEqual<int>(1,1.2)<< endl; // 显示实例化
    cout<<IsEqual<double>(1,1.2)<< endl; // 显示实例化
}

    那么咱们就知道使用模板函数就要注意模板参数的匹配问题,你也可使用显示实例化方式,强制处理这种状况的发生。要是你就是想要比较两种不一样类型,那就须要重载函数模板,使它能够接受两种类型,下面就是重载以后的:ide

bool IsEqual (const int& left , const int& right)
{
    return left == right;
}

template <typename T>
bool IsEqual (const T& left , const T& right )
{
    return left == right;
}

template <typename T1, typename T2>
bool IsEqual (const T1& left , const T2& right)
{
    return left == right;
}
void test3()
{
    cout<<IsEqual(1,1)<<endl;
    cout<<IsEqual<int>(1,1)<< endl;
    cout<<IsEqual(1,1.2)<<endl;
}

模板类函数

/*模板类的格式*/

template<class name1, class name2, ...class namen>
class name
{ ... };

    之前在处理顺序表时,要更改其中的data类型,每每是经过修改typedef int DataType ,来修改其存放的数据类型,那么如今就可使用模板类来不须要手动去修改其类型,下面是使用模板类实现顺序表:
测试

#include <string>
#include <cassert>
using namespace std;

template <class T>

class SeqList
{
public:
	SeqList()
		:_data(NULL)
		,_size(0)
		,_capacity(0)
	{
		CheakCapacity();
	}
	~SeqList()
	{
		if(_data != NULL)
		{
			delete[] _data;
		}
	}
public:
	void PushBack(const T& d)
	{
		CheakCapacity();
		_data[_size] = d;
		_size++;
	}
	void PushFront(const T& d)
	{
		CheakCapacity();
		int i = _size;
		for(i; i>0; i--)
		{
			_data[i] = _data[i-1];
		}
		_data[i] = d;
		_size++;
	}
	void PopBack()
	{
		if(_size == 0)
		{
			cout<<"List is empty!!"<<endl;
			return;
		}
		_size--;
	}
	void PopFront()
	{
		int i = 0;
		if(_size == 0)
		{
			cout<<"List is empty!!"<<endl;
			return;
		}
		for(i; i<_size; i++)
		{
			_data[i] = _data[i+1];
		}
		_size--;
	}
public:
	void CheakCapacity()
	{
		if(_size == _capacity)
		{
			T* tmp = new T[_capacity+3];
			memcpy(tmp, _data, (_capacity)*sizeof(T));
			delete[] _data;
			_data = tmp;
			_capacity = _capacity+3;
		}
	}
	void Display()
	{
		int i = 0;
		for(i; i<_size; i++)
		{
			cout<<_data[i]<<" ";
		}
		cout<<"over"<<endl;
	}
private:
	T* _data;
	int _size;
	int _capacity;
};


void test4()
{
    SeqList<int> L;
    L.PushBack(1);
    L.PushBack(2);
    L.PushBack(3);
    L.PushBack(4);
    L.PushBack(5);
    L.Display();
}

int main()
{
    test4();
    system("pause");
    return 0;
}

结果:spa

wKioL1cDX-6j46f0AAAK6LcRYRY943.png

当测试为下面test5()时:指针

void test5()
{
    SeqList<string> L;
    L.PushBack("11111111111");
    L.PushBack("21111111111");
    L.PushBack("31111111111");
    L.PushBack("41111111111");
    L.PushBack("51111111111");
    L.PushBack("61111111111");
    L.Display();
}

结果:调试

wKioL1cDYlKS2rMtAABXoC3TiEU193.png

这为何会崩溃呢

        由于使用memcpy()时:当咱们拷贝的是基本类型时,只用拷贝所传递指针上的数据,若是是string类型呢,咱们则须要在堆上开辟空间,所传递的指针如 果被直接复制,则有可能(vs下的string类型的实现原理是若字符串不长则以数组保存,若字符串过长,则经过指针在堆上开辟空间进行保存)出现同一地 址,析构两次这样的常见错误。

那么要解决上面的问题,就要使用c++中的类型萃取技术。

    类型萃取是一种经常使用的编程技巧,其目的是实现不一样类型数据面对同一函数实现不一样的操做,它与类封装的区别是,咱们并不用知道我 们所调用的对象是什么类型,类型萃取是编译后知道类型,先实现,而类的封装则是先定义类型,后实现方法。在这里咱们能够用模板的特化实现其编程思想。

再来实现上面的顺序表:

#include <iostream>
#include <string>
#include <cassert>
using namespace std;

template <class T>

class SeqList
{
public:
	SeqList()
		:_data(NULL)
		,_size(0)
		,_capacity(0)
	{
		CheakCapacity();
	}
	~SeqList()
	{
		if(_data != NULL)
		{
			delete[] _data;
		}
	}
public:
	void PushBack(const T& d)
	{
		CheakCapacity();
		_data[_size] = d;
		_size++;
	}
	void PushFront(const T& d)
	{
		CheakCapacity();
		int i = _size;
		for(i; i>0; i--)
		{
			_data[i] = _data[i-1];
		}
		_data[i] = d;
		_size++;
	}
	void PopBack()
	{
		if(_size == 0)
		{
			cout<<"List is empty!!"<<endl;
			return;
		}
		_size--;
	}
	void PopFront()
	{
		int i = 0;
		if(_size == 0)
		{
			cout<<"List is empty!!"<<endl;
			return;
		}
		for(i; i<_size; i++)
		{
			_data[i] = _data[i+1];
		}
		_size--;
	}
public:
	int Find(const T& d)
	{
		int i = 0;
		for(i; i<_size; i++)
		{
			if(_data[i] == d)
			{
				return i;
			}
		}
		return -1;
	}
	void Insert(int pos, const T& d)
	{
		CheakCapacity();
		int i = 0;
		for(i=_size; i>pos; i--)
		{
			_data[i] = _data[i-1];
		}
		_data[pos] = d;
		_size++;
	}
	void Erase(int pos)
	{
		assert(pos>0);
		assert(pos<_size);
		int i = pos;
		for(i; i<_size; i++)
		{
			_data[i] = _data[i+1];
		}
		_size--;
	}
	void Sort()
	{
		int i,j;
		for(i=0; i<_size; i++)
		{
			for(j=0; j<_size-1-i; j++)
			{
				if(_data[j]>_data[j+1])
				{
					T tmp = _data[j];
					_data[j] = _data[j+1];
					_data[j+1] = tmp;
				}
			}
		}
	}
public:
	void CheakCapacity()
	{
		if(_size == _capacity)
		{
			T* tmp = new T[_capacity+3];
			if(TypeTraits<T>::isPODType().Get())
			{
				memcpy(tmp, _data, (_capacity)*sizeof(T));
			}
			else
			{
				for(int i=0; i<_size; i++)
				{
					tmp[i] = _data[i];
				}
			}
			delete[] _data;
			_data = tmp;
			_capacity = _capacity+3;
		}
	}
	void Display()
	{
		int i = 0;
		for(i; i<_size; i++)
		{
			cout<<_data[i]<<" ";
		}
		cout<<"over"<<endl;
	}
private:
	T* _data;
	int _size;
	int _capacity;
};

struct FalseType
{
	bool Get()
	{
		return false;
	}
};
struct TrueType
{
	bool Get()
	{
		return true;
	}
};
template <class T>
struct TypeTraits
{
	typedef FalseType isPODType;//内嵌型别
};
struct TypeTraits<char>
{
	typedef TrueType isPODType;//内嵌型别
};
template<>
struct TypeTraits<int>
{
	typedef TrueType isPODType;//内嵌型别
};/*还有许多基本类型没有显示写出来,bool,float,double...*/

void test6()
{
    SeqList<string> L;
    L.PushBack("11111111111");
    L.PushBack("21111111111");
    L.PushBack("31111111111");
    L.PushBack("41111111111");
    L.PushBack("51111111111");
    L.PushBack("61111111111");
    L.Display();
}

int main()
{
    test6();
    system("pause");
    return 0;
}

结果:

wKiom1cDcPSyEpOeAAAMW88CtqQ835.png


模板总结

    优势:

        1. 模板复用了代码,节省资源,更快的迭×××发,C++的标准模板库(STL)所以而产生。

        2. 加强了代码的灵活性。

    缺点:

        1. 模板让代码变得凌乱复杂,不易维护,编译代码时间变长。

        2. 出现模板编译错误时,错误信息很是凌乱,不易定位错误。

类型萃取总结:

        类型萃取技术能够大大加快代码的效率,也可让思路变得更清晰。

        要是上面在拷贝时,其实不用memcpy()也能够,只要将对象一个一个的拷贝,也是可行的,但程序的效率就会大大下降。

        在调试的时候会让本人思路更加清晰。

相关文章
相关标签/搜索