C++入门第六篇---STL模板---string【上】string模板的介绍(上)

简介: C++入门第六篇---STL模板---string【上】string模板的介绍(上)

前言:

从这里开始,有了C++类和对象的铺垫,我们能够引入C++最为重要的一个工具–STL模板库,在STL模板库中,我们得以将许多C语言的语法的局限性完全打开,尤其是繁杂的字符串操作和对于自定义类型的处理,那么接下来,就让我们熟练STL模板的使用和模拟实现。

模板:关键字:template

何为模板?想象一下我们去超市购买的雪糕,从甜筒机里我们可以按照我们想要吃的得到各种口味的相同形状的甜筒雪糕,在这里,不同口味不同材料的冰激淋被一个统一的模板构造成相同的形状然后返回给我们,我们所关注的是最后我们冰激淋的样子,这便是一个面向对象的过程。而在C++中的模板也是如此:模板代表了一种通用的程序执行逻辑,不同类型的数据在模板里都会进行相同的操作,根据你传入的数据类型进行实际的调整,从而得到我们想要的结果。

1.模板的常见书写形式:

template<class T1(或者是typename T),class T2,class T3…>(注意,句尾没有分号,这说明模板不是完整的语句),这里的T不仅仅可以用T,用其他的字符也可以

1.在尖括号里面,我们可以定义一个或者多个模板变量,根据自己实际的需求去定义,比如你需要两个不同数据类型的数据相加或者比较大小。

2.模板语句之后必须只跟要使用模板的模板函数或者模板类,中间不要穿插其他的语句或者变量,这是因为模板不是语句它不是独立的,必须直接跟使用它的场景,否则一旦中间有分号,模板自动跟这个分号的语句识别为一组,下面的模板就会报错没有意义了

3.注意,与函数不同,模板里面传的是类型,而函数里面传的是变量,这个别搞错了

2.模板的分类:

模板根据其使用的场景,我们将其分成两类:

1.第一类为函数模板

2.第二类为类模板

下面我们分别介绍一下两种模板:

1.函数模板:

函数模板,即用于函数使用的模板类型,主要用于对函数体的程序进行模板的使用,大致的用法就是:

**模板声明后下一行直接跟函数定义即可。**如下:

template<class T>
T add(T x,T y)
{
  return x+y;
}

也可以这样:

template<class T1,class T2>
T2 add(T1 x,T2 y)
{
   return x+y;
}

这样,就可以分别传两种不同的数据类型的数据,但可能涉及到隐式转换的问题,所以我们最好不要这样,倘若非要这样,最好强转其中一个数据,比如在这里要返回int类型,倘若我传入的数据是5,5.5,那我就把强转为int类型,这样就不会出现结果的问题。

但是,倘若100个参数,我就得一个一个的去强转,那样太麻烦了,故我们可以这样:

template<class T>
T add(T x,T y)
{
  return x+y;
}
int main()
{
    cout<<add<int>(5,5.5)<<endl;
}

在这里,我们显式实例化函数模板,让对应的T变成固定的int类型,不管传入什么数据,都会被当成int类型处理这样,就可以避免数据类型不同产生的报错

故在这里,我们用两种方式来针对不同类型的参数的传入:

1.定义多个模板变量,然后分别占位
2.使用显式实例化,将所有的模板变量手动全部按照<>里面的数据类型处理

如下:

template<class T1,class T2>
 int add(T1 x, T2 y)
{
  return x + y;
}
int main()
{
  cout<<add<int>(1, 2.2);
  return 0;
 }
1.实例化:

何为实例化,首先我们要清楚,模板和类一样,本质上是一个蓝图,并不是实际占用空间的程序,而通过蓝图使变量被创建并使用的过程叫做实例化。

常见的实例化分为两种:

1.隐式实例化:即不是用户自己去定义的,而是计算机通过传入函数的参数推导出来的数据的类型并且对应使用的叫做隐式实例化
2.显式实例化:用户直接给模板规定的实例化的参数是什么,而不是计算机自己推出来的,一般在对应的位置加上<>里面写显式的变量类型,如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错

不管是函数,还是类,他们的实例化都是如此。

2.模板参数的匹配原则:

同名字和模板函数和普通的函数可以同时存在么?

由我们学到的函数重载的知识可知,T和int在编译器中会被标上特殊的符号,从而被识别为不同的函数,所以他们是可以同时存在的

让我们考虑下面的情况:

int Add(int left, int right) 
{
  return left + right; 
}
 template<class T> T Add(T left, T right) 
{
   return left + right; 
}
void Test()
{
 Add(1, 2);
}

