【C++学习】STL容器——string

news2024/10/4 18:26:42

一、STL简介

1.1 什么是STL

        STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。

1.2 STL的版本

  • 原始版本

        Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意 运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使 用。 HP 版本--所有STL实现版本的始祖。

  • P. J. 版本

        由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改。缺陷:可读性比较低, 符号命名比较怪异。

  • RW版本

        由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一般。

  • SGI版本

        由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版 本。被GCC(Linux)采用,可移植性好, 可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高。我们后面学习STL要阅读部分源代码, 主要参考的就是这个版本。

1.3 STL的六大组件

1.4 STL的重要性

        网上有句话说:“不懂STL,不要说你会C++”。STL是C++中的优秀作品,有了它的陪伴,许多底层的数据结构以及算法都不需要自己重新造轮子,站在前人的肩膀上,健步如飞的快速开发。

1.5 如何学习STL 

简单总结一下:学习STL的三个境界:能用,明理,能扩展 。

1.6 STL的缺陷

1. STL库的更新太慢了。上一版靠谱是C++98,中间的C++03基本一些修订。C++11出来已经相隔了13年,STL才进一步更新。

2. STL现在都没有支持线程安全。并发环境下需要我们自己加锁。且锁的粒度是比较大的。

3. STL极度的追求效率,导致内部比较复杂。比如类型萃取,迭代器萃取。

4. STL的使用会有代码膨胀的问题,比如使用vector/vector/vector这样会生成多份代码,当然这是模板语法本身导致的。

二、为什么学习string类

2.1 C语言中的字符串

        C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数, 但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。

2.2 平时做题

        在OJ题中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数。

三、标准库中的string类

3.1 string类(了解)

string类的文档介绍:http://www.cplusplus.com/reference/string/string/?kw=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)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

总结:

1. string是表示字符串的字符串类;

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

3. string在底层实际是:basic_string模板类的别名,typedef basic_string string;

4. 不能操作多字节或者变长字符的序列。 在使用string类时,必须包含#include头文件以及using namespace std;

3.2 string类的常用接口说明

1. string类对象的常见构造

函数名称功能说明
string()(重点)构造空的string类对象,即空字符串
string(const char* s) (重点)用C-string来构造string类对象
string(size_t n, char c)string类对象中包含n个字符c
string(const string&s) (重点)拷贝构造函数
void Teststring()
{
    string s1; // 构造空的string类对象s1
    string s2("hello bit"); // 用C格式字符串构造string类对象s2
    string s3(s2); // 拷贝构造s3
}

2. string类对象的容量操作

函数名称功能说明
size(重点)返回字符串有效字符长度
length返回字符串有效字符长度
capacity返回空间总大小
empty(重点)检测字符串释放为空串,是返回true,否则返回false
clear (重点)清空有效字符
reserve (重点)为字符串预留空间**
resize (重点)将有效字符的个数该成n个,多出的空间用字符c填充

注意:

1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。

2. clear()只是将string中有效字符清空,不改变底层空间大小。

3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。

4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。

3. string类对象的访问及遍历操作

函数名称功能说明
operator[] (重点)返回pos位置的字符,const string类对象调用
begin+ endbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
rbegin + rendbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
范围forC++11支持更简洁的范围for的新遍历方式

4. string类对象的修改操作

函数名称功能说明
push_back在字符串后尾插字符c
append在字符串后追加一个字符串
operator+= (重点)在字符串后追加字符串str
c_str(重点)返回C格式字符串
find + npos(重点)从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
rfind从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
substr在str中从pos位置开始,截取n个字符,然后将其返回

注意:

1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。

2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

5. string类非成员函数

函数功能说明
operator+尽量少用,因为传值返回,导致深拷贝效率低
operator>> (重点)输入运算符重载
operator (重点)输出运算符重载
输出运算符重载获取一行字符串
relational operators (重点)大小比较

6. vs和g++下string结构的说明

