引言:
北京时间:2023/2/13/18:29,开学正式上课第一天,直接上午一节思想政治,下午一节思想政治,生怕我们……,但,我深知该课的无聊,所以充分利用时间,把我的小黑书给拿出来看了,发现小黑书就是小黑书,那么的晦涩难懂,虽然我看的非常的仔细,一个字一个字的看,一遍一遍的看,但是很多地方还是不明白,所以我希望我终有一天可以把小黑书的大部分知识搞的七七八八吧!现在,我们就再来学习一下C++中的模板问题。
再聊类模板
通过上篇博客,我们是可以搞定类模板的大致使用的,总的来说就是把类实现成无类型表示的一个模板,然后使用时以尖括号<> 来显示该类模板的类型,从而实现不同类型的类,说以明白了这些,此时我们就再来看一下类模板中的一些小知识点,如:如何实现类模板中函数的声明和定义分离,此时的函数声明和定义分离,不可以像之前的函数声明和定义分离一样,此时类模板中把函数定义在类外,不仅要明确作用域是该类,而且需要加上模板参数,这样才可以把模板中的函数进行声明和定义分离,如图所示:
非模板类的声明和定义分离写法:
Vector::~Vector()
{
delete[] _pData;
_pData = nullptr;
_size = _capacity = 0;
}
模板类的声明和定义分离写法:
template<class T>
Vector<T>::~Vector()
{
delete[] _pData;
_pData = nullptr;
_size = _capacity = 0;
}
模板类的声明和定义分离写法和非模板类声明和定义分离写法最主要的区别就是,模板类的声明和定义分离要加上模板参数,不能只指定作用域类名,但注意:模板类的声明和定义是不允许分成.h文件
和.c文件
分别声明和定义的,这样会导致链接问题,只有普通的类(也就是非模板类)可以分成两个文件进行声明和定义,模板类是不可以的;并且此时强调,delete和new的注意点:因为使用delete
时,仅仅调用了对象数组中第一个对象的析构函数,而使用delete []
,将会逐个数组元素调用析构函数,所以当我们遇到了new[]
的时候,例:_pData(new T[capacity])
,此时我们不可以使用delete _pData;
要使用delete[] _pData;
才行,所以记住 不仅 new和delete、malloc和free要匹配使用,new[]和delete[]也要匹配使用。
简易的一个顺序表类模板:
#include<iostream>
using namespace std;
template<class T>
class Vector
{
public:
Vector(size_t capacity = 10)
:_pData(new T[capacity])
,_size(0)
,_capacity(capacity)
{
}
~Vector();
void PushBack(const T& data);
void PopBack();
size_t Size()
{
return _size;
}
T& operator[](size_t pos)//把[]进行重载
{
assert(pos < _size);
return _pData[pos];
}
private:
T* _pData;
size_t _size;
size_t _capacity;
};
template<class T>
Vector<T>::~Vector()
{
delete[] _pData;
_pData = nullptr;
_size = _capacity = 0;
}
int main()
{
return 0;
}
浅浅介绍一下STL
搞定了上述的知识和之前的知识,此时我们就可以进入到C++中的另一个重点知识STL的学习了,在正式学习STL之前,我们今天就先浅浅的了解一下什么是STL、STL的重要性、STL版本、STL的六大组件、STL的缺陷和如何学习STL等知识。
什么是STL
STL就是(standard template libaray)的缩写,按照此时的名字来理解就是一个标准模板库,是C++中标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构和算法软件的框架。
STL版本
C++ STL 规范版本正式通过以后,由于其实开源的,各个 C++ 编译器厂商在此标准的基础上,实现了满足自己需求的 C++ STL 泛型库,主要包括 HP STL、SGI STL、STLport、PJ STL、Rouge Wave STL 等。
STL的六大组件
如图所示:
STL六大组件 | 具体介绍和相关作用 |
---|---|
1.容器(containers) | 各种数据结构,如: vector,list,deque,set,map用来存放数据,从实现角度来看,STL容器是一种class template。 |
2.算法(algorithms) | 各种常用算法如sort,search,copy,erase… 从实现角度看,STL算法是一种function template。 |
3.迭代器(iterators) | 扮演容器与算法之间的胶合剂,是所谓的“泛型指针”。共有5种类型,以及其它衍生变化。从实现角度来看,迭代器是一种将operator*, operator->, operator++, operator–等相关操作予以重载的class template。所有STL容器都附带有自己专属的迭代器。 |
4.仿函数(functors) | 行为类似函数,可作为算法的某种策略(policy),从实现的角度来看,仿函数是一种重载了operator()的 class 或 class template。一般函数指针可视为狭义的仿函数。 |
5.配接器(adapters) | 一种用来修饰容器(container)或仿函数(functors)或迭代器(iterators)接口的东西。STL提供的queue和stack, 虽然看似容器,其实只能算是一种容器配接器,因为它们的底部完全借助deque,所有操作都由底层的deque供应。改变functor接口者,称为function adapter;改变container接口者,称为container adapter;改变iterator接口者,称为iterator adapter。 |
6.空间配置器(allocators) | (内存池)负责空间配置与管理,从实现角度来看,空间配置器是一个实现动态空间配置,空间管理,空间释放的class template。 |
如何学习STL
第一境界:熟用STL
第二境界:了解泛型技术的内涵与STL的学理乃至实作
第三境界:扩充STL
正式学习STL
从string看STL
搞懂了上述有关STL的知识,此时我们就来学习一下STL中的string类,还是秉承着改善C语言的目标,此时string类出现的目的就是为了解决C语言中有关字符的库函数比较分散,不方便使用和底层空间需要用户自己管理,容易导致越界访问的问题,所以string类的出现,就可以让我们无论是在编写程序,还是做题的时候,都可以非常方便的使用有关的字符函数。
标准库中的string类
英文字母编码
在我们学习string类之前,我们可以先来回顾一下有关编码的内容,相信各位在C语言初阶的时候,都了解过计算机中的编码(ASCII码) ,所以我们就来回顾一下什么是ASCII码 ,从一个问题出发,搞懂什么是ASCII和ASCII的出现原因;我们都知道计算机只能识别由0和1组成的机器语言,并不能识别各种字符,所以此时为了可以在计算机上显示和存储各种字符,此时人们就想到用编码的形式,将各种字符和相应的数字对应起来,从而就生成了我们的ASCII码表,我们通过ASCII码值对各种字符进行使用,此时计算机就可以通过ASCII码值转化为二进制编码,从而识别我的各种字符,具体过程:字符=>ASCII(0—128)=>内存(对应的二进制)=>计算机;如下图所示,我们写了一个字符数组,此时字符数组中有6个字符"apple\0"
,切记这种写法,字符串的末尾是有"\0"
结束的,所以此时我们可以打开调式窗口中的内存选项,搜索字符数组str
的地址,此时我们可以发现,该地址有8个字节,并且是以16进制命名的,并且ASCII码值61就是字符a的16进制,70就是字符p的16进制,6c就是字符l的16进制,65就是字符e的16进制,00就是字符\0的16进制,所以此时如图可以得出结论,我们的字符串在内存中存储的就是该字符对应的16进制的ASCII码值。
汉字编码
所以我们搞懂了apple字符在计算机内存中是如何存储的,那么此时就应该想到,我们中国的汉字应该怎么存储呢?我们的汉字博大精深,比26个引文字母不知道多了多少,所 以此时应该怎样在计算机中存储我们的汉字呢? 在上述知识中,我们知道一个英文字符,只需要使用一个字节就够了,因为一个字节是8个比特位,最大值是256,足够编码所有的字符,但汉字是成千上万的,所以此时使用一个字节就不够我们进行对每一个汉字进行编码,所以一般是使用2个字节,两个字节就是2^16,是65536,足够我们对每一个汉字进行编码, 所以一个汉字在内存中最少是占两个字节的,所以此时就会涉及到一个叫万国码的东西,有这个计算机就可以存储各个国家的语言了,这样才使计算机可以在全球流行。
如该图所示,此时的下标0/1表示"你"、2/3表示"好"、4/5表示"世"、6/7表示"界"、8表示"\0",并且此时为了使该编码形式可以兼容ASCII码表,所以在前面都加上了一个负号(-),
注意:二进制编码以0开头就是一个字节编码,以1开头就是两个字节编码。
如果各对万国码感兴趣的话,可以参考该链接:万国码详解
正式学习C++中的string
话不多说,学习某一个函数和某一个类的时候,我们必须去cplusplus这个网站上看一下,该函数或者该类的具体情况,如下图:
发现,该类是封装在std中的,并且原名是basic_string
通过重命名(typedef
)变成string
,并且该类是通过basic_string
模板经过实例化得到的一个字符类型(char
)的字符串类。并且通过往下阅读,我们可以发现,string有100多个函数,此时我们不需要学习这么多,只要学习其中的20个到30个就行了,所以接下来,我们就开始string类中接口函数的学习。
走进string类内部
如下图,我们可以看出,在string类中重载了很多不同功能的函数,通过它们的参数的个数不同或者参数的类型不同,对这些函数进行重载,所以当我们使用string时,其实本质上是有很多功能可以使用的,主要与参数的类型和个数是有关的,所以现在我们如该图,一个一个的string函数开始学习,具体看一下string类中的string函数都有什么功能。
构造一个字符串对象,根据所使用的构造函数版本初始化其值:(意思就是使用函数重载的方式来构造对象)
与上图中的序号相对应,每个重载函数的功能如下表:
重载函数 | 具体功能 |
---|---|
(1) 空字符串构造函数(默认构造函数) | 构造一个空字符串,长度为零个字符。 |
(2) 复制构造函数 | 构造 str 的副本。 |
(3) 子字符串构造函数 | 复制 str 中从字符位置 pos 开始并跨越 len 字符的部分(如果 str 太短或 len 是字符串::npos,则直到 str 的末尾)。 |
(4)从C串 | 复制 s 指向的以 null 结尾的字符序列(C 字符串)。 |
(5) 从缓冲液 | 从 s 所指的字符数组中复制前 n 个字符。 |
(6) 填充构造函数 | 用字符 c 的 n 个连续副本填充字符串。 |
(7) 范围构造函数 | 以相同的顺序复制区域 [第一个、最后一个) 中的字符序列。 |
(8) 初始值设定项列表 | 以相同的顺序复制 il 中的每个字符。 |
(9) 移动构造函数 | 获取 str 的内容。str 处于未指定但有效的状态。 |
搞定了string函数,此时我们接着去看一看string类中的有关容量的函数。
例如:此时容量中的函数,计算字符串的长度的函数
如图计算字符串长度的函数有两个,一个是size
一个是length
,两个都可以计算,那么有什么区别呢?
主要的区别就是不同的数据结构类型,在使用上是有所不同的。(我们平时使用size就行了)
所以以上就是所有的有关容量的string类总的成员函数了,下述为这些成员函数的具体使用功能:
函数名 | 具体功能 |
---|---|
大小 | 返回字符串的长度(公共成员函数) ) |
长度 | 返回字符串的长度(公共成员函数) ) |
max_size | 返回字符串的最大大小(公共成员函数) ) |
调整 | 调整字符串大小(公共成员函数) ) |
能力 | 返回已分配存储的大小(公共成员函数) ) |
储备 | 请求更改容量(公共成员功能 ) |
清楚 | 清除字符串(公共成员函数 ) |
空 | 测试字符串是否为空(公共成员函数 ) |
shrink_to_fit | 缩小以适合(公共成员函数) ) |
string类中的+=运算符使用
了解了上述的函数,我们可以发现,这些函数都比较的鸡肋,接下来我们学习一下string类中的比较实用的函数和运算符。
可以发现我们使用+=
运算符对字符串进行增加字符和字符串是比使用push_back和append
函数更加优的,不仅可以使代码更加易懂,而且好记好写,但本质上我们都懂+=运算符
的重载,还是由push_back和append
函数来完成的,只是对push_back和append
这两个函数重新进行一定的封装而已。当然这也就是运算符重载的好处。
总结:string在STL中是非常重要的一个类,需要我们慢慢的通过不断摸索,孰能生巧的去逐渐搞定它。
转载:https://blog.csdn.net/weixin_74004489/article/details/129014857