返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >【C++精华铺】10.STL string模拟实现
  • 372
分享到

【C++精华铺】10.STL string模拟实现

c++开发语言stl数据结构 2023-09-11 09:09:18 372人浏览 独家记忆
摘要

1. 序言         STL(标准模板库)是一个c++标准库,其中包括一些通用的算法、容器和函数对象。STL的容器是C++ STL库的重要组成部分,它们提供了一种方便的方式来管理同类型的对象。其中,STLstring是一种常用的字符串

1. 序言

        STL(标准模板库)是一个c++标准库,其中包括一些通用的算法容器和函数对象。STL的容器是C++ STL库的重要组成部分,它们提供了一种方便的方式来管理同类型的对象。其中,STLstring是一种常用的字符串类型。

        STLstring是一个类,它封装了字符串的操作,并提供了一组成员函数。STLstring的实现使用了动态的内存分配技术,这意味着字符串的大小可以随时改变。STLstring还提供了一些高效的成员函数,例如substr、find、replace等,这些函数可以对字符串进行快速的操作。

        STLstring的实现主要基于字符数组。字符数组是一种固定大小的数组,其中每个元素包含一个字符。STLstring使用一个字符数组来存储字符串,并通过动态的内存分配技术来管理数组的大小。当向一个空的STLstring对象中添加字符时,STLstring会自动调整数组的大小。

        STLstring还实现了一些常见的字符串操作,例如连接字符串、查找字符串、替换字符串和分割字符串等。这些操作使用了C++ STL库中的alGorithm算法,可以高效地处理字符串。同时,STLstring也提供了迭代器的支持,允许用户使用STL算法来处理字符串。

2. string类的接口实现

        在实现接口之前先要给出我们的初始类,包括三个私有成员(_size,_capacity,_str),接下来我们会对这个初始类一步一步的完善(为了文章易读后续接口函数不会展示类的全貌,只会展示实现的接口函数内容,在文章的末尾会给出完整的string类代码)如下:

namespace zybjs{class string{public:private:size_t _size;size_t _capacity;char* _str;};}

2.1 构造函数

(1)默认构造和C串构造

        在STL库中将C串构造和默认构造分开实现,但是我们在模拟实现string类的时候可以将这俩个构造函数合并,这样的代码会更简洁,也方便我们自己的使用。在此之前我们先按库里面的思路走一趟:在我们给初始容量的时候即便字符串长度是0我们也不能给0,避免后续无法倍数扩容,这里我们给的是4。并且给字符串开空间的时候要比容量多一个,最后一个留给'\0'。(VS下实现思路不同,VS下给了一个16字节的字符数组_buf,如果要存储的字符串小于16字节就存放在字符数组_buf中,否则就重新开一个空间)

default (1)            string();

from c - string(2)  string(const char* s);

string():_size(0),_capacity(4)  //不能给0,如果给0后续无法倍数扩容 下同{_str = new char[_capacity + 1]; //开空间的时候要比容量多一个字节留给'\0',因为容量的大小不算'\0',下同    _str[0] = '\0';}string(const char* str)  //const类型接收右值:_size(strlen(str)){_capacity = _size == 0 ? 4 : _size;_str = new char[_capacity + 1];strcpy(_str, str);}

         但是我们在自己实现的时候大可不必这样去写,我们之前学习了缺省参数,并且全缺省的构造函数也可以作为默认构造,所以我们可以对上述代码进行优化,给str一个空串作为缺省参数,这样当我们调用的时候不给实参就会默认使用空串进行构造来完成库中”string();“的功能。如下:

string(const char* str = "")   //const类型接收右值:_size(strlen(str)){_capacity = _size == 0 ? 4 : _size;_str = new char[_capacity + 1];strcpy(_str,str);  }

(2)拷贝构造

        string类的拷贝构造很简单,要注意的点有俩个:其一,string类涉及到空间管理,所以在拷贝的时候要深拷贝,否则会导致同一空间多次析构导致报错;其二,传参不能传值传参,要使用传引用传参,否则会导致无穷递归