注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节。

  • vs下string的结构

        string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字符串的存储空间:

        当字符串长度小于16时,使用内部固定的字符数组来存放

        当字符串长度大于等于16时,从堆上开辟空间

union _Bxty
{   
    // storage for small buffer or pointer to larger one

    value_type _Buf[_BUF_SIZE];
    pointer _Ptr;
    char _Alias[_BUF_SIZE]; // to permit aliasing

} _Bx;

        这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。 其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量。最后:还有一个指针做一些其他事情。 故总共占16+4+4+4=28个字节。

  • g++下string的结构

        g++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:

  1. 空间总大小
  2. 字符串有效长度
  3. 引用计数
  4.  指向堆空间的指针,用来存储字符串。
struct _Rep_base
{
    size_type _M_length;
    size_type _M_capacity;
    _Atomic_word _M_refcount;
};

四、string类的模拟实现

4.1 经典的string类问题

        上面已经对string类进行了简单的介绍,只要能够正常使用即可。在面试中,面试官总喜欢让学生自己来模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数。看一下以下string类的实现是否有问题?

// 为了和标准库区分,此处使用String
class String
{
public:
    /*String()
        :_str(new char[1])

        {*_str = '\0';}

    */
    //String(const char* str = "\0") 错误示范
    //String(const char* str = nullptr) 错误示范

    String(const char* str = "")
    {
    // 构造String类对象时,如果传递nullptr指针,可以认为程序非

        if (nullptr == str)
        {
            assert(false);
            return;
        }
 
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
    }
 
    ~String()
    {
        if (_str)
        {
            delete[] _str;
            _str = nullptr;
        }
    }

private:
    char* _str;
};
 

// 测试

void TestString()
{
    String s1("hello bit!!!");
    String s2(s1);
}

        说明:上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

4.2 浅拷贝

        浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。 就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一不想分享就你争我夺,玩具损坏。

        可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。父母给每个孩子都买一份玩具,各自玩各自的就不会有问题了。

4.3 深拷贝

        如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。

4.3.1 传统版写法的String类

class String
{
public:
    String(const char* str = "")
    {
        //构造String类对象时,如果传递nullptr指针,可以认为程序非
        if(nullptr == str)
        {
            assert(false);
            return;
        }
        
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
    }

    String(const String& s)
        : _str(new char[strlen(s._str + 1])
    {   
        strcpy(_str, s._str);
    }
 
    String& operator=(const String& s)
    {
        if(this != &s)
        {
            char* pStr = new char[strlen(s.str) + 1]
            strcpy(pStr, s._str);
            delete[] _str;
            _str = pStr;
        }
        return *this;
    }

    ~String()
    {
        if(_str)
        {
            delete[] _str;
            _str = nullptr;
        }
    }

private:
    char* _str;
};

4.3.2 现代版写法的String类

class String
{
public:
    String(const char* str = "")
    {
        if(nullptr == str)
        {
            assert(false);
            return;
        }
 
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
    }

    String(const String& s)
        : _str(nullptr)
    {   
        Sring strTmp(s._str);
        swap(_str, strTmp._str);
    }
 
    //对比下和上面的赋值哪个实现比较好?
    String& operator=(String s)
    {
        swap(_str, s._str);
        return *this;
    }

    /*
    String& operator=(const String& s)
    {
        if(this != &s)
        {
            Sring strTmp(s._str);
            swap(_str, strTmp._str);
        }
        return *this;
    }
    */

    ~String()
    {
        if(_str)
        {
            delete[] _str;
            _str = nullptr;
        }
    }

private:
    char* _str;
};

4.3 写实拷贝

        写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。

