【C++ STL】深入理解string类的底层实现

embedded/2024/12/22 18:30:22/

string类的模拟实现

  • 一.string的构造与析构函数
    • 1.普通构造函数与析构函数
    • 2.拷贝构造的浅拷贝所带来的问题
    • 3.如何实现深拷贝
  • 二.运算符重载
    • 1.赋值运算符重载
    • 2.大小比较相关的运算符重载
  • 三.迭代器的实现
  • 四.string常用操作的实现
    • 1.静态const成员npos的定义
    • 2.插入操作
    • 3.查找操作
    • 4.删除操作

string_1">一.string的构造与析构函数

1.普通构造函数与析构函数

我们首先实现一下string类的基本框架,包括成员变量,构造以及析构函数

string底层其实是一个字符数组,与普通意义上的数组不同的是,string支持自动扩容,并且可以通过断言(assert)的方式来更加严格的检查越界问题,使用起来要更加的安全有效

class string
{
public://默认构造函数string(const char* str=string">""):_size(strlen(str)){_str = new char[_size + 1];_capacity = _size;strcpy(_str, str);}//析构函数~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}
private:char* _str;size_t _size;size_t capacity;
};

分析代码:

  1. 构造函数:申请空间,并初始化string
  2. 析构函数:释放空间,避免出现内存泄露

2.拷贝构造的浅拷贝所带来的问题

我们接下来再考虑拷贝构造如何实现?
首先,通过类和对象部分的学习.我们知道:我们不实现,编译器为我们实现的默认的拷贝构造函数完成的是值拷贝也就是浅拷贝,我们的string类如果浅拷贝的话,程序就会直接崩溃.

浅拷贝的危害:
1. 对同一块空间会析构两次,第二次1析构时程序会崩溃,因为此时的空间已经被释放,是未被申请利用的.
2.如果我们对其中一个string进行修改,那么另一个string对象也会受到影响.

3.如何实现深拷贝

经过上述分析,为了避免浅拷贝带来的问题,我们需要在拷贝构造函数中实现深拷贝。深拷贝确保每个对象都有自己独立的内存空间,不会与其他对象共享内存。

代码示例:

string(const string& s)
{_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;
}

上述是传统写法(我们自己手动释放旧空间,申请新空间,并完成内容拷贝),下面我们介绍一下现代写法:

	void string::swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}string(const string& s){string tmp(s._str);swap(tmp);}

现代写法就是借助构造函数来完成空间的申请和内容的拷贝,再利用swap函数来交换,这样当函数调用完成即函数栈帧销毁时,由于tmp是临时对象,会调用析构函数完成资源的清理.

二.运算符重载

1.赋值运算符重载

在C++中,当我们将一个对象赋值给另一个对象时,同拷贝构造,默认情况下,编译器会为我们生成一个浅拷贝的赋值运算符。这意味着赋值后的对象和原对象会共享同一个内存空间,这会导致和浅拷贝相同的潜在问题,特别是在一个对象被销毁时,另一个对象继续使用该内存区域会引发错误。

因此这里依然会出现浅拷贝的问题,我们都思路和拷贝构造一样,完成深拷贝.

代码示例:

string& operator=(const string& s)
{char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete _str;_str = tmp;_size = s._size;_capacity = s._capacity;return *this;
}

整体思路和拷贝构造相似,都是先创建新空间,拷贝数据,释放旧空间.
下面我们看看令人惊叹的现代写法,只需要一行代码即可搞定:

string& operator=(string s)
{swap(s);return *this;
}

代码分析:由于此时参数不再是引用,所以s就是一个临时对象,直接交换,出了作用域s销毁自动调用析构函数.

2.大小比较相关的运算符重载

string的大小比较是按照ASCII码来进行,依次取两个string的第一个字符,第二个字符以此类推,直至出现大小不同.
例如:
aaaabc > aaaaaaaa

*为了方便,我们主要实现<和==,其他的比较都可以复用这两个函数
代码示例:

	bool operator<(const string& s) const{return strcmp(_str, s._str) < 0;}bool operator>(const string& s) const{return !(*this <= s);}bool operator<=(const string& s) const{return *this < s || *this == s;}bool operator>=(const string& s) const{return !(*this < s);}bool operator==(const string& s) const{return strcmp(_str, s._str) == 0;}bool operator!=(const string& s) const{return !(*this == s);}

三.迭代器的实现

迭代器是我们访问容器的一种通用方式,它可以让我们在不了解具体某个数据结构的底层的情况下,依旧完成对容器的遍历,通常情况下,我们可以将迭代器理解为像"指针"一样的东西.

这里由于string的底层是连续的物理空间,我们直接使用char* 作为string的迭代器即可.当然,在Linux和VS环境下官方库中的string的迭代器并不是简答的指针,而是将指针封装之后的一个类

这里我们可以打印迭代器的类型看一下:

int main()
{cout << typeid(string::iterator).name() << endl;return 0;
}