string(const string& s):_size(s._size),_capacity(s._capacity){      //深拷贝_str = new char[_capacity + 1];  //重新开空间strcpy(_str,s._str);     //字符序列拷贝}

2.2 析构函数

        string类因为涉及到内存的管理,所以析构函数不能使用默认生成的析构,需要我们自己去实现析构。在实现析构的时候将_size和_capacity全部置零,然后通过delete[]释放_str就可以了。如下:

~string(){delete[] _str;   //释放_str_str = nullptr;_size = _capacity = 0;}

2.3 size()、capacity()

        size()和capacity()很多人在实现的时候都觉得很简单反而会忽略一个点:就是const对象访问的时候是否会权限放大。因为这个俩个函数仅涉及到读取,没有修改的操作,所以我们只需要实现const版本来兼容const对象和非const对象。如下

size_t size() const  //兼容const对象和非const对象{return _size;}size_t capacity() const//兼容const对象和非const对象{return _capacity;}

2.4 c_str()

        返回指向一个数组的指针,该数组包含以 null 结尾的字符序列(即 C 字符串),该序列表示字符串对象的当前值。只需完成const版本来兼容const对象和非const对象。指针的返回值也需要是const char *类型,防止通过指针对对象进行修改。

const char* c_str() const {return _str;}

2.5 operator[]

        因为要实现类似于数组的访问方式,所以我们要实现[]的重载形式。[]的特性:能够随机访问元素,对于非const对象能够修改元素。所以operator[]要实现const版本和非const版本,const版本的返回值必须是const引用。

const char& operator[](size_t pos) const{assert(pos < _size&& pos >= 0);return _str[pos];}char& operator[](size_t pos){assert(pos < _size && pos >= 0);return _str[pos];}

2.6 operator=

        赋值运算符的重载是对字符串对象的深拷贝,和拷贝构造的过程相同,但是‘=’的特性支持连续的赋值,所以我们在实现赋值重载的时候需要返回一个string对象来支持赋值重载的连续赋值。因为我们不涉及对传入参数的修改,所以我们需要传入一个const string& 类型。注意在赋值之前需要释放原来的空间。如下:

string& operator=(const string& s){if (s._str != _str){_size = s._size;_capacity = s._capacity;//深拷贝char* _tmp = new char[_capacity + 1]; strcpy(_tmp,s._str);//先释放原来的空间delete[] _str;_str = _tmp;}return *this;}

测试

zybjs::string s1("cacaca");zybjs::string s2("dadada");zybjs::string s3("bababa");s3 = s2 = s1;std::cout << s1.c_str() << std::endl;std::cout << s2.c_str() << std::endl;std::cout << s3.c_str() << std::endl;

2.7 字符串比较 

        字符串比较我们通过strcmp来进行比较(strcmp(srt1,str2)),返回的值为0,字符串相同;返回的值大于0,str1>str2;返回的值小于0,str1

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

2.8 迭代器实现

        string的迭代器底层是一个指针,string类的迭代器是一种用于访问字符串中字符的对象,可以通过迭代器的运算符访问字符串中的字符。迭代器为C++容器提供了一种通用的访问手段。

typedef char* iterator;typedef const char* const_iterator;iterator begin(){//指向第一个字符的位置return _str;}iterator end(){//指向最后一个字符的后一个位置return _str + _size;}const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}

2.9 reserve

        reserve()是为了给对象预留空间,如果我们提前得知字符串需要的空间我们就可以提前开好,避免频繁扩容带来的性能消耗。当reserve的参数小于string底层空间大小的时候,reserve就不会对容量进行处理。

void reserve(size_t n){if (n > _capacity){char* _tmp = new char[n+1];strcpy(_tmp, _str);delete[] _str;_str = _tmp;_capacity = n;}}

2.10 resize

          修改字符有效个数为n,多出的空间用字符ch填充。如果n小于有效字符数,本质就是删字符操作。如果容量不够会扩容。

void resize(size_t n, char ch = '\0'){//当n<有效字符数的时候本质上就是删字符//但是我们一般不会进行缩容,在原来的空间上将n位置的字符设置为'\0'if (n < _size){_size = n;_str[_size] = '\0';}else if (n > _size){if (n > _capacity)  //n>_capacity就进行扩容{reserve(n);        }size_t i = _size;while (i < n)       //将非有效字符初始化为ch{_str[i] = ch;i++;}_size = n;_str[_size] = '\0'; //设置终止位}}

2.11 push_back、append、operator+=

        push_back尾插,append是在字符串后面追加一个字符串,实现比较简单,但要注意检查容量。

void push_back(char ch){if (_size + 1 > _capacity){reserve(2 * _capacity);}_str[_size] = ch;_size++;_str[_size] = '\0';}void append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str+_size,str);_size += len;}

        operator+=的功能可以由push_back和append的复用来实现:

string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}

2.12 insert

        insert是string类中支持pos位插入字符或者字符串的函数。其中,pos表示插入的位置,返回string的引用表示插入后的新字符串。

string& insert(size_t pos, char ch){if (_size + 1 > _capacity){reserve(2 * _capacity);}size_t n = pos;size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];end--;}_str[pos] = ch;_size += 1;_str[_size] = '\0';return *this;}string& insert(size_t pos, const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_capacity + len);}size_t n = pos;size_t end = _size + 1;while (end > pos){_str[end - 1 + len] = _str[end - 1];end--;}strncpy(_str + pos, str, len);_size += len;_str[_size] = '\0';return *this;}

