当前位置: 首页 > news >正文

十八、array 类

Ⅰ . 非类型模板参数

01 什么是非类型模板参数?

STL 中的 array 就有一个非类型模板参数

注意看,我们普通定义的 T 是类型,而 N 这里并不是类型,而是一个常量

类型模板参数定义的是虚拟类型,注重的是你要传什么,而非类型模板参数定义的是变量

 

               "非类型模板参数"👇
template<class T, size_t N> class array;👆    "类型模板参数"

02 非类型模板参数的使用场景

假设我们要定义一个静态栈:

#define N 100template<class T> 
class Stack 
{private:int _arr[N];int _top;
};

那么我们如果想定义两个容量不同的栈,可以做到嘛?

这里无论 #define 改成 100 还是 500 都无法解决这里的问题

这里只能使用非类型模板参数来解决

代码实现:

template<class T, size_t N>
class Stack {private:int _arr[N];int _top;
};int main() 
{Stack<int, 100> st1;     Stack<double, 500> st2;  return 0;
}

这里我们在模板这定义一个常量 N,于是我们就可以在实例化时去指定其实例化对象的大小了

这个 N 就是非类型模板参数

03 非类型模板参数不能修改

非类型模板参数是常量,是不能被修改的

template<class T, size_t N> 
class Stack {public:void f() {    // 修改常量试试看N = 10; }private:int _arr[N];int _top;
};int main()
{Stack<int, 100> st1;st1.f();return 0;
}

运行结果如下:

test1711.cpp:10:15: error: lvalue required as left operand of assignment
             N = 10;

04 非类型模板参数类型规定

有些类型是不能作为非类型模板参数的,比如浮点数、类对象和字符串

非类型模板参数基本上都是整型

05 STL 中的 array

文档介绍:array - C++ Reference

我们现在再来看 array:

array 是 C++ 11 新增的,那么它有什么特别的地方嘛?

很可惜,基本没有

#include <iostream>
#include <array>
#include <vector>
using namespace std;int main()
{vector<int> v1(100, 0);array<int, 100> a1;cout << "size of v1: " << sizeof(v1) << endl;cout << "size of a1: " << sizeof(a1) << endl;return 0;
}

运行结果如下:

vector 是开在堆上,而 array 是开在堆上

尴尬的是 array 能做的操作 vector 几乎都能做,array 也只是封装过的原生数组罢了

array<int, 100> a1;  // 封装过的原生数组
int a2[100];         // 原生数组

比起原生数组,array 的最大优势也只是越界的检擦,读和写都可以检查到是否越界

总结:array 相较于原生数组,有越界检查,实际中还是建议直接用 vector

Ⅱ . 模板的特化

01 给特殊类型准备特殊模板

通常情况下,使用模板可以实现一些与类型无关的代码

但对于一些特殊类型,我们需要一些特殊的处理

代码演示:

#include <iostream>
#include"Date.h"
using namespace std;template<class T>
bool Less(T left, T right)
{return left < right;
}int main()
{cout << Less(1, 2) << endl;Date d1(2024, 8, 28);Date d2(2024, 8, 29);cout << Less(d1, d2) << endl;Date* d3 = new Date(2024, 1, 1);Date* d4 = new Date(2024, 1, 2);cout << Less(d3, d4) << endl;return 0;
}

运行结果如下:每次运行的结果都不一样

问题出在没传指针,传 *d3 和 *d4 就能解决

但如果不让你传指针怎么办呢?

这里就需要用到模板的特化,针对特定的类型做特殊化处理

02 模板特化的步骤

首先,需要有一个基础的函数模板

其次,关键字 template 后面接上一堆空的尖括号 <>

然后,函数名后跟上一对尖括号,尖括号中指定需要特化的内容

最后,函数形参表必须要和模板函数的基础参数类型完全相同

代码实现:

template<class T>
bool Less(T left, T right)
{return left < right;
}template<>
bool Less<Date*>(Date* left, Date* right)
{return *left < *right;
}int main()
{cout << Less(1, 2) << endl;Date d1(2024, 8, 28);Date d2(2024, 8, 29);cout << Less(d1, d2) << endl;Date* d3 = new Date(2024, 1, 1);Date* d4 = new Date(2024, 1, 2);cout << Less(d3, d4) << endl;return 0;
}

运行结果如下:

对于普通类型,它还是会调用正常的模板,对于 Date* 编译器就会发现这里有一个专门为它准备的特化版本,编译器会优先选择该特化版本,这就是模板的特化

那如果我们直接加一个普通函数,会调用哪个呢?

template<class T>
bool Less(T left, T right)
{return left < right;
}// 特化
template<>
bool Less<Date*>(Date* left, Date* right)
{return *left < *right;
}// 普通
bool Less(Date* left, Date* right)
{return *left < *right;
}

函数重载,会直接调用普通函数的版本,因为是现成的,不需要实例化

03 类模板的特化

 刚才的函数模板不一定要特化,可以写一个具体的函数

但类模板没法实现一个具体的实际类型,就必须要特化

template<class T1, class T2>
class Date
{
public:Date(){cout << "Date<T1 ,T2>" << endl;}
private:T1 _d1;T2 _d2;
};int main()
{Date<int, int> d1;Date<int, double> d2;return 0;
}

这种情况就需要类模板的特化

代码实现:

template<class T1, class T2>
class Date
{
public:Date(){cout << "Date<T1 ,T2>" << endl;}
private:T1 _d1;T2 _d2;
};// 类模板的特化
template<>
class Date<int, double>
{
public:Date(){cout << "Date<int, double>" << endl;}
};int main()
{Date<int, int> d1;Date<int, double> d2;return 0;
}

运行结果如下:

 04 全特化和半特化

全特化:将模板参数列表中的所有参数全都确定化

...
// 全特化
template<>
class Data<int, double> 
{
public:Data() {cout << "Data<int, double>" << endl;}
};

半特化(偏特化):将部分参数列表中的一部分参数特化

...
// 半特化(偏特化)
template<class T1>
class Data<T1, char> 
{
public:Data() {cout << "Data<T1, char>" << endl;}
};int main()
{// 只要第二个值是 char 都会匹配到半特化Data<int, char> d3;Data<char, char> d4;return 0;
}

半特化还可以用来对参数进行进一步限制

template<class T1, class T2>
class Date<T1*, T2*>
{
public:Date(){cout << "Date<T1*, T2*>" << endl;}
};int main()
{Date<int*, char*> d3;Date<char*, string*> d4;Date<char**, void*> d5;return 0;
}

运行结果如下:

template<class T1, class T2>
class Date<T1&, T2&>
{
public:Date(){cout << "Date<T1&, T2&>" << endl;}
};int main()
{Date<int&, char&> d6;return 0;
}

运行结果如下:

Ⅲ . 模板的优缺点

优点:

① 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生。

② 增强了代码的灵活性。

缺点:

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

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

具体分析:

优点:

泛型编程:模板允许你编写通用代码,可以适用于多种数据类型,而不仅仅是特定类型。这使得代码更具通用性和重用性。

类型安全:C++模板系统提供了强类型检查,这有助于捕捉在编译时发生的类型错误,而不是在运行时。

性能:模板生成的代码通常比使用宏或运行时多态的方式更高效。编译器可以生成针对具体类型的优化代码,从而提高性能。

容器和算法库:STL(Standard Template Library)使用了C++模板,提供了丰富的容器和算法,使开发者能够更轻松地使用和操作数据结构。

可扩展性:模板允许你创建自定义数据类型和函数,从而增强C++的可扩展性。

缺点:

编译时间:使用模板可能导致较长的编译时间,因为编译器需要为每个具体的模板实例生成代码。对于大型项目,这可能会导致显著的编译时间增加。

复杂性:模板语法相对复杂,可能对初学者不够友好。编写和维护模板代码需要一定的经验。

错误消息:当涉及到模板的错误发生时,编译器生成的错误消息通常比较晦涩难懂,这增加了调试的难度。

二进制兼容性:在C++中,由于模板的实现方式,对于不同编译器版本、不同编译选项或不同平台之间的二进制兼容性可能存在问题。

代码膨胀:每个不同的模板实例都会生成新的代码,这可能会导致代码膨胀,增加可执行文件的大小。

代码膨胀

代码膨胀(Code bloat)是指代码有着不必要的长度、缓慢或者其他浪费资源的情况。代码膨胀可能是由于编写代码的语言、编译时所用的编译器,或者编写的程序员所致。因此,虽然代码膨胀通常指源代码存在不必要的部分(由程序员导致),但也可指生成的代码或者二进制文件文件有膨胀问题。

在编程中使用了大量的模板、宏或泛型编程技术,导致生成的代码变得冗长、复杂和庞大的现象。这种情况通常出现在C++等编程语言中,其中模板和泛型编程被广泛应用。代码膨胀的主要原因是为了实现通用性和灵活性,程序员使用了大量的模板和泛型类型,以满足各种不同的数据类型和需求。代码膨胀可能会导致以下问题:

编译时间变长:生成大量的冗长代码需要更多的时间来进行编译。编译器需要处理更多的代码,这可能会导致编译时间明显增加,尤其是在大型项目中。

可维护性下降:庞大的代码库更难以维护和理解,因为其中可能包含了大量看似相似但实际上略有不同的代码片段。这会增加错误的引入和修复的难度,降低代码的可维护性。

错误信息混乱:当代码膨胀时,编译器生成的错误信息可能会变得非常混乱,不容易理解。这使得在出现编译错误时很难迅速定位和解决问题,增加了调试的复杂性。

为了避免代码膨胀,程序员可以谨慎使用模板和泛型编程技术,只在必要的情况下使用它们,而不是过度使用。此外,编译器优化和代码重构也可以帮助减少代码膨胀的问题。


http://www.mrgr.cn/news/13132.html

相关文章:

  • 更改etcd默认存储2G限制
  • Linux下单网卡配置多个路由ip方法
  • linux dma cache和主存数据不一致问题
  • chapter08-面向对象编程——(chapter08作业)——day10
  • 鹭鹰优化算法SBOA优化RBF神经网络的扩散速度实现多数入多输出数据预测,可以更改数据集(MATLAB代码)
  • hive客户端
  • 【Leetcode 2103 】 环和杆 —— 二维数组的应用
  • MATLAB 快速计算点到二维直线的距离并可视化(79)
  • C++ 文件操作
  • 国产游戏行业的技术突破与未来展望:挑战与机遇并存
  • 数据结构——二叉树经典OJ题
  • 搭建FTP服务器,通过浏览器访问FTP服务器,测试终端上传的音频文件。
  • #网络编程 笔记
  • 《第二十章 字符串处理 - 正则表达式》
  • 通过小程序进度条了解Linux下的多文件操作
  • 力扣网页端无法进入(问题已解决)
  • py 可视化图层
  • 国货之光|暴雨信创服务器亮相北京科博会
  • SpringBootFFmpeg实现M3U8切片转码播放(本地)
  • html2canvas ios慎用和createImageBitmap ios慎用