C++学习-----类型萃取

类型萃取



本篇博客主要讲述内容:


 1. 什么是类型萃取
 2. 模板的特化
 3. 类型萃取的代码实现(模板特化的应用)


C++学习-------模板
-----------------------------------------------------


 在C++中我们通常通过typeid (是一个函数)来提取; 但是只能获得类型,并不能用来声明变量;
 于是就产生了类型萃取来完成这一功能;

POD类型萃取: plain old data 平凡类型(无关痛痒的类型)--基本类型指在C++ 中与 C兼容的类型,可以按照 C 的方式处理。


通俗的理解就是方便我们区别开我们想要分开的类型,比如 内置类型和自定义类型;
比如,在写顺序表进行扩容时遇到的拷贝问题,如果是内置类型的话,我们就可以用memcpy,但是如果是自定义类型时就会有问题;

比如说下面这段代码:

template<typename T>
void Seqlist<T>:: CheckCapacity()//顺序表中的扩容函数;
{
	if(_size == capacity)
	{
		int NewCapacity = capacity*2 + 3;
		T* tmp  = new T[Newcapacity];
		memcpy(tmp,_pdata,capacity*sizeof(T));// 假设原来顺序表的空间是 _pdata表示;
		delete _pdata;
		_pdata = tmp;
		capacity = NewCapacity;// 切记更新容量;
	}
}