2.13 erase

        C++中的string类提供了一个名为erase()的成员函数,用于删除字符串中的一部分字符并修改该字符串。该函数可以接受1个或2个参数,具体取决于要删除的字符数。如果没有显式的指定删除字符数,会使用默认的npos也就是无符号整型-1来作为缺省参数(65535),就会默认删除pos位后面所有的字符。然后返回删除字符后生成的新字符。

        首先我们要定义一个和库里相同的npos:

string& erase(size_t pos,size_t len = npos){assert(pos < _size);if (len >= _size - pos - 1){_size = pos;_str[_size] = '\0';}else{strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;}

2.14 流插入和流提取

        

std::ostream& operator<<(std::ostream& out, const string& s){for (auto ch : s){out << ch;}return out;}std::istream& operator>>(std::istream& in, string& s){s.clear();        //输入之前要清空字符串char ch = in.get();//获取字符包括'\n'char buff[32];  //设置缓冲区来防止频繁扩容size_t i = 0;while (ch != ' ' && ch != '\n'){buff[i] = ch;if (i == 30){buff[31] = '\0';s += buff;i = 0;}i++;ch = in.get();}buff[i] = '\0';s += buff;}

3. 完整代码(均调试通过)

#pragma once#define _CRT_SECURE_NO_WARNINGS 1#include#includenamespace zybjs{class string{public:typedef char* iterator;typedef const char* const_iterator;iterator begin(){//指向第一个字符的位置return _str;}iterator end(){//指向最后一个字符的后一个位置return _str + _size;}const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}//string()//:_size(0)//,_capacity(4)  //不能给0,如果给0后续无法倍数扩容 下同//{//_str = new char[_capacity + 1]; //开空间的时候要比容量多一个字节留给'\0',因为容量的大小不算'\0',下同//_str[0] = '\0';//}//string(const char* str)//:_size(strlen(str))//{//_capacity = _size == 0 ? 4 : _size;//_str = new char[_capacity + 1];//strcpy(_str, str);//}string(const char* str = "")   //const类型接收右值:_size(strlen(str)){_capacity = _size == 0 ? 4 : _size;_str = new char[_capacity + 1];strcpy(_str,str);  }string(const string& s):_size(s._size),_capacity(s._capacity){      //深拷贝_str = new char[_capacity + 1];  //重新开空间strcpy(_str,s._str);     //字符序列拷贝}~string(){delete[] _str;   //释放_str_str = nullptr;_size = _capacity = 0;}size_t size() const  //兼容const对象和非const对象{return _size;}size_t capacity() const//兼容const对象和非const对象{return _capacity;}const char* c_str() const {return _str;}const char& operator[](size_t pos) const{assert(pos < _size&& pos >= 0);return _str[pos];}char& operator[](size_t pos){assert(pos < _size && pos >= 0);return _str[pos];}string& operator=(const string& s){if (s._str != _str){_size = s._size;_capacity = s._capacity;//深拷贝char* _tmp = new char[_capacity + 1]; strcpy(_tmp,s._str);//先释放原来的空间delete[] _str;_str = _tmp;}return *this;}//字符串比较bool operator>(const string& s) const{return strcmp(_str, s._str) > 0;}bool operator==(const string& s) const{return strcmp(_str, s._str) == 0;}bool operator>=(const string& s) const{//return *this > s || *this == s;return *this > s || s == *this;}bool operator<(const string& s) const{return !(*this >= s);}bool operator<=(const string& s) const{return !(*this > s);}bool operator!=(const string& s) const{return !(*this == s);}void reserve(size_t n){if (n > _capacity){char* _tmp = new char[n+1];strcpy(_tmp, _str);delete[] _str;_str = _tmp;_capacity = n;}}void resize(size_t n, char ch = '\0'){//当n<有效字符数的时候本质上就是删字符//但是我们一般不会进行缩容,在原来的空间上将n位置的字符设置为'\0'if (n < _size){_size = n;_str[_size] = '\0';}else if (n > _size){if (n > _capacity)  //n>_capacity就进行扩容{reserve(n);        }size_t i = _size;while (i < n)       //将非有效字符初始化为ch{_str[i] = ch;i++;}_size = n;_str[_size] = '\0'; //设置终止位}}void push_back(char ch){if (_size + 1 > _capacity){reserve(2 * _capacity);}_str[_size] = ch;_size++;_str[_size] = '\0';}void append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str+_size,str);_size += len;}string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}string& insert(size_t pos, char ch){if (_size + 1 > _capacity){reserve(2 * _capacity);}size_t n = pos;size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];end--;}_str[pos] = ch;_size += 1;_str[_size] = '\0';return *this;}string& insert(size_t pos, const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_capacity + len);}size_t n = pos;size_t end = _size + 1;while (end > pos){_str[end - 1 + len] = _str[end - 1];end--;}strncpy(_str + pos, str, len);_size += len;_str[_size] = '\0';return *this;}string& erase(size_t pos,size_t len = npos){assert(pos < _size);if (len >= _size - pos - 1){_size = pos;_str[_size] = '\0';}else{strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;}void clear(){_size = 0;_str[_size] = '\0';}private:size_t _size;size_t _capacity;char* _str;static const size_t npos;};const size_t string::npos = -1;std::ostream& operator<<(std::ostream& out, const string& s){for (auto ch : s){out << ch;}return out;}std::istream& operator>>(std::istream& in, string& s){s.clear();        //输入之前要清空字符串char ch = in.get();//获取字符包括'\n'char buff[32];  //设置缓冲区来防止频繁扩容size_t i = 0;while (ch != ' ' && ch != '\n'){buff[i] = ch;if (i == 30){buff[31] = '\0';s += buff;i = 0;}i++;ch = in.get();}buff[i] = '\0';s += buff;}}

 

来源地址:https://blog.csdn.net/qq_64293926/article/details/132516484

--结束END--

本文标题: 【C++精华铺】10.STL string模拟实现

本文链接: https://lsjlt.com/news/402876.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 【C++精华铺】10.STL string模拟实现
    1. 序言         STL(标准模板库)是一个C++标准库,其中包括一些通用的算法、容器和函数对象。STL的容器是C++ STL库的重要组成部分,它们提供了一种方便的方式来管理同类型的对象。其中,STLstring是一种常用的字符串...
    99+
    2023-09-11
    c++ 开发语言 stl 数据结构
  • 【C++精华铺】9.STL string
    目录 1. string类的优势 2. string类的常用接口 2.1 常用构造 1. 空串构造:string(); 2. C串构造:string(const char* s); 3. 拷贝构造:string(const string&...
    99+
    2023-09-01
    c++ stl 数据结构
  • C++ STL vector的模拟实现
    1. vector的介绍和使用 vector是表示可变大小数组的序列容器。 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对v...
    99+
    2024-04-02
  • 关于C++STL string类的介绍及模拟实现
    目录一、标准库中的string类1.string类2.string类中的常用接口说明+模拟实现2.1 string类对象的常见构造+模拟实现 2.2 string类对象的容量操作+模...
    99+
    2024-04-02
  • 详解C++ STL模拟实现list
    目录list 概述接口总览list 的节点默认成员函数默认构造函数析构函数拷贝构造函数复制赋值函数list 的迭代器构造函数operator==operator!=operator*...
    99+
    2023-01-11
    C++ STL实现list C++ STL list
  • 详解C++STL模拟实现forward_list
    目录forward_list 概述接口总览forward_list 的节点默认成员函数默认构造函数析构函数forward_list 的迭代器构造函数operator==operato...
    99+
    2023-01-13
    C++ STL实现forward_list C++ STL forward_list
  • C++  STL _ Vector使用及模拟实现
    目录1.Vector的介绍1.1 Vector的介绍2.Vector的使用2.1 vector的定义2.2 vector 迭代器的使用 2.3 vector的空间增长问题3...
    99+
    2024-04-02
  • C++中STL vector的模拟实现示例
    这篇文章主要介绍C++中STL vector的模拟实现示例,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1. vector的介绍和使用vector是表示可变大小数组的序列容器。就像数组一样,vector也采用的连续存...
    99+
    2023-06-14
  • 利用C++模拟实现STL容器:list
    目录一、list的介绍二、list的排序三、迭代器1、list的迭代器失效问题2、迭代器的功能分类3、list迭代器的模拟实现4、迭代器价值5、迭代器operator->的重载...
    99+
    2022-12-08
    C++实现STL容器list C++ STL容器list C++ STL容器
  • 怎么用C++模拟实现STL容器
    这篇文章主要介绍了怎么用C++模拟实现STL容器的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇怎么用C++模拟实现STL容器文章都会有所收获,下面我们一起来看看吧。一、list的介绍列表是一种顺序容器,它允许在...
    99+
    2023-07-04
  • c++模拟实现string类详情
    目录一、string类简介二、模拟实现成员变量成员函数迭代器重载运算符[ ]三、几种常见函数reserve()resize()push_back()append()重载+=inser...
    99+
    2024-04-02
  • C++ STL容器详解之红黑树部分模拟实现
    目录一、红黑树的概念二、红黑树的性质三、红黑树节点的定义四、红黑树结构 五、 红黑树的插入操作六、代码总结一、红黑树的概念 红黑树(Red Black Tree),是在计算机科学中用...
    99+
    2024-04-02
  • C++模拟实现string的方法详解
    目录1.string 成员变量2.构造函数3.拷贝构造、赋值重载和析构函数1.拷贝构造2.赋值重载3.析构函数4.访问成员变量5.遍历1.下标+【】2.迭代器(iterator)3....
    99+
    2022-11-13
    C++实现string C++ string
  • C++string底层框架模拟实现代码
    目录一、 前言 二、 浅拷贝与深拷贝优缺点1. 浅拷贝2. 深拷贝3. 深拷贝现代版4. 写时拷贝三、 string框架搭建1. 框架定义2. 构造函数3. 析构函数4. 赋值重载5...
    99+
    2024-04-02
  • C++ STL容器中红黑树部分模拟实现的示例分析
    这篇文章主要介绍了C++ STL容器中红黑树部分模拟实现的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、红黑树的概念红黑树(Red Black Tree...
    99+
    2023-06-21
  • 【C++进阶(二)】STL大法--vector的深度剖析以及模拟实现
    💓博主CSDN主页:杭电码农-NEO💓   ⏩专栏分类:C++从入门到精通⏪   🚚代码仓库:NEO的学习日记🚚   ...
    99+
    2023-09-06
    c++ java 开发语言
  • 【C++杂货铺】探索string的底层实现
    文章目录 一、成员变量二、成员函数2.1 默认构造函数2.2 拷贝构造函数2.3 operator=2.4 c_str()2.5 size()2.6 operator[ ]2.7 itera...
    99+
    2023-09-04
    c++ 开发语言 热门 java
  • C++String部分成员模拟实现流程详解
    目录string类的成员设计普通构造函数的模拟拷贝构造函数的模拟赋值重载函数的模拟String的析构函数模拟补全上述的成员函数迭代器的简单模拟其他成员函数的模拟string类的成员设...
    99+
    2024-04-02
  • C++list的模拟实现
    目录一、节点的结构,list的迭代器的结构,以及list的结构1、节点的结构2、迭代器的结构3、list的结构二、迭代器的实现1、*运算符重载2、++ 与 --运算符3、->运...
    99+
    2023-05-16
    C++ list C++ list模拟实现
  • C++ 函数模板详解:直观理解 STL 的实现
    函数模板是一种 c++++ 机制,允许编写通用代码以适用于不同类型数据。它在 stl 中广泛使用,使容器和算法灵活、可重用。函数模板的语法为:template returntype fu...
    99+
    2024-04-28
    c++ 函数模板 标准库
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作