目录
引言:C++为啥有模板类?
1:可用来创建动态增加或减少的数据结构
2:它与某种特定类型无关,因此代码可重复使用
3:它在编译时检查数据类型而不是运行时检查数据类型,保证了类型的安全
4:它是平台无关的,具有很好的移植性
1.泛型编程
引言:我们先来写一个交换函数
//交换int类型 void Swap(int& left, int& right) { int temp = left; left = right; right = temp; } void Swap(double& left, double& right) { double temp = left; left = right; right = temp; }
如果有字符型还需要来个char类型的重载,但是各位不觉得有些烦琐吗?而且调用的概率低的可怜,C++可是高级语言呢,那能不能改进一下呢?各位有没有见过模具,就比如做月饼等用的我们想让月饼啥样式就啥样式那种,C++也提供了一种解决方式---泛型编程
模板是泛型编程的基础,使用反省编程可以编写与类型无关的通用代码,是代码复用的一种手段。
模板分为:
- ·类模板;
- ·函数模板
2.函数模板
2.1函数模板概念
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
template<typename T1, typename T2,..., typename Tn >
返回值类型(一般是T) 函数名()
{
//...
}
template是模板参数,T是模板类型,typename是关键字一般可以用class代替但是不能用struct代替。
就比如这个代码,int ,double都能编译,所以推出这个模板是有用的。
这就是高级语言的的高级之处,将我们应该重复做的自己给做了。
其实这个Swap函数我们也不用写,C++库里已经写了
我们直接用就行了。
2.2函数模板实例化
2.21实例化分为两类:
隐式实例化, 显式实例化。
隐式实例化就是让编译器根据实参推演模板参数的实际类型,如上述中的Swap函数。但是当我们传递的参数含有两种类型时,编译器无法推断T的具体类型,此时就需要我们使用显示实例化解决。
解决方式1:我们手动将类型转换Add(a , (int)d );
解决方式2:显式实例化如下
显式实例化
显示实例化:在函数名后面<>中指定参数类型。
如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错。
2.22函数模板支持多个实例化参数
template<class K, class V> //两个模板参数 void Func(const K& key, const V& value) { cout << key << ":" << value << endl; } int main() { Func(1, 1); //K和V均int Func(1, 1.1);//K是int,V是double Func<int, char>(1, 'A'); //多个模板参数也可指定显示实例化不同类型 }
2.3.模板参数的匹配问题
2.31匹配原则
1:一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。
//专题函数 int Add(int left, int right) { return left + right; } //模板函数 template<class T> T Add(T left, T right) { return left + right; } int main() { Add(2, 3); }
各位猜猜编译器会调用那个add函数?
看反汇编得知用的是专题Add,
专题Add函数就是专门用来解决加法的,而模板函数只是一个备胎,让我们选反正我选专题函数。
2:对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板
就比如这个代码,它与非模板函数不完全匹配并且调用模板函数的契合度更高,他就会调用模板函数。
3:模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
3.类模板
3.1类模板格式与实例化
引言:我们都知道typedef int SLDate ,那我们的实例化对象有int型又有double型呢,一个typedef就管不了这么多了所以来了个类模板
3.11格式:
template<class T1, class T2, ..., class Tn> class 类模板名 { // 类内成员定义 };
让我们用类模板写一个栈试试
//类模板完成栈
template<class T>
class Stack
{
public:
Stack(size_t capacity = 0)
:_a(nullptr)
, _top(0)
, _capacity(capacity)
{
if (capacity > 0)
{
_a = new T[capacity];
_top = 0;
_capacity = capacity;
}
}
~Stack();//类外定义
void Push(const T& x)
{
if (_capacity == _top)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
T* tmp = new T[newcapacity];
if (_a)
{
memcpy(tmp, _a, _top * sizeof(T));
delete[] _a;
}
_a = tmp;
_capacity = newcapacity;
}
_a[_top++] = x;
}
void Pop()
{
assert(_top > 0);
_top--;
}
const T& Top()
{
assert(_top > 0);
return _a[_top - 1];
}
bool Empty()
{
return _top == 0;
}
private:
T* _a;
size_t _top;
size_t _capacity;
};
//类外定义--加模板参数列表
template<class T>
Stack<T>::~Stack()
{
delete[] _a;
_top = _capacity = 0;
_a = nullptr;
}
int main()
{
Stack<int> s;//使用时需要显示提供参数类型
s.Push(1);
s.Push(2);
s.Push(3);
s.Push(4);
s.Push(5);
while (!s.Empty())
{
cout << s.Top() << " ";
s.Pop();
}
cout << endl;
return 0;
}
注意:类模板一定要指定,函数模板根据情况指定。如果模板函数不能推演就要指定指定模板参数进行显式实例化。
4非类型模板参数
它的名字有点绕,就是上面的类模板参数是泛型的根据需要能变成int,double,char等,但是这个非类模板参数就是这个参数当做一个常量来使用,比如我可以用它来指定一个数组的大小,数组大小就由我们随意改变即可。
template<class T,size_t N = 10> class Mystack { public: //... private: T _a[N]; size_t _top; }; int main() { Mystack<int,100> st1; //100 Mystack<int,200> st2; //200 Mystack<int> st3; //可以给缺省参数:从右向左确,且连续 return 0; }
这个n就是非类模板参数。
非类模板参数的缺点:
1.浮点数和类类型不可以作为非类型模板参数。
2.不能使用字符串常量和指针作为非类型模板参数的实参,但是数组可以。
5.具体用法
5.1用法
C++中的类模板和函数模板是用来实现通用编程的重要工具,它们可以使得代码更加灵活和易于维护。下面分别介绍类模板和函数模板的用法。
- 类模板
类模板是一种通用的类,其中某些成员变量或成员函数的类型可以根据实例化时指定的类型而变化。类模板的定义以template关键字开头,后面跟上模板参数列表,然后是普通类的定义。例如:
template <typename T> class MyVector { public: MyVector(); void push_back(T val); T operator[](int index) const; private: T* data_; int size_; int capacity_; };
在这个例子中,类模板MyVector可以用来表示任意类型的向量。其中,模板参数T表示向量中元素的类型。在类模板中,我们可以使用T替代任何需要表示元素类型的地方,包括成员变量、成员函数的参数类型和返回类型等。
当我们需要实例化一个具体类型的MyVector时,需要提供一个类型参数,例如:
MyVector<int> v1; MyVector<double> v2;
在这里,v1是一个存储int类型元素的向量,v2是一个存储double类型元素的向量。
2.函数模板
函数模板是一种通用的函数,其中某些参数或返回值的类型可以根据调用时传入的类型而变化。函数模板的定义以template关键字开头,后面跟上模板参数列表,然后是普通函数的定义。例如:
template <typename T> T Max(T a, T b) { return a > b ? a : b; }
在这个例子中,函数模板Max可以用来计算任意类型的值的最大值。其中,模板参数T表示值的类型。在函数模板中,我们可以使用T替代任
5.2区别
类模板是一种模板类,它可以用来生成多个具有相同结构但数据类型不同的类。类模板定义以template关键字开头,后面跟上模板参数列表,然后是普通类的定义。在实例化过程中,需要明确指定模板参数的具体类型。
函数模板是一种模板函数,它可以用来生成多个具有相同代码但参数类型不同的函数。函数模板定义以template关键字开头,后面跟上模板参数列表,然后是函数的定义。在调用过程中,需要明确指定函数参数的具体类型。
- 注意事项
(1)类模板中可以包含成员函数和成员变量,而函数模板只能包含函数。
(2)类模板中的模板参数可以是任意类型,包括类类型、原始类型和指针类型等,而函数模板的模板参数只能是类型。
(3)类模板的实例化是在编译时进行的,因此它可以提供更好的性能和类型安全。函数模板的实例化是在运行时进行的,因此它的性能可能会受到影响。
(4)类模板可以有多个模板参数,而函数模板只能有一个模板参数。
(5)类模板可以具有默认模板参数,而函数模板不能具有默认模板参数。
(6)类模板中的数据成员和成员函数可以使用模板参数类型,也可以使用类模板中定义的类型参数。而函数模板中的参数和返回值必须使用模板参数类型。
总之,类模板和函数模板都是C++中实现通用编程的