C++模板类的类型萃取技术
来源:互联网 发布:js二维数组固定长度 编辑:程序博客网 时间:2024/06/06 12:38
模板是泛型编程的基础,所谓泛型编程就是指编写与类型无关的逻辑代码,是一种复用的方式。所以使用模板的目的就是方便程序员编写与类型无关的代码,减少相似代码在程序中出现的概率。
假如我们要编写一个判断两个变量是否相等的函数,那么为了处理int,char,double等不同类型的变量,我们得一一写出这些类型的重载函数。这样不仅代码会显得十分冗余,也会增加程序员无谓的工作量。为了解决这个问题,C++中引入了模板的概念。如下,我们可以利用模板来编写判断两个变量是否相等的函数。
template<class T>bool IsEqual(const T& left, const T& right){return left == right;}
void Test1(){string s1("xiaoxu");string s2("xiaoyu");cout << IsEqual(s1, s2) << endl; //比较string类型的变量cout << IsEqual(11, 11) << endl; //比较int类型的变量cout << IsEqual('1', '1') << endl;//比较char类型的变量}
有了模板,我们不仅可以编写模板函数还可以编写模板类。以下就是利用模板实现顺序表的代码。
template<typename T>class SeqList{public:SeqList():_array(NULL), _size(0), _capacity(0){}SeqList<T>(const SeqList<T>& s):_array(new T[s._size]), _size(s._size), _capacity(s._size){memcpy(_array, s._array, sizeof(T)*s._size);}/*SeqList<T>& operator=(const SeqList<T>& s){if (this != &s){T* tmp = new T[s._size];memcpy(tmp, s._array, sizeof(T)*s._size);delete[] _array;_array = tmp;_size = s._size;_capacity = s._size;}return *this;}*/SeqList<T>& operator=(const SeqList<T>& s){if (this != &s){SeqList tmp(s);swap(_array, tmp._array);swap(_size, tmp._size);swap(_capacity, tmp._capacity);}return *this;}/*SeqList<T>& operator=(SeqList<T> s){swap(_array, s._array);swap(_size, s._size);swap(_capacity, s._capacity);return *this;}*/~SeqList(){if (_array){delete[] _array;_size = _capacity = 0;}}void PushBack(const T& d);void PopBack();void PushFront(const T& d);void PopFront();void Print();void Insert(size_t pos, const T& d);void Erase(size_t pos);size_t Find(const T& d);const T& Back();size_t Size();bool Empty();protected:void CheckCapacity(){if (_size == _capacity){_capacity = _capacity * 2 + 3;_a = (DataType*)realloc(_a, sizeof(DataType)*_capacity);}}protected:T* _array;size_t _size;size_t _capacity;};template<typename T>void SeqList<T>::PushBack(const T& d){_CheckCapacity();_array[_size] = d;_size++;}template<typename T>void SeqList<T>::PopBack(){if (_size > 0){_size--;}else{cout << "顺序表为空,无法尾删" << endl;}}template<typename T>void SeqList<T>::PushFront(const T& d){_CheckCapacity();for (size_t i = _size; i > 0; i--){_array[i] = _array[i-1];}_size++;_array[0] = d;}template<typename T>void SeqList<T>::PopFront(){if (_size > 0){for (size_t i = 0; i < _size; i++){_array[i] = _array[i + 1];}_size--;}else{cout << "顺序表为空,无法头删" << endl;}}template<typename T>void SeqList<T>::Insert(size_t pos, const T& d){_CheckCapacity();if (_size == 0){PushFront(d);}else{for (size_t i = _size; i > pos - 1; i--){_array[i] = _array[i - 1];}_array[pos - 1] = d;_size++;}}template<typename T>void SeqList<T>::Erase(size_t pos){if (_size > 0){for (size_t i = pos-1; i < _size; i++){_array[i] = _array[i + 1];}_size--;}else{cout << "顺序表为空,无法删除数据" << endl;}}template<typename T>size_t SeqList<T>::Find(const T& d){for (size_t i = 0; i < _size; i++){if (_array[i] == d){return i + 1;}}return 0;}template<typename T>void SeqList<T>::Print(){{if (_size > 0){for (size_t i = 0; i < _size; i++){cout << _array[i] << " ";}cout << endl;}else{cout << "顺序表为空" << endl;}}}
由于模板不支持分离编译,所以我将类的成员函数的声明和定义写在了一个.hpp文件中。
以下为测试代码。
void Test1(){SeqList<int> s1;s1.PushBack(1);s1.PushBack(2);s1.PushBack(3);s1.PushBack(4);s1.Print();//s1.PopBack();//s1.Print();//s1.PopBack();//s1.PopBack();//s1.PopBack();//s1.Print();//s1.PopBack();//s1.PushFront(4);//s1.PushFront(3);//s1.PushFront(2);//s1.PushFront(1);//s1.Print();//s1.PopFront();//s1.Print();//s1.PopFront();//s1.PopFront();//s1.PopFront();//s1.Print();//s1.PopFront();}输出结果:
void Test2(){SeqList<string> s2;s2.PushBack("22");s2.PushBack("1111111111111111111111111111111111111111111111111111111111111111");s2.PushBack("33");s2.PushBack("44");s2.Print();//s2.PopBack();//s2.Print();//s2.PopBack();//s2.PopBack();//s2.PopBack(); //s2.Print();//s2.PopBack();}输出结果:
可以看到,测试int类型时程序正常输出,测试string类型时程序却崩溃了。罪魁祸首是下面这条语句
memcpy(_array, s._array, sizeof(T)*s._size);
memcpy()是值拷贝函数,当我们使用它拷贝基本类型时没有任何问题,可是拷贝string类型时问题就出现了,VS下的string类型的实现原理是若字符串不长则以数组保存,若字符串过长(超过16),则通过指针在堆上开辟空间保存,所以当使用memcpy()函数拷贝较长的字符串(长度超过16)时,会直接拷贝指针,从而出现深浅拷贝的问题。
那么为了解决这个问题我们还得重新写一份代码吗?完全不用!用类型萃取技术即可解决。
类型萃取是一种常用的编程技巧,它可以使不同类型数据实现同一份函数代码。在调用时,事先并不知道对象是什么类型,编译器是开始编译后才进行模板推演,根据不同的对象类型生成相应的代码。以下就是用类型萃取优化上述代码。
struct __TrueType{bool Get(){return true;}};struct __FalseType{bool Get(){return false;}};template<typename T>struct TypeTraits //类型萃取{typedef __FalseType IsPodType; //Pod——内置类型};template<>struct TypeTraits<int> //特化内置类型{typedef __TrueType IsPodType;};重新改写一下_Checkcapacity()函数即可,分成两种实现方式可提高代码运行效率。
void _CheckCapacity(){if (_size >= _capacity){_capacity = _capacity * 2 + 3;if (TypeTraits<T>::IsPodType().Get()) //内置类型的实现方式{cout << "Pod Type" << endl;_array = (T*)realloc(_array, sizeof(T)*_capacity);}else //非内置类型的实现方式{cout << "Not Pod Type" << endl;T* tmp = new T[_capacity];if (_array){//memcpy(tmp, _array, sizeof(T)*_size);for (size_t i = 0; i < _size; i++){tmp[i] = _array[i];}delete[] _array;}_array = tmp;}}}成功输出string类型的顺序表:
0 0
- C++模板类的类型萃取技术
- c++:模板的类型萃取
- c++:模板的类型萃取
- C++_模板类与类型萃取技术
- 模板的类型萃取
- 模板的类型萃取
- 模板的类型萃取
- 模板的类型萃取
- 模板的类型萃取
- C++ 模板类型萃取技术
- C++ 模板类型萃取技术 第二部分 基于泛型的类型萃取技术
- 类型萃取&类的模板特化
- C++的类型萃取技术
- C++的类型萃取技术
- C++的类型萃取技术
- C++的类型萃取技术
- C++的类型萃取技术
- C++ 模板类型萃取技术 traits
- 简单聊聊HDFS的HA
- js中关于交换两变量值得四中方法的思考
- systemtap调试内核
- LeetCode-Easy部分中标签为Dynamic Programming 121. Best Time to Buy and Sell Stock
- 洛谷P1967codevs3287货车运输
- C++模板类的类型萃取技术
- WOJ 24. Divide by Six
- CopyOnWriteArrayList读写锁
- sqoop从mysql导入hdfs数据过程遇到的问题
- Eclipse建立Maven项目后无法建立src/main/java资源文件夹的办法
- Power Calculus UVA
- Win7 + Ubuntu 16.04 双系统安装
- 面试题41:有序数组,查找和为s的两个元素and 和为s的连续数字
- Lintcode 寻找峰值