目录
前言
1.非类型模版参数
2.模版的特化
2.1概念
2.2函数模版特化
2.3 类模板特化
2.3.1 全特化和偏特化
2.3.2类模版特化应用实例
3.模版分离编译
3.1 什么是分离编译
3.2 模板的分离编译
3.3 解决方法
4. 模板总结
结束语
前言
在模版初阶我们学习了函数模版和类模版的相关知识,对模版有了一定的了解,接下来我们将对模版进行进一步的了解,话不多说,直接上货!!!
本节内容
1. 非类型模板参数2. 类模板的特化3. 模板的分离编译
1.非类型模版参数
比如我们简单写一个静态栈,可以定义不同大小的栈,这里我们没有传默认参数,传也可以。
template<size_t N>
class Stack {
private:
int _a[N];
int _top;
};
int main() {
Stack<5>s1;
Stack<10>s2;
return 0;
}
1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的 。2. 非类型的模板参数必须在编译期就能确认结果 。
#include <array>
#include <iostream>
using namespace std;
int main(){
array<int, 10> a1;
array<int, 100> a2;
int a3[10];
return 0;
}
那么用array定义数组的好处是什么呢,是数组初始化吗,当然不是。
相比较于int a3[10],其实array的好处是越界检查方面,array对于数组的读和写都会进行越界检查,而int a3[10],对于读不会检查,而写会检查,进行的是抽取,一般会在数组后面默认分配两个数据大小空间左右。
array<int, 10> a1;
array<int, 100> a2;
// a1[100] = 3;
int a3[10];
cout << a3[100] << endl;
a3[10] = 1;
// a3[12] = 1;
array底层访问是调用了一个函数进行实现,都会调用assert语句来判断。
template<class T, size_t N = 10>
class array
{
public:
T& operator[](size_t index) {
assert(index < N);
return _array[index];
}
private:
T _array[N];
size_t _size;
};
其实对于数组的定义,在C++中我们采用vector来定义,可以进行初始化。
vector<int>v(10,1);
但是如果要频繁的开设数组,其实array的效率可能要高些,因为array是在栈上开空间的,栈是向下生长的,vector是在堆上。
2.模版的特化
2.1概念
class Date {
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year),
_month(month),
_day(day)
{}
bool operator<(const Date& other) const {
if (_year != other._year) return _year < other._year;
if (_month != other._month) return _month < other._month;
return _day < other._day;
}
/*
friend ostream&operator<<(ostream& out, const Date& d) {
out << d._year << "-" << d._month << "-" << d._day;
return out;
}*/
private:
int _year;
int _month;
int _day;
};
template <class T>
bool Less(const T &left,const T& right) {
return left < right;
}
int main() {
cout << Less(1, 2) << endl; // 可以比较,结果正确
Date d1(2024, 7, 9);
Date d2(2024, 7, 8);
cout << Less(d1, d2) << endl; // 可以比较,结果正确
Date* p1 = &d1;
Date* p2 = &d2;
cout << Less(p1, p2) << endl; // 可以比较,结果错误
return 0;
}
2.2函数模版特化
template <class T>
bool Less(const T &left,const T& right) {
return left < right;
}
template<>
bool Less<Date*>(Date* const& left, Date* const& right)
{
return *left < *right;
}
/*
template<>
bool Less<const Date*>(const Date* const& left, const Date* const& right)
{
return *left < *right;
}
*/
注意:const要放在*之后,修饰的才是引用变量本身,放在*之前是修饰指向内容
非const特化
template <class T>
bool Less( T &left,T& right) {
return left < right;
}
template<>
bool Less<Date*>(Date* &left, Date* &right)
{
return *left < *right;
}
所以对于下面这段代码
Date * p1 = & d1 ;Date * p2 = & d2 ;cout << Less ( p1 , p2 ) << endl ; // 调用特化之后的版本,而不走模板生成了
bool Less(Date* left, Date* right)
{
return *left < *right;
}
2.3 类模板特化
2.3.1 全特化和偏特化
template<class T1, class T2>
class Data
{
public:
Data() { cout << "Data<T1, T2>" << endl; }
private:
T1 _d1;
T2 _d2;
};
//全特化
template<>
class Data<int, char>
{
public:
Data() { cout << "Data<int, char>" << endl; }
private:
int _d1;
char _d2;
};
//偏特化
template<class T1>
class Data<T1, int> {
public:
Data() { cout << "Data<T1, int>" << endl; }
private:
T1 _d1;
int _d2;
};
int main() {
Data<int, int>d1;
Data<int, char>d2;
Data<int, int> d3;
return 0;
}
偏特化有以下两种表现方式:
部分特化
// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:
Data() {cout<<"Data<T1, int>" <<endl;}
private:
T1 _d1;
int _d2;
};
/两个参数偏特化指针类型
//template<class T1,class T2>
template<typename T1, typename T2>
class Data<T1*, T2*>
{
public:
Data() { cout << "Data<T1*, T2*>" << endl; }
private:
T1 _d1;
T2 _d2;
};
//两个参数特化引用类型
template<class T1, class T2>
class Data<T1&, T2&>
{
public:
Data() { cout << "Data<T1&, T2&>" << endl; }
private:
T1 _d1;
T2 _d2;
};
//一个指针一个引用
template<class T1, class T2>
class Data<T1&, T2*>
{
public:
Data() { cout << "Data<T1&, T2*>" << endl; }
private:
T1 _d1;
T2 _d2;
};
2.3.2类模版特化应用实例
实例1:
class Date {
public:
Date(int year = 1, 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& out, const Date& d) {
out << d._year << "-" << d._month << "-" << d._day;
return out;
}
private:
int _year;
int _month;
int _day;
};
/*
template <class T>
bool Less(const T &left,const T& right) {
return left < right;
}
*/
template<class T>
struct Less
{
bool operator()(const T& x, const T& y) const
{
return x < y;
}
};
int main() {
vector<Date>d1;
d1.push_back(Date(2024, 8, 25));
d1.push_back(Date(2024, 8, 24));
d1.push_back(Date(2024, 8, 27));
sort(d1.begin(), d1.end(), Less<Date>());
for (const auto& date : d1) {
cout << date << endl;
}
return 0;
}
实例2:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Date {
public:
Date(int year = 1, 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& out, const Date& d) {
out << d._year << "-" << d._month << "-" << d._day;
return out;
}
private:
int _year;
int _month;
int _day;
};
template<class T>
class Less
{
public:
bool operator()(const T& x, const T& y) const
{
return x < y;
}
};
// 对Less类模板按照指针方式特化
/*
template <class T>
bool Less(const T &left,const T& right) {
return left < right;
}
*/
int main()
{
Date d1(2022, 7, 7);
Date d2(2022, 7, 6);
Date d3(2022, 7, 8);
vector<Date> v1;
v1.push_back(d1);
v1.push_back(d2);
v1.push_back(d3);
// 可以直接排序,结果是日期升序
sort(v1.begin(), v1.end(), Less<Date>());
for (const auto& date : v1) {
cout << date << endl;
}
vector<Date*> v2;
v2.push_back(&d1);
v2.push_back(&d2);
v2.push_back(&d3);
// 可以直接排序,结果错误日期还不是升序,而v2中放的地址是升序
// 此处需要在排序过程中,让sort比较v2中存放地址指向的日期对象
// 但是走Less模板,sort在排序时实际比较的是v2中指针的地址,因此无法达到预期
sort(v2.begin(), v2.end(), Less<Date*>());
for (const auto& date : v2) {
cout << *date << endl;
}
return 0;
}
// 对Less类模板按照指针方式特化
template<>
struct Less<Date*>
{
bool operator()(Date* x, Date* y) const
{
return *x < *y;
}
};
3.模版分离编译
3.1 什么是分离编译
3.2 模板的分离编译
// a.h
template<class T>
T Add(const T& left, const T& right);
// a.cpp
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
// main.cpp
#include"a.h"
int main()
{
Add(1, 2);
Add(1.0, 2.0);
return 0;
}
C/C++程序的运行过程通常包括以下几个步骤:
1. 预处理(Preprocessing):
- 预处理器处理源代码文件中的预处理指令,如 `#include`、`#define`、`#if`、`#ifdef` 等。
- 预处理器将包含的文件内容插入到源文件中,处理宏定义,条件编译指令等。
- 结果是一个扩展的源代码文件,通常以 `.i` 文件(C语言)或 `.ii` 文件(C++语言)表示。
2. 编译(Compilation):
- 编译器将预处理后的源代码翻译成汇编语言。
- 在这个阶段,编译器进行词法分析、语法分析、语义分析、中间代码生成和优化等操作。
- 生成的汇编语言文件通常以 `.s` 文件表示。
3. 汇编(Assembly):
- 汇编器将汇编语言转换成机器语言指令,这些指令是二进制形式的,可以被计算机的CPU直接执行。
- 生成的目标代码文件通常以 `.o`(Linux/Unix系统)或 `.obj`(Windows系统)表示。
4. 链接(Linking):
- 链接器将一个或多个目标文件以及所需的库文件合并成一个可执行文件。
- 在这个阶段,链接器解析外部引用和符号,合并相同的函数和数据,进行重定位等操作。
- 最终生成的可执行文件通常以 `.exe`(Windows系统)或无扩展名(Linux/Unix系统)表示。
5. 加载(Loading):
- 当程序运行时,操作系统负责将可执行文件加载到内存中。
- 加载器读取可执行文件,并将程序代码和数据映射到内存的适当位置。
6. 执行(Execution):
- CPU开始执行程序的主函数,程序正式开始运行。
- 在执行过程中,程序可能会进行各种计算、输入输出操作、调用库函数等。
7. 终止(Termination):
- 程序执行完成后,或者遇到无法处理的错误时,程序会终止。
- 在终止前,程序可能需要执行一些清理工作,如关闭打开的文件、释放分配的内存等。
链接之前各文件没有交互,模版没有实例化没有生成指令,代码,链接就出现问题。
3.3 解决方法
显示实例化:
template
int Add(const int&left,const int&right)
对于其他类型照样,所以比较复杂
4. 模板总结
结束语
本篇就到此结束啦,内容有点多,相信大家通过本篇博客,对模版的认识有了进一步的理解。
最后感谢各位友友的支持,支持小编的留个赞吧!!!