在VS下,这段程序的运行结果是:

class std::_String_iterator<class std::_String_val<struct std::_Simple_types<char>> >

可以看出,是经过复杂封装之后的模板类.

我们自己实现的迭代器代码示例:

class string
{public:typedef char* iterator;typedef const char* const_iterator;iterator string::begin(){return _str;}iterator string::end(){return _str + _size;}const_iterator string::begin() const{return _str;}const_iterator string::end() const{return _str + _size;}
};

在定义了begin和end函数之后,我们自己的string也就支持了C++11中的范围for遍历.

string_200">四.string常用操作的实现

1.静态const成员npos的定义

通过查看文档,我们会发现在插入,删除,查找等函数中,都用了一个缺省值npos,它的类型是const static size_t,值为-1,也就是说它表示无符号整数的最大值,大约是42亿多.

对于静态成员变量的声明与定义,之前的类和对象篇有过讲解,如有需要可以前往:类和对象(下).

代码示例:

class string {
public:static const size_t npos = -1;  // 可以在类内初始化
};

2.插入操作

	void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}
void insert(size_t pos, char ch)
{assert(pos < _size);if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];end--;}_str[pos] = ch;_size++;
}

这里注意一个点即可: size_t(无符号整数) 与 0 相比较作为循环条件时,容易出现死循环
例如:

size_t end = _size ;while (end >= pos){_str[end + 1] = _str[end];end--;}
//当pos等于0时,会出现死循环!!!

3.查找操作

代码示例:

	//查找一个子串size_t find(const char* str, size_t pos) const{char* sub = strstr(_str + pos, str);return sub - _str;}//查找一个字符size_t find(char ch, size_t pos) const{for (int i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}

4.删除操作

代码示例:

	//从pos位置开始,删除len个字符void erase(size_t pos, size_t len){assert(pos < _size);if (len > _size - pos){_str[pos] = string">'\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}}

希望能让你对string的理解更加透彻,感谢观看!!!


http://www.ppmy.cn/embedded/120183.html

相关文章

大模型智能体在金融公告理解领域的应用 | OPENAIGC开发者大赛高校组AI创新之星奖

在第二届拯救者杯OPENAIGC开发者大赛中&#xff0c;涌现出一批技术突出、创意卓越的作品。为了让这些优秀项目被更多人看到&#xff0c;我们特意开设了优秀作品报道专栏&#xff0c;旨在展示其独特之处和开发者的精彩故事。 无论您是技术专家还是爱好者&#xff0c;希望能带给…

基于php的幸运舞蹈课程工作室管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

[Uninstall] 软件彻底卸载工具的下载及详细安装使用过程(附有下载文件)

一般软件安装的有问题&#xff0c;或者想重新安装其他版本就需要将原来的版本删除干净&#xff0c;但常常删不干净&#xff0c;本文分享一个软件彻底卸载工具&#xff0c;完成彻底卸载软件的工作 下载链接在文末 下载压缩包后解压 &#xff01;&#xff01;安装路径不要有中文…

【Verilog学习日常】—牛客网刷题—Verilog企业真题—VL62

序列发生器 描述 编写一个模块&#xff0c;实现循环输出序列001011。 模块的接口信号图如下&#xff1a; 要求使用Verilog HDL实现&#xff0c;并编写testbench验证模块的功能。 输入描述&#xff1a; clk&#xff1a;时钟信号 rst_n&#xff1a;复位信号&#xff0c;低电平…

Qt_文件操作

目录 1、输入输出类 2、QFile介绍 3、QFile测试 4、QFileInfo介绍 5、QFileInfo测试 结语 前言&#xff1a; 文件操作是程序中的一个重要概念&#xff0c;数据的存储和读取都离不开文件操作。Qt具有强大的跨平台性&#xff0c;因此他提供了跨平台的文件操作能力。Qt中将…

【机器学习-无监督学习】聚类

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈Python机器学习 ⌋ ⌋ ⌋ 机器学习是一门人工智能的分支学科&#xff0c;通过算法和模型让计算机从数据中学习&#xff0c;进行模型训练和优化&#xff0c;做出预测、分类和决策支持。Python成为机器学习的首选语言&#xff0c;…

阿里云k8s如何创建可用的api token

阿里云的 Kubernetes 配置文件&#xff08;如您所提供的 YAML 格式文件&#xff09;通常不会直接包含用于连接 Kubernetes 集群的令牌。而是包含了客户端证书和私钥数据&#xff0c;这些是用于通过证书验证而不是令牌验证的方式来与 Kubernetes API 服务器进行安全交互的。 1.…

什么是ELK

ELK 是指 Elasticsearch、Logstash 和 Kibana 这三种工具的组合&#xff0c;通常用于日志分析、数据搜索和可视化。它们分别承担不同的功能&#xff0c;形成了强大的数据处理和分析平台&#xff1a; Elasticsearch&#xff1a;一个分布式搜索引擎&#xff0c;擅长实时搜索、存储…