太原做网站哪家好,房产信息查询平台,北京网站建设推,网站代码 输入文字 跳出内容课程总目录 文章目录 一、详解函数模板二、类模板三、类模板实践#xff1a;实现向量容器vector四、理解容器空间配置器allocator的重要性 一、详解函数模板
模板的意义#xff1a;对类型也可以进行参数化了
// 也可以用templateclass T#xff0c;但class容易和类…
课程总目录 文章目录 一、详解函数模板二、类模板三、类模板实践实现向量容器vector四、理解容器空间配置器allocator的重要性 一、详解函数模板
模板的意义对类型也可以进行参数化了
// 也可以用templateclass T但class容易和类混淆我们都用typename
templatetypename T // 模板参数列表
bool compare(T a, T b) // compare是一个函数模板
{cout template compare endl;return a b;
}/*
调用点实例化出来的模板函数
bool compareint(int a, int b)
{return a b;
}bool comparedouble(double a, double b)
{return a b;
}
*/int main()
{// 函数的调用点compareint(10, 20);comparedouble(10.5, 20.5);// 函数模板实参推演compare(20, 20); // 还是用的刚才实例化的compareint// compare(30, 40.5); // 错误推演不出来是什么类型// 解决方法一templatetypename T, typename Ea和b用两个类型各推各的// 解决方法二compareint(30, 40.5)double强转成int
}函数模板不进行编译因为类型还不知道 模板函数在函数调用点编译器用程序员指定的类型从原模板实例化一份函数代码出来这就叫做模板函数这是实例化出来真正需要进行编译的函数因此站在编译器的角度来看待编译的函数并没有减少只是我们编写的代码量减少了。 同时实例化出来的模板函数在.o文件符号表中产生相应的符号每个函数名的符号只能出现一次 来看看字符串的情况 模板的特例化
// 针对compare函数模板提供const char*类型的特例化版本
template // 要写上
bool compare(const char* a, const char* b)
{cout compareconst char* endl;return strcmp(a, b) 0;
}
// 模板特化不需要在函数名后面加上类型参数
// 即别写成compareconst char*int main()
{// 推演T为const char*字符串 代表的是比较两个常量的地址要用strcmp才能比较字符串的字典顺序// 对于某些类型来说依赖编译器默认实例化的模板代码代码处理逻辑是错误的// 这时候就需要我们进行模板的特例化了这不是编译器提供的而是程序员提供的compare(aaa, bbb);compareconst char*(aaa, bbb);// 这两种写法都是对的
}当然非模板函数普通函数优先被调用
//非模板函数 - 普通函数
bool compare(const char* a, const char* b)
{cout normal compare endl;return strcmp(a, b) 0;
}int main()
{// 这时候就调用普通函数了不调用模板函数了compare(aaa, bbb);// 调用模板函数compareconst char*(aaa, bbb);
}编译器优先把compare处理成函数名字没有的话才去找compare模板特例化如果没有特例化才进行模板的实例化 分文件编写
模板代码是不能在一个文件中定义在另一个文件中使用的否则链接的时候会出现错误
比如在test.cpp中存放模板代码在main.cpp中声明这是不可以的因为声明产生的符号是*UND*而在test.cpp中只有模板模板本身是不编译的没有模板实例化出来的compareint等函数所以不可以
模板代码调用之前一定要看到模板定义的地方这样的话模板才能进行正常的实例化产生能够被编译器编译的代码。
所以模板代码都是放在头文件.h当中的然后在原文件当中直接进行#include包含
模板的非类型参数
必须是整数类型整数或者地址/引用都可以是常量只能使用而不能修改
模板不仅可以接受类型参数typename T还可以接受非类型参数。这些非类型参数可以是整型、指针、引用等。它们在编译时是常量只能使用不能修改
示例代码
template int N
class Array {
public:int arr[N];int size() const { return N; }
};int main() {Array5 myArray; // 创建一个包含5个整数的数组cout Array size: myArray.size() endl;return 0;
}
// 使用模板实现冒泡排序
template typename T, int N
void bubbleSort(T* arr) {for (int i N - 1; i 1; --i){int flag 0;for (int j 1; j i; j){if (arr[j - 1] arr[j]){T temp arr[j];arr[j] arr[j - 1];arr[j - 1] temp;flag 1;}}if (flag 0)return;}
}int main() {int arr[] { 64, 34, 25, 12, 22, 11, 90 };const int size sizeof(arr) / sizeof(arr[0]);// 调用冒泡排序模板函数bubbleSortint, size(arr);cout 排序后的数组: ;for (int i : arr)cout i ;cout endl;return 0;
}二、类模板
类模板 → \to → 实例化 → \to → 模板类类名称 模板名称 类型参数列表为了简化构造和析构函数不用加T其他出现模板的地方都要加上类模板可以设置默认类型参数实例化的时候只用写SeqStack就行了
//templatetypename T int // 类模板可以设置默认类型参数
templatetypename T
class SeqStack
{
public:SeqStack(int size 10):_pstack(new T[size]), _top(0), _size(size){}~SeqStack(){delete[]_pstack;_pstack nullptr;}SeqStack(const SeqStackT stack):_top(stack._top), _size(stack._size){_pstack new T[_size];for (int i 0; i _top; i)_pstack[i] stack._pstack[i];}SeqStackT operator(const SeqStackT stack){// 防止自赋值if (this stack)return *this;delete[]_pstack;_top stack._top;_size stack._size;_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(){cout pop(): _pstack[_top] endl;if (empty())return;--_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;// 扩容void expand(){T* ptmp new T[_size * 2];for (int i 0; i _top; i)ptmp[i] _pstack[i];delete[] _pstack;_pstack ptmp;_size * 2;}
};// 在类外实现成员方法
// 注意点1.加类的作用域SeqStackT:: 2.写templatetypename T
templatetypename T
void SeqStackT::push(const T val)
{cout push(const T val): val endl;if (full())expand();_pstack[_top] val;
}三、类模板实践实现向量容器vector
templatetypename T
class vector
{
public:vector(int size 10){_first new T[size];_last _first;_end _first size;}~vector(){delete[] _first;_first _last _end nullptr;}vector(const vectorT vec){int size vec._end - vec._first;_first new T[size];int len vec._last - vec._first;for (int i 0; i len; i)_first[i] vec._first[i];_last _first len;_end _first size;}vectorT operator(const vectorT vec){// 防止自赋值if (this vec)return *this;// 释放本身指向delete[] _first;// 拷贝int size vec._end - vec._first;_first new T[size];int len vec._last - vec._first;for (int i 0; i len; i)_first[i] vec._first[i];_last _first len;_end _first size;}void push_back(const T val) // 向容器末尾添加元素{if (full())expend();*_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 _first _last; }int size() const { return _last - _first; }private:T* _first; // 指向数组起始位置T* _last; // 指向数组中有效元素的后继位置T* _end; // 指向数组空间的后继位置void expend() // 容器的二倍扩容{int size _end - _first;T* ptmp new T[2 * size];for (int i 0; i size; i)ptmp[i] _first[i];delete[] _first;_first ptmp;_last _first size;_end _first 2 * size;}
};int main()
{vectorint vec;for (int i 0; i 20; i)vec.push_back(i);vec.pop_back(); // 弹出19while (!vec.empty()){// 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0cout vec.back() ;vec.pop_back();}cout endl;return 0;
}四、理解容器空间配置器allocator的重要性
目前存在的问题