类模板模板的意义:对类型也可以进行参数化了
int sum(int a ,int b) {return a + b;}
函数模板 :是不进行编译的,因为类型还不知道
模板的实例化 :函数调用点进行实例化
在函数调用点,编译器用用户指定的类型,从原模板实例化一份函数代码出来
比如:
compare
(10,20); 调用它就会实例化以下的函数代码 bool compare
(int a,int b) {
return a > b;
}
compare
(10.5,20.5); bool compare
(double a,double b) {
return a > b;
}
特殊的比较:compare("aaa","bbbb");
compare
(const char* a,const char *b) {
return strcmp(a,b) > 0;
}
模板函数 :才是要被编译器所编译的
模板类型参数 typename/class
模板的非类型参数: 必须是整数类型(整数或者地址/引用都可以),都是常量,只能使用,而不能修改 template
SIZE为非类型参数 //必须要指定类型以及传SIZE
sort
(arr); 模板的实参推演 :可以根据用户传入的实参的类型,来推导出模板类型参数的具体类型
模板的特例化(专用化):特殊(不是编译器提供的,而是用户提供的)的实例化
模板函数、模板的特例化、非模板函数的重载关系
模板代码是不能在一个文件中定义,在另外一个文件中使用的,模板代码调用之前,一定要看到模板定义的地方,这样的话,模板才能够进行正常的实例化,产生能够被编译的代码
所以,模板代码都是放在头文件当中的,然后在源文件中直接进行#include 包含
//定义一个模板参数列表,多个就用逗号隔开 //compare 是一个函数模板 templatebool compare(T a, T b) { cout << "temaplate compare" << endl; return a > b; } //针对compare函数模板,提供const char* 类型的特例化版本 template<> bool compare (const char *a, const char *b) { cout << "compare " << endl; return strcmp(a,b) > 0; } //非模板函数 bool compare(const char *a, const char *b) { cout << "normal compare" << endl; return strcmp(a, b) > 0; } int main() { //模板调用需要加上<>以及指定类型才能是函数 compare (10,20); //对于某些类型来说,依赖编译器默认实例化的模板代码,代码处理逻辑是有问题的,可以写一个特例化版本 //编译器优先把compare处理成函数名字,没有的话,才去找compare模板 compare("aaa","bbb"); return 0; }
#includeusing namespace std; template class SeqStack //模板名称 + 类型参数列表 = 类名称 { public: //构造和析构函数名可以不加 ,其他出现模板的地方都加上类型参数列表 SeqStack (int size = 10): _pstack(new T[size]), _top(0), _size(size) {} ~SeqStack () { delete []_pstack; _pstack = nullptr; } SeqStack (const SeqStack &stack) : _top(stack._top),_size(stack._size) { _pstack = new T[_size]; for (int i = 0; i < _top; ++i) { _pstack[i] = stack._pstack[i]; } } SeqStack & operator=(const SeqStack &stack) { if (this == &stack) return *this; delete []_pstack; _size = stack._size; _top = stack._top; _pstack = new T[_size]; for(int i = 0; i < _top; ++i) { _pstack[i] = stack._pstack[i]; } return *this; } void push(const T &val); void pop() { if(!empty()) --_top; } T top() const { if (empty()) throw "stack is empty!"; return _pstack[_top-1]; } bool full() const { return _top == _size ; } bool empty() const { return _top == 0; } private: T *_pstack; int _top; int _size; //按照2倍的方式扩容 void expand() { T *temp = new T[_size * 2]; for (int i = 0; i < _top; ++i) { temp[i] = _pstack[i]; } delete []_pstack; _pstack = temp; _size *= 2; } }; template //在外边定义类函数的时候,必须要重新写类命名 void SeqStack ::push(const T &val) { if (full()) expand(); _pstack[_top++] = val; } int main() { SeqStack s1; return 0; } 实现C++STL 向量容器vector代码
无容器空间配置器版本:
templateclass vector { public: //构造 vector(int size = 10) { _first = new T[size]; _last = _first; _end = _first + size; } //析构 ~vector() { delete []_first; _first = _last = _end = nullptr; } //拷贝构造 vector (const vector &rhs) { int size = rhs._end - rhs._first; _first = new T[size]; int len = rhs._last - rhs._first; for(int i = 0; i < len; ++i) { _first[i] = rhs._first[i]; } _last = _first + len; _end = _first + size; } //赋值重载 vector & operator=(const vector &rhs) { if(this == &rhs) return *this; delete []_first; int size = rhs._end - rhs._first; int len = rhs._last - rhs._first; _first = new T[size]; for(int i = 0; i < len; ++i) { _first[i] = rhs._first[i]; } _last = _first + len; _end = _first + size; return *this; } //向容器末尾添加元素 void push_back(const T &val) { if(full()) expand(); *_last = val; _last ++; // *_last++ = val; } //从容器末尾删除元素 void pop_back() { if(empty()) return; --_last; } //返回容器末尾的值 T back() const { return *(_last - 1); } //检查容器是否满了 bool full() const {return _last == _end;} //检查容器是否为空 bool empty() const { return _last == _first;} //返回容器的个数 int size() const {return _last - _first;} private: T *_first; // 指向数组起始的位置 T *_last ; // 指向数组中有效元素的后继位置 T *_end ; // 指向数组空间的后继位置 void expand() { int size = _end - _first; T *temp = new T[size * 2]; for (int i = 0; i < size; i++) { temp[i] = _first[i]; } delete []_first; _first = temp; _last = _first + size; _end = _first + size * 2; } }; int main() { vector vec; for(int i = 0; i < 20 ; ++ i) { vec.push_back(rand() % 1000); } vec.pop_back(); while(!vec.empty()) { cout << vec.back() << " "; vec.pop_back(); } cout << endl; return 0; } 1.如果只是普通类型的数据是没有问题的,如果是特殊类型的就有问题了,比如向容器里边存类对象,需要一个一个的存,并不是全部就给我构造了。应该是只开辟内存,不初始化类。如以下代码:new 申请内存就直接初始化了。了解请移步看看new 和 delete
class Test { public: Test(){ cout << "Test()" << endl; } ~Test() { cout << "~Test()" << endl; } }; int main() { vectorvec; return 0; } int main() { Test t1,t2,t3; cout << "------------------" << endl; vectorvec; //vector 的构造如果直接new Test[10] 的话,导致10个Test就直接构造了。 //在当前加入一个对象的时候。 //就相当于在数组0的位置做了一个赋值操作,因为对象已经有初始化的对象了 vec[0] = t1;两个存在的相同对象做赋值操作 vec.push_back(t1); vec.push_back(t2); vec.push_back(t3); cout << "------------------" << endl; //做pop_back操作的时候如果直接--_last 内存减减,如果这个对象有指向外部资源的话,就会找不到了 vec.pop_back(); //需要析构对象。要把对象的析构和内存释放分离开 delete cout << "------------------" << endl; return 0; } 以上的情景就是开辟内存就全部构造,跟场景需求根本就不符合。还有析构vector的时候,首先需要先析构容器的有效元素。然后再释放_first指针指向的堆内存
容器空间配置器allocator
空间适配器做的事情:
1.内存开辟 / 内存释放
2.对象构造 /对象析构
#include#include using namespace std; //空间适配器 template class Allocator { public: T* allocate(size_t size) // 只负责开辟内存 { return (T*)malloc(sizeof(T) * size); } void deallocate(void *p) // 只负责内存释放 { free(p); } //在指定开辟好的内存上进行构造,用定位new void construct(T *p, const T &val) { new (p) T(val); } void destroy(T *p) //负责对象析构 { p->~T(); //调用对象的析构函数 ~T()代表了T类型的析构函数 } }; template > // Alloc 默认使用Allocator class vector { public: //构造 //空间配置器可以让用户传进来,模板上边就不用 Alloc 那个参数名了 // vector(int size = 10,const &alloc = Allocator ()):_allocator(alloc) vector(int size = 10) { //需要把内存开辟和对象构造分开处理 // _first = new T[size]; _first = _allocator.allocate(size); _last = _first; _end = _first + size; } //析构 ~vector() { //析构容器有效地元素,然后再释放_first指针指向的堆内存 // delete []_first; for(T *p = _first; p != _last; ++p) { //把_first指针指向的数组的有效元素进行析构操作 _allocator.destroy(p); } //释放堆上的数组内存 _allocator.deallocate(_first); _first = _last = _end = nullptr; } //拷贝构造 vector(const vector &rhs) { int size = rhs._end - rhs._first; // _first = new T[size]; _first = _allocator.allocate(size); int len = rhs._last - rhs._first; for(int i = 0; i < len; ++i) { // _first[i] = rhs._first[i]; _allocator.construct(_first+i,rhs._first[i]); } _last = _first + len; _end = _first + size; } //赋值重载 vector & operator=(const vector &rhs) { if(this == &rhs) return *this; // delete []_first; for(T *p = _first; p != _last; ++p) { //把_first指针指向的数组的有效元素进行析构操作 _allocator.destroy(p); } //释放堆上的数组内存 _allocator.deallocate(_first); int size = rhs._end - rhs._first; int len = rhs._last - rhs._first; // _first = new T[size]; _first = _allocator.allocate(size); for(int i = 0; i < len; ++i) { // _first[i] = rhs._first[i]; _allocator.construct(_first+i,rhs._first[i]); } _last = _first + len; _end = _first + size; return *this; } //向容器末尾添加元素 void push_back(const T &val) { if(full()) expand(); // *_last++ = val; //_last 指针指向的内存构造一个值为val的对象 _allocator.construct(_last,val); _last ++; } //从容器末尾删除元素 void pop_back() { if(empty()) return; --_last; //不仅仅要把_last指针--,还需要析构删除的元素 _allocator.destroy(_last); } //返回容器末尾的值 T back() const { return *(_last - 1); } //检查容器是否满了 bool full() const {return _last == _end;} //检查容器是否为空 bool empty() const { return _last == _first;} //返回容器的个数 int size() const {return _last - _first;} private: T *_first; // 指向数组起始的位置 T *_last ; // 指向数组中有效元素的后继位置 T *_end ; // 指向数组空间的后继位置 Alloc _allocator; //定义容器的空间配置器对象 void expand() { int size = _end - _first; // T *temp = new T[size * 2]; T *temp = _allocator.allocate(2*size); for (int i = 0; i < size; i++) { // temp[i] = _first[i]; _allocator.construct(temp+i,_first[i]); } // delete []_first; for(T *p = _first; p != _last; ++p) { //把_first指针指向的数组的有效元素进行析构操作 _allocator.destroy(p); } //释放堆上的数组内存 _allocator.deallocate(_first); _first = temp; _last = _first + size; _end = _first + size * 2; } }; class Test { public: Test(){ cout << "Test()" << endl; } Test(const Test &src) { cout << "Test(const Test &src)" << endl; } ~Test() { cout << "~Test()" << endl; } }; int main() { Test t1,t2,t3; cout << "------------------" << endl; vector vec; vec.push_back(t1); vec.push_back(t2); vec.push_back(t3); cout << "------------------" << endl; vec.pop_back(); cout << "------------------" << endl; return 0; }



