山东淄博网站建设游戏推广公司怎么接游戏的
C++ 函数模板
一般使用在开发库中
模板的意义:对类型也可以进行参数化了
可以定义多个类型参数,中间用逗号隔开
也可以用关键字class替代typename
我们看下面代码
compare只是一个函数模板名称。我们调用这个compare的时候,肯定是希望这个是模板函数名称。
模板名加上参数列表才是函数名称
在函数调用点,编译器用用户指定的类型,从原模板实例化一份函数代码出来模板函数如下
bool compare<int>(int a, int b)
{return a > b;
}
如果我们又去调用一个compare的时候,double类型的。
在调用点,编译器又会用double去实例化一份函数代码出来模板函数如下
bool compare<double>(double a, double b)
{return a > b;
}
模板的好处是
我们用户只用写一套代码实现逻辑就可以了,至于用什么类型,模板最终用什么类型实例化,我们可以在调用的地方去指定类型,编译器就会从原模板用指定的类型去实例化代码出来。
对于编译器来说,需要待编译的函数并没有减少,每个类型都对应一份函数。
只用函数模板的名字,我们也可以进行函数的调用
这是因为 模板的实参推演 => 可以根据用户传入的实参的类型,来推导出模板类型参数的具体类型
注意:不会再生成和之前生成的模板函数一模一样的函数,不然就成函数的重定义了,会产生符号,每个模板函数的符号只能出现一次,每种模板函数只产生一份,下次需要调用就直接调用之前产生的那个对应的模板函数就可以了
下面这种情况是什么样的?
通过第一个传入的实参,推导出T是int整型,通过第二个传入的实参,推导出T是double型,那么T到底是int还是double呢???
编译器推导不出来。
解决方法:另外定义一个新的模板类型参数,a和b用不同的模板类型参数。
我们还可以指定模板类型是int,第一个实参是整型,就传给整型,第二个实参是double,就把double转换成int整型带进去。
函数模板: 是无法编译的。因为不知道T的类型是什么,无法编译。
函数模板 <= 是不进行编译的,因为类型还不知道
模板的实例化 <= 函数调用点进行实例化
模板函数 <= 才是要被编译器所编译的
假设现在我们要比较两个字符串。
也是可以比较的
但是,我们在调用这个模板的时候,并没有指定这个参数列表,它就得进行函数模板实参的推演,通过实参推导出T是const char*
得到的实例化的模板函数是:
bool compare<const char*>(const char* a, const char* b)
{return a>b;
}
显然这是在比较两个字符串的地址的大小,没有意义啊。
我们肯定是想比较它们的字典顺序。
实际上,应该调用
bool compare<const char*>(const char* a, const char* b)
{return strcmp(a, b) > 0;
}
但是编译器没有办法,因为模板给的是a>b,它只能用你指定的或者推导出来的类型来确定类型,并不能根据具体的类型来改变你的代码。
对于某些类型来说,依赖编译器默认实例化的模板代码,代码处理逻辑是有错误的
这时候怎么办?
我们需要给模板提供特例化了。
模板的特例化(专用化) 特殊(不是编译器提供的,而是用户提供的)的实例化
先有模板,才能模板特例化哦
当然,我们的代码还可以出现这样的操作
函数模板、模板的特例化、非模板函数的重载关系
现在进行调用这个代码的话,编译器优先看成是函数名!
调用的是普通的非模板函数
但是,如果是写出下面这样的形式
编译器无论如何不能把compare当成函数名了,因为函数名后面是不能加<>的,只有模板名的后面才可以加<>
所以,这时编译器就去看compare模板,刚要实例化模板,看到用户已经自定义特例化版本了,就直接用特例化。
//函数模板实参的推演 T const char*//对于某些类型来说,依赖编译器默认实例化的模板代码,代码处理逻辑是有错误的//编译器优先把compare处理成函数名字,没有的话,才去找compare模板compare("aaa", "bbb");compare<const char*>("aaa", "bbb");
我们再看,我们在写多文件的时候,要注意:
我们在一个文件中定义模板,在另一个文件中声明模板,然后使用它。
会发生链接错误
找不到相应的符号
我们把int,double的调用模板代码屏蔽掉,是可以调用特例化的。
我们把自定义的普通函数也声明一下。
模板代码是不能在一个文件中定义,在另外一个文件中使用的
模板代码调用之前,一定要看到模板定义的地方,这样的话,模板才能够进行正常的实例化,产生能够被编译器编译的代码
所以,模板代码都是放在头文件当中的,然后在源文件当中直接进行#include包含
include是在预编译的过程中,把头文件的代码直接在当前源文件中展开
如果一定要在多个文件调用,还是有办法的。
不用看调用点,直接用用户指定的类型进行模板实例化。
模板的非类型参数
必须是整数类型(整数或者地址/引用都可以)
都是常量,只能使用,而不能修改
template<typename T, int SIZE>
void sort(T *arr)
{for (int i = 0; i < SIZE - 1; ++i){for (int j = 0; j < SIZE - 1 - i; ++j){if (arr[j] > arr[j + 1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}
}
int main()
{int arr[] = { 12,5,7,89,32,21,35 };const int size = sizeof(arr) / sizeof(arr[0]);//常量表达式 sort<int, 7>(arr);for (int val : arr){cout << val << " ";}cout << endl;return 0;
}
C++类模板
模板类实现顺序栈
类模板 => 实例化 => 模板类
//类模板
template<typename T>
class SeqStack//模板名称+类型参数列表 = 类名称
{
public://构造和析构函数名不用加<T>,其它出现模板的地方都加上类型参数列表SeqStack(int size = 10): _pstack(new T[size]), _top(0), _size(size){}~SeqStack(){delete[]_pstack;_pstack = nullptr;}SeqStack(const SeqStack<T> &stack):_top(stack._top),_size(stack._size){_pstack = new T[_size];//不要用memcopy进行拷贝for (int i = 0; i < _top; ++i){_pstack[i] = stack._pstack[i];}}SeqStack<T>& operator=(const SeqStack<T> &stack){if (this == &stack)return *this;delete[]_pstack;_top = stack._top;_size = stack._size;_pstack = new T[_size];//不要用memcopy进行拷贝for (int i = 0; i < _top; ++i){_pstack[i] = stack._pstack[i];}return *this;}void push(const T &val);//入栈操作void pop()//出栈操作{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;//顺序栈底层数组按2倍的方式扩容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;}
};
template<typename T>//类外实现,还要再写这个哦,因为这个类模板作用域到{开始 }结束了
void SeqStack<T>::push(const T &val)//入栈操作
{if (full())expand();_pstack[_top++] = val;
}int main()
{//类模板的选择性实例化//实例化后 产生模板类 class SeqStack<int>{};SeqStack<int> s1;//编译器到达这个调用点,会把模板实例化一份专门处理int的类出来 //到这个点生成的模板类只有构造函数和析构函数,其他的方法等被对象调用了才被实例化出来 //减少编译器的工作 s1.push(20);s1.push(78);s1.push(32);s1.push(15);s1.pop();cout << s1.top() << endl;SeqStack<> s2;return 0;
}
类模板还可以加默认的类型参数
就可以这样去使用它