        引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/699517.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

java的final变量

Java在声明一个变量时,如果声明为final的,那么这个变量只能被赋值一次,赋值以后变量的值不能改变。 如果final变量指向一个对象的引用,对象的状态可以改变,但final变量始终指向同一对象的引用。 这个也规则也适用于数组…

第十八章 MobileViT网络详解

系列文章目录 第一章 AlexNet网络详解 第二章 VGG网络详解 第三章 GoogLeNet网络详解 第四章 ResNet网络详解 第五章 ResNeXt网络详解 第六章 MobileNetv1网络详解 第七章 MobileNetv2网络详解 第八章 MobileNetv3网络详解 第九章 ShuffleNetv1网络详解 第十章…

代码随想录算法训练营第五十一天

第一题、买卖股票的最佳时机含冷冻期 力扣题目链接 class Solution { public:int maxProfit(vector<int>& prices) {if(prices.size() 0) return 0;vector<vector<int>> dp(prices.size(), vector<int>(4, 0));dp[0][0] -prices[0];dp[0][1] d…

JVM GC ROOT分析与垃圾收集器原理分析(四)

目录 一、GC ROOT 1、虚拟机栈中的本地变量 2、static 成员 3、常量引用 4、本地方法栈中的变量 5、类加载器 6、线程 二、回收算法 1、标记和清除 2、复制算法 3、标记整理 三、垃圾收集器 1、新生代-复制算法 2、老年代-标记清除/整理 3、垃圾收集器分类 1、…

频数分析拟合优度卡方检验

一、案例介绍 某医学美容院对某年425位顾客抱怨原因进行了分析&#xff0c;结果见下表&#xff0c;现在想知道这四种抱怨原因的构成比是否有差异&#xff1f; 二、问题分析 本案例的分析目的是研究四种抱怨原因的构成比是否存在差异&#xff0c;抱怨原因为定类数据&#xff0…

Spring Boot 中的 EhCacheCacheManager 是什么,原理,如何使用

Spring Boot 中的 EhCacheCacheManager 是什么&#xff0c;原理&#xff0c;如何使用 前言 在现代化的应用程序中&#xff0c;缓存是提高性能的关键所在。缓存可以降低数据库的负载&#xff0c;提高响应速度&#xff0c;减少资源消耗。Spring Boot提供了多种缓存管理器&#…

结合GPS的SAT图道路识别

文章目录 2019 Leveraging Crowdsourced GPS Data for Road Extraction from Aerial Imagery基本介绍对GPS的使用关于怎么证明GPS信息有效How to render gps to imageGPS信息简介GPS点状特征GPS其他特征挖掘 可借鉴的点 2020 Convolutional Recurrent Network for Road Boundar…

《黑马头条》SpringBoot+SpringCloud+ Nacos等企业级微服务架构项目

环境搭建、SpringCloud微服务(注册发现、服务调用、网关) 1)课程对比 2)项目概述 2.1)能让你收获什么 2.2)项目课程大纲 2.3)项目概述 随着智能手机的普及&#xff0c;人们更加习惯于通过手机来看新闻。由于生活节奏的加快&#xff0c;很多人只能利用碎片时间来获取信息&#x…

红帽认证考试流程详解,让你少走弯路

参加红帽认证考试涉及以下三个流程 账号和证件的准备 1 RHN 账号注册 考试前需要您提前注册红帽帐号(RHN) 注册地址&#xff1a;https://www.redhat.com/wapps/ugc/register.html 注册时以下条目请重点注意&#xff1a; ▸ 账户类型选择 Personal ▸ 红帽账户登录名…

onnx修改模型节点【改变input的shape】

因为转onnx后模型的输入无法转rknn&#xff0c;所以要对onnx进行节点修改成左边 import onnx import onnx.helper as helper from onnx.helper import TensorProto import numpy as npmodel onnx.load(demo.change.onnx) #自己创建新的节点 ###############################…

【LeetCode热题100】打卡第29天:二叉树的层序遍历二叉树的最大深度

文章目录 【LeetCode热题100】打卡第29天&#xff1a;二叉树的层序遍历&二叉树的最大深度⛅前言 二叉树的层序遍历&#x1f512;题目&#x1f511;题解 二叉树的最大深度&#x1f512;题目&#x1f511;题解 【LeetCode热题100】打卡第29天&#xff1a;二叉树的层序遍历&am…