针对上面的情况,同时存在模板Add函数和普通的全局Add函数,他们是可以同时存在的,那我这里的Add(1,2),它会进入哪个函数呢?

我们通过调试如下:

我们发现它进入了普通的函数add,这是为什么呢?

这是由于,模板由于参数的不确定性,编译器需要识别判断,但现成的函数就相当于已经显式实例化的模板,编译器自然会进入不用它自己推导的普通函数里面。

想要让他进入我们的模板函数,可以这样改:

int Add(int left, int right) 
{
  return left + right; 
}
 template<class T> T Add(T left, T right) 
{
   return left + right; 
}
void Test()
{
 Add<int>(1, 2);
}

调试如下:

我们发现,它就进入我们的模板函数,这是由于,我显式实例化了add,同时也起到了告诉编译器强制进入模板函数的意思,编译器发现反正都不需要推导,不如就进入你让我进入的那个函数,故就进入了模板函数。

故我们在这里总结模板函数的匹配原则:

1.一个非模板的函数可以和一个同名的模板函数同时存在,而且该模板函数还可以实例化这个非模板函数
2.对于非模板函数和同名的模板函数,如果其他条件都相同(包括参数个数,参数的数据类型这些,主要是看参数),那么在调用时会优先调用非模板函数而不是模板函数,但我们可以显式实例化使其强制调用模板,如果模板可以产生一个具有更好匹配的函数,那么编译器就会选择调用模板函数,这个更好匹配主要看你传入的参数类型是什么

3.模板函数不允许自动类型转换,但普通函数可以进行自动类型转换,即哪怕是双目操作符两边的数据需要转换,也不会转换

2.类模板:

类模板的书写方式和函数一样,也是在类的前面加上template<class T1,class T2…>

但类的使用和函数不同,类必须要显式实例化,也就是必须要加<>在里面给具体的模板参数的类型是什么

!!!注意:类模板最为重要的一点,接触到模板后,我们要清楚一个概念:
在模板出现之前,类的名字就是其类型,但有了模板后,类的名字就是名字,类的类型是名字<显式实例化>,而不是单纯的名字,故我们在使用创建模板类的时候千万别忘了加<>显式实例化的内容,同时,模板类最好不要声明和定义分离,并不是不能,而是很容易错,因为涉及到这个<>有时候很容易错

例如:

// Vector类名,Vector<int>才是类型
Vector<int> s1;
Vector<double> s2;

模板阶段总结:

现在,我们已经有了模板的概念并且清楚模板如何使用,模板的知识是我们接下来的STL模板库的一个重要的铺垫,一定要先弄明白再向下进行,否则对于模板库的理解会出现问题。

STL模板标准库介绍:

1.定义:

STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。也就是说,通过模板库,我们只需要传参就可以实现对应的功能,不需要再像C语言那样自己手动构建这些框架和功能。

我们的STL大概分为四种版本:

2.版本:

1.原始版本

2.PJ版本

3.RW版本

4.SGI版本

而SGI版本由于可移植性好,被广泛使用,现在的很多STL资料基本都是SGI版本,全面而且阅读体验也很好,在这里我也用这个版本的STL库

3.STL的具体内容:

STL主要分为一下的内容:

首先,这里的容器就是数据结构,这个是最容易被误解的东西,其实容器本质上就是存储数据的,加容器也无可厚非。

3.STL的一些问题:

1.STL库写的非常的冗余,很多功能的配置本质上是不需要的,这也是因为很多人为了自己的需求而不断去扩展导致的。

2.STL内部实现很复杂,设计迭代器一些东西

3其实STL的问题还有很多,但是介于我们目前所学,这两点是我们首先应该对STL有的一个大致的概念

我会在后面附上:

正如我今天的标题所说,我们首先会进入STL最常见的第一个模板:string模板,即字符串操作模板

string模板:

依旧是我们经常使用的思维方式:

让我们先想想我们C语言字符串方面的问题:在C语言中,字符串以\0结尾,C语言提供了一系列操作字符串的函数,但问题在于函数的功能太过单一,不够全面,而且很容易出现访问越界的问题,通俗来讲就是用户需要管理底层,从而导致访问大量越界。而C++则直接让用户关注于字符串本身,大量的成员函数保证了其功能的完整。

1.string模板的介绍:

  1. 字符串是表示字符序列的类
  2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
  3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
  4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
  5. 注意,这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

** 针对string的特性,我们这样总结:

a.string是表示字符串的字符串类

b.该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作

c.string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;

d. 不能操作多字节或者变长字符的序列。**

下面,我们针对string的几个重要的函数接口进行一下说明和使用:

