目录

C模板进阶

C+模板进阶


一、非类型模板参数

#define N 10;
template<class T>
class Stack
{
private:
	T _a[N];
};
Stack<int> st1;//10
Stack<int> st2;//100

你看这里N已经define写死了,但是我想让st1和st2开辟不同大小的空间怎么办,

#define N 10000;

像我们这里把N扩大到10000,但是我们又用不了这么多空间,这还会造成资源浪费,所以C++引入了一个叫非类型模板参数

template<class T,size_t N>
class Stack
{
private:
	T _a[N];
};
	Stack<int,10> st1;//10
	Stack<int,100> st2;//100

这是一个常量,常量才能控制数组的大小,像我们输入值这种都是不行的

像如果我们是变量的时候,编译器在编译的时候要实例化,实例化我们又不知道实例化出多少,数组我们也不知到去开多大

像char,int,short这些整型家族的才可以

函数模板的特化

步骤

1.必须要先有一个基础的函数模板

2.关键字template后面接一对空的尖括号<>

3.函数名后跟一对尖括号,尖括号中指定需要特化的类型

4.函数形参表:必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误

二、类模板的特化

模板特化:针对某些类型进行特殊化处理

template<class T1, class T2>
class Data
{
public:
	Data()
	{
		cout << "Data<T1,T2>" << endl;
	}
private:
	T1 d1;
	T2 d2;
};
template<>
class Data<int,double>
{
public:
	Data()
	{
		cout << "Data<int,double>" << endl;
	}
};
Data<int, int> d1;
Data<int, double> d2;

我们把下面一个类叫作上面一个类的特化

是int,double走下面这个最匹配的,其他的走上面那个

https://i-blog.csdnimg.cn/direct/953bc8d6676b4ecb8772f3c875391380.png

这个特化叫作 全特化

还有一个叫 偏特化 ,特化部分

template<class T1>
class Data<T1,int>
{
public:
	Data()
	{
		cout << "Data<T1,int>" << endl;
	}

};

还可以弄指针

template<class T1, class T2>
class Data<T1*, T2*>
{
public:
	Data()
	{
		cout << "Data<T1*, T2*>" << endl;
	}

};

注意:有最匹配的会找最匹配的

那么函数模板特化呢,这里我复用一下上一篇文章的代码

class Date
{
public:
    Date(int year = 1900, int month = 1, int day = 1)
        : _year(year)
        , _month(month)
        , _day(day)
    {}

    bool operator<(const Date& d) const
    {
        return (_year < d._year) ||
            (_year == d._year && _month < d._month) ||
            (_year == d._year && _month == d._month && _day < d._day);
    }

    bool operator>(const Date& d) const
    {
        return (_year > d._year) ||
            (_year == d._year && _month > d._month) ||
            (_year == d._year && _month == d._month && _day > d._day);
    }

    friend ostream& operator<<(ostream& _cout, const Date& d);
private:
    int _year;
    int _month;
    int _day;
};

ostream& operator<<(ostream& _cout, const Date& d)
{
    _cout << d._year << "-" << d._month << "-" << d._day;
    return _cout;
}
template <class T>
bool Less(T x, T y)
{
		return x < y;
}
template <>
bool Less<Date*>(Date*  x, Date* y)
{
	return *x < *y;
};
int main()
{
	Date* a1 = new Date(2025, 3, 9);
	Date* a2 = new Date(2025, 3, 10);
	cout << Less(a1,a2) << endl;
	return 0;
}

这样就是函数的模板特化了

或者我们也不用写这个写一个函数的重载就行了

三、模板的分离编译

#include <iostream>
using namespace std;
template<class T>
T Add(const T& left, const T& right);

https://i-blog.csdnimg.cn/direct/dad77086bf9c4341ae86608cc5360265.png

#include "stack.h"
template<class T>
T Add(const T& left, const T& right)
{
	cout << "T Add(const T& left, const T& right)" << endl;
	return left + right;
}
#include "stack.h"
template<class T>
T Add(const T& left, const T& right)
{
	cout << "T Add(const T& left, const T& right)" << endl;
	return left + right;
}
#include "stack.h"
int main()
{
	Add(1, 2);
}

https://i-blog.csdnimg.cn/direct/97856e91e4d848a79e0b6edfff6afd28.png

这样的声明和定义分离就会报所谓的链接错误

好让我们分析一下其中的原因

编译器运行代码一般分为四个阶段

1.预处理 像是头文件的展开/宏替换….

2.编译 检查语法生成汇编代码

3.汇编 汇编代码转成二进制机器码

4.链接

链接的时候它就需要去call这个地址,跳到这个地址,但是在.cpp文件中,只包含了stack.h的头文件,相当于只包含了声明,而没有给你定义,就是相当于你买房子给你你图纸没有给你房子,然而给了你声明就让你去其他文件去找地址

https://i-blog.csdnimg.cn/direct/5bb7cc62681340f5942d4342fd0112de.png

但是我们会发现Add找不到,因为stack.cpp因为Add没有实例化,没有Add的地址

我template.cpp知道要实例化成什么,但是没有定义

就相当于一种沟通不畅,template有图纸有需求就是没有房子,stack.cpp有房子有图纸就是有毛坯房就是不知道装修成什么样子装修在哪,在两个文件中,倘若我stack.cpp实例化了,那就有了具体的地址,我template手上有声明了就知道到哪里去找它了

解决方案1

显示实例化

template
int Add<int>(const int& left, const int& right);

这是一种解决方案,但是有一种弊端

Add(1.1, 2.2);

https://i-blog.csdnimg.cn/direct/3f54ec3ccb714fac82f38b7b63e93a35.png

显示实例化这种方法可行但是不好用换一种类型又要写一种了;

我们连类一起看一下

这是stack.h里面的

template<class T>
class stack
{
public:
	void push(const T& x);
private:
	T* _a;
	int _top;
	int _capacity;
}

这是stack.cpp里面的

template<class T>
void stack<T>::push(const T& x)
{
	cout << "void push(const T& x)" << endl;
}
stack<int> st;
st.push(1);

也是一样的结果

template
class stack<int>;

也是不好用的

解决方案二

不分离

我们直接把stack.cpp删掉,把声明和定义全都放到一起

https://i-blog.csdnimg.cn/direct/891be3b412ce4fb8baebd40631cbf0e2.png

声明和定义全都放在一起,我stack.h在编译的时候一开,直接就能实例化了

四、模板总结

优点:模板复用了代码,节省资源,更快的迭代开发

增强了代码的灵活性

缺陷

模板会导致代码膨胀问题,也会导致编译时间变长

出现模板编译错误时,错误信息非常凌乱,不易定位错误

模板进阶就到这里结束了接下来进入继承,写的不好的地方欢迎大家指出