Stage模型HarmonyOS服务卡片开发ArkTS卡片相关模块

图1 ArkTS卡片相关模块 FormExtensionAbility&#xff1a;卡片扩展模块&#xff0c;提供卡片创建、销毁、刷新等生命周期回调。 FormExtensionContext&#xff1a;FormExtensionAbility的上下文环境&#xff0c;提供FormExtensionAbility具有的接口和能力。 formProvider&…

CASAIM三维扫描仪在运动防护的应用高端运动器材设计定制器材头盔

CASAIM三维扫描技术在运动防护领域有广泛的应用&#xff0c;可以帮助设计和生产高端运动器材&#xff0c;检测运动器材适配性&#xff0c;以提供更好的运动防护。 CASAIM三维扫描仪可以将运动员的身体尺寸和形状精确地捕捉下来&#xff0c;以便制造出符合其个人需求的定制化器…

稳坐钓鱼台,第三大手机操作系统?华为鸿蒙OS 4.0首批测试版推送

华为鸿蒙OS 4.开发者Beta版首期招募正式开始&#xff0c;首批机型包括华为P60、P60 Pro、Mate 50、MatePad Pro 11英寸性能版等8款。 不可思议的是&#xff0c;华为方才开始招募没几天&#xff0c;就已经开始向用户推送鸿蒙OS 4.开发者Beta版的升级。它的更新速度令人瞩目&…

ChatGPT 话题相关和类 ChatGPT 工具 | 优质文章、相关论文、应用、学习资源整理

文章目录 一、前言二、主要内容三、总结 &#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、前言 人工智能与手机和互联网一样具有革命性。 2023 年已经过去一半&#xff0c;ChatGPT 在今年以来一直备受瞩目。目前 ChatGPT 的更新速度逐渐放缓&#…

常见的远程协助软件简介

1.RayLink远程控制软件 RayLink是一个支持多系统多设备间相互远程控制的软件&#xff0c;是具有远程制作、远程办公、远程游戏、远程协助等需求的IT技术、游戏玩家、网络办公等这些人的最佳选择。通过使用RayLink&#xff0c;可随时随地远程控制其他电脑或手机&#xff0c;远程…

AI日报:DragGAN通过拖拽像素点实现图像调整 等

&#x1f989; AI新闻 &#x1f680; DragGAN&#xff1a;一种直观的图像编辑工具&#xff0c;通过拖拽像素点实现图像调整 摘要&#xff1a;研究者们来自马克斯・普朗克计算机科学研究所、MIT CSAIL和谷歌&#xff0c;他们开发了一种名为DragGAN的图像编辑工具。通过拖拽像素…

MATLAB 之 Simulink 子系统及其封装

这里写目录标题 一、子系统及其封装1. 子系统的创建1.1 通过 Subsystem 模块建立子系统1.2 通过已有的模块建立子系统 2. 子系统的条件执行2.1 使能子系统2.2 触发子系统2.3 使能加触发子系统 3. 子系统的封装3.1 lcon & Ports 选项卡的参数设置3.2 Parameters & Dialo…

最大池化|知识补充

最大池化的超参数&#xff1a;规模为2的过滤器&#xff0c;步幅为2 对于左上角深蓝色的区域&#xff0c;其中最大的数值是9&#xff0c;所以得到的输出为9&#xff1b; 向右移动两个步幅&#xff0c;输出为2&#xff1b; 第二行&#xff0c;22的绿色区域内输出为6&#xff1b;…

2023版新鲜出炉,1000多道互联网Java高频面试真题及详解汇总

最近感慨面试难的人越来越多了&#xff0c;一方面是市场环境&#xff0c;更重要的一方面是企业对Java的人才要求越来越高了。 基本上这样感慨的分为两类人&#xff1a; 第一&#xff0c;虽然挂着3、5年经验&#xff0c;但肚子里货少&#xff0c;也没啥拿得出手的项目&#xff…