C++模板(泛型编程)
✨Blog:🥰不会敲代码的小张:)🥰
🉑推荐专栏:C语言🤪、Cpp😶🌫️、数据结构💀
💽座右铭:“記住,每一天都是一個新的開始😁😁😁”
💀本章内容:《C++模板》的介绍✨
模板介绍
C++中的模板是一种通用编程工具,允许编写泛型代码,以便在不同类型的数据上工作而不需要重复编写多个版本的代码。
C++中有两种主要类型的模板:
函数模板
类模板
日常使用class和typename没有区别。
函数模板
模板参数定义的是类型
template <typename Tp>
函数模板:
template <typename T>
void Swap(T& x, T& y)
{
T tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 1;
int b = 2;
double c = 1.1;
double d = 2.2;
Swap(a, b);
//Swap(c, d);
return 0;
}
我们可以传任何类型给Swap函数,因为它已经不是普通的函数,如果我们不写模板,那么我们就需要指定类型。有了模板后,这些事情编译器就会替我们解决,如果需要int类型的,编译器会实例化int类型的,如果需要double类型,同样也会去实例化double类型…看似调用的是同一个函数,实则不同类型的调用,底层已经变了。
调用的不是同一个函数,函数模板实例化生成具体函数。
根据调用,自己推到模板参数类型,实例化出对应的函数。
有些函数无法自动推类型,只能显示实例化:
这种场景只能显示实例化:
类模板
模板实例化类的需求:
普通类:类名和类型是一样的
Stack s;
类模板:类名和类型不一样
类名:Stack
类型:Stack<T>
类只能显示实例化:
类模板实例化不同的模板参数就是不同的类型!!!!!!!!!
模板进阶
- 模板可以控制容器里面的数据问题
- 可以控制逻辑,比如说适配器
typename和class的区别
如果不再container前面加上typename那么编译器会分不清它到底是类型还是对象,加上typename的意思就是告诉编译器,这是个类型,具体什么类型还不知道,因为还没有实例化。需要继续让编译器往下去走回过头来才能确定类型,因为编译器是自顶向下编译的,在编译的时候还没走到具体类型,就遇到了不知道是对象还是类型的语句,所以加上typename是告诉编译器是类型。
#include <iostream>
#include <string>
using namespace std;
template<class Container>
void Print(const Container& x)
{
//typename Container::const_iterator cit = x.begin();
Container::const_iterator cit = x.begin();//报错
while (cit != x.end())
{
cout << *cit << " ";
++cit;
}
cout << endl;
}
int main()
{
string s;
s = "hello world";
Print(s);
return 0;
}
总结:如果类模板要去类里面取东西,但类型还没有被实例化,就要使用typename
非类型模板参数
1.常量
2.必须是整形
假设我们想定义一个静态的栈,大小是100,然后又需要一个栈大小是10。下面这种情况我们只能写死,但是对于s2来说100太大了。
#define Size 100
template<class T>
class Stack
{
private:
T _a[Size];
int _top;
};
int main()
{
//需要100的大小
Stack<int> s1;
//需要10的大小
Stack<int> s2;
return 0;
}
非类型模板参数就可以很好的解决这里的问题
template<class T, size_t N>
class Stack
{
private:
T _a[N];
int _top;
};
int main()
{
//需要100的大小
Stack<int, 100> s1;
//需要10的大小
Stack<int, 10> s2;
return 0;
}
模板特化(特殊化处理)
// 通用的模板类 Array
template <typename T>
class Array {
public:
void print() {
std::cout << "Generic Array" << std::endl;
}
};
// 针对 int 类型的特化
template <>//注意格式
class Array<int> {
public:
void print() {
std::cout << "Specialized Array for int" << std::endl;
}
};
int main() {
Array<double> arr1;
Array<int> arr2;
arr1.print(); // 输出: Generic Array
arr2.print(); // 输出: Specialized Array for int
return 0;
}
全特化:模板参数全部特殊化处理
全特化是对模板中所有的参数进行特化的情况。也就是说,所有的模板参数都被明确指定了。
它通常用于为特定组合的模板参数提供定制化的实现。
全特化的模板声明和定义都要提供,而不是使用通用模板。
// 通用模板类
template <typename T, typename U>
class MyClass
{
public:
void print()
{
std::cout << "Generic MyClass" << std::endl;
}
};
// 全特化为 int, double 类型
template <>
class MyClass<int, double>
{
public:
void print()
{
std::cout << "Specialized MyClass for int, double" << std::endl;
}
};
int main()
{
MyClass<int, int> x1;
MyClass<int, double> x2;
x1.print();
x2.print();
return 0;
}
偏特化:特化部分参数/可能对某些类型进一步限制
// 通用模板类
template <typename T, typename U>
class MyClass
{
public:
void print()
{
std::cout << "Generic MyClass" << std::endl;
}
};
// 偏特化为 T 类型为 int 的情况
template <typename U>
class MyClass<int, U>
{
public:
void print()
{
std::cout << "Partial Specialization for int" << std::endl;
}
};
模板分离编译
Myclass.h文件
#include <iostream>
using namespace std;
template<class T>
class MyClass
{
public:
void print();
};
Myclass.cpp文件
#include "stack.h"
template<class T>
void MyClass<T>::print()
{
cout << "hello world" << endl;
}
test.cpp文件
int main()
{
MyClass<int> m;
m.print();
return 0;
}
报错结果:
一般这种报错都是编译链接时候的错误,为什么呢?
涉及模板声明定义分离,在链接过程找不到定义地址,因为模板没有实例化,所以编译器不知道如何生成地址
解决方法:
显示实例化,但是这种方法治标不治本。打个比方来说,那我如果需要实例化double类型呢?char类型呢?难不成都要手动去实例化?
如果非要声明和定义分离,最好在同一个文件进行,这样就能有效的解决问题
总结:
优点:
- 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库STL因此而产生。
- 增强了代码的灵活性。
缺点: - 模板会导致代码膨胀(类模板实例化不同的模板参数就是不同的类型,编译器会自己生成,只是我们看不见),也会导致编译时间变长。
- 出现模板编译报错时,错误信息凌乱,不容易定位错误。
原文地址:https://blog.csdn.net/YISHENG1s/article/details/136335106
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!