注意,使用string类模板需要我们引用头文件#include< string >同时要开放官方命名空间std

#include< string >
using namespace std;

1.构造类:

1.构造函数:

在这里我们常用的是无参或者一个字符串或者一个字符串的一部分,在这里我们看所给的函数的具体实现的,我们从函数的具体参数可以知道,构造是支持传入无参空字符,一个字符,一个字符串(const或者非const),一个字符串的前几位,一个字符串的起始点到中止点的一部分,或者指定长度改为一个字符去创建字符串。具体要看我们要实现怎样的功能。

这里的第2个就是拷贝构造函数千万别混淆,拷贝构造也是构造函数的一种

2.析构函数:

这个析构函数和我们类和对象里面的析构函数差不多,我们具体在析构函数里面实现我们要资源清理的内存,这里不过多阐述,主要就是当有动态内存被开辟的时候,我们的析构函数就必须要显式书写对应的内存释放的析构函数。

3.赋值运算符重载:

这个赋值运算符重载和之前讲的赋值运算符重载,**倘若两个已经被创建出来的string类,就直接调用赋值运算符重载,倘若为未创建的string类,则直接调用拷贝构造函数创建变量,**这里有很多的细节,为了不影响我们的主体内容,在这里我们先不介绍,我会在本篇的最后补充着部分的知识。

2.类对象的容量操作类

1.size函数/length函数:

在这里设置size是可以用来返回字符串的长度的,和strlen一样,size也不会把\0计入到长度中,由于_size是private的成员函数,故没法直接访问,故只能通过函数待会得到长度。

length和size的用法相同,只不过以前叫length,经过更新引入了size,其具体的函数用法都一模一样。

2.capacity函数:

capacity函数是用来返回字符串的内存大小的,这个和size不一样,size是实时控制字符串的长度,而capacity不管是否插入数据,其内存都存在,capacity就是用来记录这个内存的大小的,一个字符串的长度可能为4,但它开辟的空间可能要大于4,但绝对不能小于4.

目录
相关文章
|
12天前
|
存储 缓存 C++
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
C++ 标准模板库(STL)提供了一组功能强大的容器类,用于存储和操作数据集合。不同的容器具有独特的特性和应用场景,因此选择合适的容器对于程序的性能和代码的可读性至关重要。对于刚接触 C++ 的开发者来说,了解这些容器的基础知识以及它们的特点是迈向高效编程的重要一步。本文将详细介绍 C++ 常用的容器,包括序列容器(`std::vector`、`std::array`、`std::list`、`std::deque`)、关联容器(`std::set`、`std::map`)和无序容器(`std::unordered_set`、`std::unordered_map`),全面解析它们的特点、用法
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
|
4天前
|
存储 算法 C++
【c++丨STL】priority_queue(优先级队列)的使用与模拟实现
本文介绍了STL中的容器适配器`priority_queue`(优先级队列)。`priority_queue`根据严格的弱排序标准设计,确保其第一个元素始终是最大元素。它底层使用堆结构实现,支持大堆和小堆,默认为大堆。常用操作包括构造函数、`empty`、`size`、`top`、`push`、`pop`和`swap`等。我们还模拟实现了`priority_queue`,通过仿函数控制堆的类型,并调用封装容器的接口实现功能。最后,感谢大家的支持与关注。
21 1
|
12天前
|
存储 算法 C++
深入浅出 C++ STL:解锁高效编程的秘密武器
C++ 标准模板库(STL)是现代 C++ 的核心部分之一,为开发者提供了丰富的预定义数据结构和算法,极大地提升了编程效率和代码的可读性。理解和掌握 STL 对于 C++ 开发者来说至关重要。以下是对 STL 的详细介绍,涵盖其基础知识、发展历史、核心组件、重要性和学习方法。
|
12天前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
15天前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)
|
12天前
|
安全 编译器 C语言
【C++篇】深度解析类与对象(中)
在上一篇博客中,我们学习了C++类与对象的基础内容。这一次,我们将深入探讨C++类的关键特性,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载、以及取地址运算符的重载。这些内容是理解面向对象编程的关键,也帮助我们更好地掌握C++内存管理的细节和编码的高级技巧。
|
12天前
|
存储 程序员 C语言
【C++篇】深度解析类与对象(上)
在C++中,类和对象是面向对象编程的基础组成部分。通过类,程序员可以对现实世界的实体进行模拟和抽象。类的基本概念包括成员变量、成员函数、访问控制等。本篇博客将介绍C++类与对象的基础知识,为后续学习打下良好的基础。
|
2月前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
73 19
|
2月前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
60 13
|
2月前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
58 5

热门文章

最新文章