------------------------------------------------------------------------------------------------------------------------------------------------

 上面这段代码,如果模板参数传的是内置类型是没有问题的应用到顺序表中, 但是如果模板参数是string类型呢?  试 试还可不可以?(本篇博客最后贴出类型萃取后的全部代码
 

那么这里就需要插入一个知识点了;   string类型的字符串在内存中的存储问题;

----------------------------------------------------
我们知道其实在库里的string 对象中有两个成员_buf和_ptr; 那么这两个成员有什么用呢?_buf我们默认的有16个字节的空间,挡string的字符串小于16个的时候一般存储在_buf中,当字符串长度比较长的时候,我们一般就用到了_ptr, 这个_ptr是一个指针,它可以指向这个字符串的空间,这样,我们string 类就可以 用这四个字节的空间去存储字符串的地址;

 但是,我们这里用了memcpy函数来进行拷贝,那么,问题来了, memcpy是内存拷贝,直接拷贝内存,于是乎,就将这个_ptr 进行了一次拷贝,然后释放掉原来的_ptr; 是不是有问题了,  memcpy进行拷贝之后,我的两个_ptr都指向同一块空间;没有引用计数器的情况下,你释放掉一个_ptr,那就有问题了吧!   这就相当于浅拷贝的问题!

-----------------------------------------------------

 现在我们就是要解决这个问题,于是乎有了下面的写法:

template<typename T>
void Seqlist<T>:: CheckCapacity()//顺序表中的扩容函数;
{
	if(_size == capacity)
	{
		int NewCapacity = capacity*2 + 3;
		T* tmp  = new T[Newcapacity];
		//memcpy(tmp,_pdata,capacity*sizeof(T));// 假设原来顺序表的空间是 _pdata表示;
		for(int i = 0; i<_size; i++)
		{
			tmp[i] = _pdata[i];// 注意,我们这里是赋值操作符;
		}
		delete _pdata;
		_pdata = tmp;
		capacity = NewCapacity;// 切记更新容量;
	}
}


---------------------------------------------------------------------


  看到上面的代码,同学们不禁要问, 这有什么区别?


 仔细看,我们在用循环解决这个问题的时候循环内部是 _tmp[i] = _pdata[i];这里调用的是成员赋值操作符,如果是string类的成员,那么我们就调用的是string类的赋值操作符;而我们库里的string类已经解决这个问题,如果你写过string类的话一定会记得,这里我就不把string类模拟实现了;

-------------------------------------


 写到这里,是不是觉得有些偏离主题了, 不, 还没有,现在你看到我们用for循环解决了自定义类型的扩容拷贝问题,但是,如果是内置类型的模板参数,是不是memcpy的效率更高一点;

那么接下来,我们要解决的问题就是:
如何使得传进来的模板参数是内置类型就调用memcpy,如何使得传进来的参数是自定义类型就调用循环?

这里又要引入我们模板中的内容-------类模板的特化


 什么是类模板的特化:基于模板的对模板参数的特殊化处理;
 分为全特化和局部特化(偏特化)


例如:

// 全特化
template<typename T>
class Seqlist<int>
{
public:
	void PopBack();
	void PopFront();
};

// 特化之后的成员函数不需要模板形参
void Seqlist<int>::PopBack()
{}

// 偏特化

template<typename T,typename T>
class Date<T,int> // 类似全特化的使用方式
{};

偏特化也不一定是只特化一个参数,
偏特化并不仅仅是指特殊部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本

比如:

template<typename T,typename T>
class Date<T*, T*>
{};

template<typename  T, typename T>
class Date<T&,T&>
{};


--------------------------------------------------


大概了解什么是模板的特化后,估计不少同学很迷惑了,这特化有什么用呢? 

 这就回到了我们最开始的问题,怎样区分内置类型和自定义类型,同时也解决中间提到的 顺序表扩容的一个问题,下面我们用类型萃取来实现顺序表的扩容:

//模板实现顺序表;

#include<iostream>
#include<string>
#include<cassert>

using namespace std;


struct __TrueType
{
    bool Get ()
	{
		return true ;
	}
};

struct __FalseType
{
	bool Get ()
	{
		return false ;
	}
};

// 自定义类型一般不特化
template <class _T>
struct TypeTraits
{
	typedef __FalseType __IsPODType;
};

// 下面是对常见的几种内置类型的特化,当然内置类型还有很多,我只是举几个常见的;

template <>
struct TypeTraits< bool>
{
	typedef __TrueType __IsPODType;
};

template <>
struct TypeTraits< char>
{
	typedef __TrueType __IsPODType;
};


template <>
struct TypeTraits< short>
{
	typedef __TrueType __IsPODType;
};


template <>
struct TypeTraits< int>
{
	typedef __TrueType __IsPODType;
};


template <>
struct TypeTraits< long>
{
	typedef __TrueType __IsPODType;
};

template <>
struct TypeTraits< unsigned long long>
{
	typedef __TrueType __IsPODType;
};

template <>
struct TypeTraits< float>
{
	typedef __TrueType __IsPODType;
};

template <>
struct TypeTraits< double>
{
	typedef __TrueType __IsPODType;
};

// 接下来就是怎么使用的问题

template<typename T>
void Seqlist<T>:: CheckCapacity()//顺序表中的扩容函数;
{
	if(_size == capacity)
	{
		int NewCapacity = capacity*2 + 3;
		T* tmp  = new T[Newcapacity];
		if(TypeTraits<T>::__IsPODType.get())// 判断返回值
		{
			memcpy(tmp,_pdata,capacity*sizeof(T));
		}// 假设原来顺序表的空间是 _pdata表示;
		else
		{
			for(int i = 0; i<_size; i++)
			{
				tmp[i] = _pdata[i];// 注意,我们这里是赋值操作符;
			}
		}
		delete _pdata;
		_pdata = tmp;
		capacity = NewCapacity;// 切记更新容量;
	}
}

这样,就解决了我们前面的问题,最后贴出完整的代码!


---------------------------------------------------------------


#include<iostream>
#include<string>
#include<cassert>

using namespace std;


struct __TrueType
{
    bool Get ()
	{
		return true ;
	}
};

struct __FalseType
{
	bool Get ()
	{
		return false ;
	}
};

// 自定义类型一般不特化
template <class _T>
struct TypeTraits
{
	typedef __FalseType __IsPODType;
};

// 下面是对常见的几种内置类型的特化,当然内置类型还有很多,我只是举几个常见的;

template <>
struct TypeTraits< bool>
{
	typedef __TrueType __IsPODType;
};

template <>
struct TypeTraits< char>
{
	typedef __TrueType __IsPODType;
};


template <>
struct TypeTraits< short>
{
	typedef __TrueType __IsPODType;
};


template <>
struct TypeTraits< int>
{
	typedef __TrueType __IsPODType;
};


template <>
struct TypeTraits< long>
{
	typedef __TrueType __IsPODType;
};

template <>
struct TypeTraits< unsigned long long>
{
	typedef __TrueType __IsPODType;
};

template <>
struct TypeTraits< float>
{
	typedef __TrueType __IsPODType;
};

template <>
struct TypeTraits< double>
{
	typedef __TrueType __IsPODType;
};


template<class T>
class SeqList
{
public:
	SeqList ()
		:_pdata(NULL)
		,_size(0)
		,_capacity(3)
	{
		_pdata = new T[3];
	}
	~SeqList ()
	{
		if(_pdata != NULL)
		{
			delete[] _pdata;
			_size = 0;
			_capacity = 0;
		}
	}
	template<typename T>
	friend ostream& operator<<(ostream& os, const SeqList<T>& s);

	void Display()
	{
		for(int i = 0; i<_size; i++)
		{
			cout<<_pdata[i]<<" ";
		}
		cout<<endl;
	}

	void PushBack(const T& data);
	void PushFront(const T& data);
	void PopBack();
	void PopFront();
	void Sort();
	int Find(const T& P);
	void Intsert(const int& pos,const T& data);
	void Remove(const T& data);
	void RemoveAll(const T& data);


	T& operator[](int size)
	{
		assert(size<_size);
		assert(size>=0);

		return _pdata[size];
	}
	 
private :
    void CheckCapacity()//顺序表中的扩容函数;
	{
	if(_size == capacity)
	{
		int NewCapacity = capacity*2 + 3;
		T* tmp  = new T[Newcapacity];
		if(TypeTraits<T>::__IsPODType.get())// 判断返回值
		{
			memcpy(tmp,_pdata,capacity*sizeof(T));
		}// 假设原来顺序表的空间是 _pdata表示;
		else
		{
			for(int i = 0; i<_size; i++)
			{
				tmp[i] = _pdata[i];// 注意,我们这里是赋值操作符;
			}
		}
		delete _pdata;
		_pdata = tmp;
		capacity = NewCapacity;// 切记更新容量;
	}
}

	T* _pdata;
	int _size;
	int _capacity;
};


template<typename T>
 ostream& operator<<(ostream& os, const SeqList <T>& s )
{
	int i = 0;
	for(i = 0; i< s._size ;i++)
	{
		os<<s._pdata [i]<<" ";
	}
	return os;
}

template<typename T>
int SeqList <T>::Find(const T& data)
{
		int i = 0;

		for(i= 0 ; i < _sz; i++)
		{
			if(_pdata[i] == data)//注意这里的等号,千万别写成赋值等;
				return i;
		}
		return -1;
}

template<typename T>
void SeqList<T>:: PushBack(const T& data)
	{
		CheckCapacity ();
		_pdata[_size] = data;
		_size++;
	}

template<class T>
void SeqList<T>::PushFront(const T& data)
{
	    CheckCapacity();
		int length =  _size;
		while(length)
		{
			_pdata[length]=_pdata[length-1];
			length--;
		}
		_pdata[0] = data;
		_size++;
}

template<typename T>
void SeqList <T>::PopBack ()
{
			if(_size>=1)
		{
			_size-=1;
		}
}

template<typename T>
void SeqList <T>::PopFront()
{
			int len = 0;
		while(len<_size-1)
		{
			_pdata[len] = _pdata[len+1];
			len++;
		}
		_size--;
}

template<typename T>
void SeqList <T>::Intsert(const int& pos, const T& data)
{
		CheckCapacity ();

		int index = Find(pos);
		if(index>=0)
		{
			int length = _size;
		while(length > index)
		{
			_pdata[length]=_pdata[length-1];
			length--;
		}
		_pdata[index] = data;
		_size++;
		}
}

template<typename T>
void SeqList <T>::Remove(const T& data)
{
			int index = Find(data);

		if(index>=0)
		{
		while(index<_size-1)
		{
			_pdata[index] = _pdata[index+1];
			index++;
		 }
		}
		_size--;
}

template<typename T>
void SeqList <T>::RemoveAll (const T& data)
{
		while(Find(data)>=0)
		{
			Remove(data);
		}
}

template<typename T>
void SeqList <T>::Sort ()
{
	for(int i = 0; i<_size-1; i++)
	{
		for(int j = 0; j < _size-i-1; j++)
		{
			if(_pdata[j]<_pdata[j+1])
			{
				T tmp = _pdata [j];
				_pdata [j] = _pdata [j+1];
				_pdata [j+1] = tmp;
			}
		}
	}
}

void test()
{
	SeqList<string> s;
	SeqList<int> s1;
	s1.PushBack(2);
	s1.PushBack(5);
	s1.PushBack(4);
	s1.PushBack(6);
	s.PushBack ("aaaaaaaaaaaaaa");
	s.PushBack ("dsadsas");
	s.PushBack ("ssssssssssss");
	s.PushBack ("sdasadadafffffffffffffffffffffffffffffffffff");
	s.PushBack ("sdasdas");
	s1.Sort ();
	//s1.Display ();
	cout<<s1<<endl;
}

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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值