【C++】-- STL之String模拟实现(一)

简介: 【C++】-- STL之String模拟实现

一、String类实现

为了和库里面的string 区分开,使用命名空间delia将 string类和库里string隔离开

string类有3个成员变量:_str字符串内容、_size字符串大小、_capacity字符串容量

1. namespace delia
2. {
3.  class string
4.  {
5.     private:
6.         char* _str;
7.      size_t _size;
8.      size_t _capacity;
9.     }
10. }

string的模拟实现包括:

1.string类的构造

分配空间时,要多分配1个字节的空间,这1个字节是留给'\0'的,_size最大为申请字节数-1,_capacity最大也为申请字节数-1

1.         //构造函数
2.    string(const char* str = "")
3.    {
4.      _str = new char[strlen(str) + 1];
5.      strcpy(_str, str);
6. 
7.      _size = strlen(str);
8.      _capacity = _size;
9.    }

 

2.swap()

使用库里的swap函数交换*this和s的内容:包括_str字符串内容、_size字符串大小和_capacity字符串容量

1.         //实现交换函数
2.    void swap(string& s)
3.    {
4.             //用::指定调用全局的swap函数即库里的swap函数
5.      ::swap(_str, s._str);
6.      ::swap(_size, s._size);
7.      ::swap(_capacity, s._capacity);
8.    }

3.拷贝构造

(1)先使用s._str作为参数构造一个临时对象

(2)将临时对象tmp的内容和*this进行交换

注意:必须要在初始化列表进行初始化 ,否则交换完毕后tmp出了拷贝构造函数作用域会调用析构函数释放tmp在堆上申请的空间,因为如果_str没有被初始化为空指针,那么_str就是随机值,交换后,tmp对象的成员_str也是随机值,随机值的空间时不可以被释放的,会导致不可预知的错误;空指针时可以被释放的,因此_str必须被初始化为空指针。

1. //现代拷贝构造
2.    string(const string& s)
3.      :_str(nullptr)
4.      , _size(0)
5.      , _capacity(0)
6.    {
7.      string tmp(s._str);
8.      swap(tmp);
9.    }

4.赋值运算符重载

使用swap()函数将*this的内容和s进行交换

1.         //赋值运算符重载
2.    string& operator=(string s)
3.    {
4.      swap(s);
5.      return *this;
6.    }

 

5.析构

释放_str在对上申请的空间、将_size和_capacity置0

1.      //析构函数
2.    ~string()
3.    {
4.      delete[] _str;
5.      _str = nullptr;
6.      _size = 0;
7.      _capacity = 0;
8.    }

 

6.迭代器

分两种:普通迭代器(可读可写)和const迭代器(只读)

(1)普通迭代器

1.         //迭代器
2.    iterator begin()
3.    {
4.      return _str;
5.    }
6. 
7.    //迭代器
8.    iterator end()
9.    {
10.       return _str + _size;
11.     }

(2)const迭代器

1. //const迭代器
2.    iterator begin() const
3.    {
4.      return _str;
5.    }
6. 
7.    //const迭代器
8.    iterator end() const
9.    {
10.       return _str + _size;
11.     }

 

7.operator[ ]

也分为普通operator[ ](可读可写)和const operator[ ](只读)

char要加&,如果不加,那么return就是传值返回,返回的是_str[i]的拷贝,即临时对象,而临时对象具有常性,不能被修改,只能读;如果要对_str进行修改的话,char要加&,用传引用返回

1.      //const string对象遍历,只读
2.    char& operator[](size_t i)  const
3.    {
4.      assert(i < _size);
5.      return _str[i];
6.    }
7. 
8.    //非const string对象遍历,可读可写
9.    char& operator[](size_t i)
10.     {
11.       assert(i < _size);
12.       return _str[i];
13.     }

 

8.size()

1.      //求string对象大小
2.    size_t size() const
3.    {
4.      return strlen(_str);
5.    }

 

9.c_str()

获取c形式字符串,将 const string* 类型 转化为 const char* 类型

1.      const char* c_str()
2.    {
3.      return _str;
4.    }

 10.reserve()

开空间,扩展_capacity,_size不变

(1)申请新空间

(2)拷贝字符串

(3)释放旧空间

(4)指向新空间

(5)更新容量

1.         void reserve(size_t n)
2.    {
3.      if (n > _capacity)
4.      {
5.        char* tmp = new char[n + 1];//1.申请新空间,多开的那一个空间,存放\0
6.        strncpy(tmp, _str, _size + 1);//2.将字符串包含\0在内都拷贝到新空间
7.        delete[] _str;//3.释放旧空间
8. 
9.        _str = tmp;//4.指向新空间
10.         _capacity = n;//5.更新容量
11.       }
12.     }

11.resize()

开空间+初始化,扩展_capacity,_size也要修改

(1)当n<_size,无需增容

(2)当n>_size,分两种情况

           ①_size < n <_capacity,无需增容,直接将_size到n位置置为val

           ②n >_capacity,需增容,并将_size到n位置置为val

         n>_size的这两种情况只有是否增容的区别,其他没有区别,可以合二为一

1.         void resize(size_t n, char val = '\0')//如果没有显式给出val,val为\0
2.    {
3.      //1.当n<_size,无需增容
4.      if (n < _size)
5.      {
6.        _size = n;//直接更新_size
7.        _str[_size] = '\0';//将_size位置置为\0
8.      }
9.      //2.当n>_size,分两种情况
10.       //(1)_size < n <_capacity,无需增容,直接将_size到n位置置为val
11.       //(2)n >_capacity,需增容,并将_size到n位置置为val
12.       //这两种情况只有是否增容的区别,其他没有区别
13.       else
14.       {
15.         if (n > _capacity)
16.         {
17.           reserve(n);
18.         }
19. 
20.         for (size_t i = _size; i < n; i++)
21.         {
22.           _str[i] = val;
23.         }
24. 
25.         _str[n] = '\0';//将n的位置置\0
26.         _size = n;//更新_size
27.       }
28.     }

 

12.push_back()

要考虑是否需要开空间

1.         //尾插一个字符
2.    void push_back(char ch)
3.    {
4.      //字符个数已经达到容量时,重新开空间
5.      if (_size == _capacity)
6.      {
7.        reserve(_capacity == 0 ? 4 : _capacity * 2);//如果是第一次开空间,就开4个字节,如果不是第一次开空间,就开成原来的2倍容量
8.      }
9. 
10.       _str[_size] = ch;//将字符ch缀到字符串末尾
11.       _str[_size + 1] = '\0';//字符ch后面加\0,表明字符串结束
12.       _size++;//字符串大小+1
13.     }

13.append()

将str内容copy至this._str+_size(*this._str的末尾)位置

 

1.      //追加字符串
2.    void append(const char* str)
3.    {
4.      size_t len = _size + strlen(str);//函数执行完毕后字符串的大小
5. 
6.      if (len > _capacity)
7.      {
8.        reserve(len);//空间不够,增容
9.      }
10. 
11.       strcpy(_str + _size, str);//将str内容拷贝至_str末尾
12.       _size = len;//更新_size
13.     }

 

相关文章
|
2天前
|
C++
模拟实现c++中的string
模拟实现c++中的string
|
24天前
|
C++ 容器
【c++丨STL】stack和queue的使用及模拟实现
本文介绍了STL中的两个重要容器适配器:栈(stack)和队列(queue)。容器适配器是在已有容器基础上添加新特性或功能的结构,如栈基于顺序表或链表限制操作实现。文章详细讲解了stack和queue的主要成员函数(empty、size、top/front/back、push/pop、swap),并提供了使用示例和模拟实现代码。通过这些内容,读者可以更好地理解这两种数据结构的工作原理及其实现方法。最后,作者鼓励读者点赞支持。 总结:本文深入浅出地讲解了STL中stack和queue的使用方法及其模拟实现,帮助读者掌握这两种容器适配器的特性和应用场景。
55 21
|
2月前
|
编译器 C语言 C++
【c++丨STL】list模拟实现(附源码)
本文介绍了如何模拟实现C++中的`list`容器。`list`底层采用双向带头循环链表结构,相较于`vector`和`string`更为复杂。文章首先回顾了`list`的基本结构和常用接口,然后详细讲解了节点、迭代器及容器的实现过程。 最终,通过这些步骤,我们成功模拟实现了`list`容器的功能。文章最后提供了完整的代码实现,并简要总结了实现过程中的关键点。 如果你对双向链表或`list`的底层实现感兴趣,建议先掌握相关基础知识后再阅读本文,以便更好地理解内容。
42 1
|
2月前
|
算法 C语言 C++
【c++丨STL】list的使用
本文介绍了STL容器`list`的使用方法及其主要功能。`list`是一种双向链表结构,适用于频繁的插入和删除操作。文章详细讲解了`list`的构造函数、析构函数、赋值重载、迭代器、容量接口、元素访问接口、增删查改操作以及一些特有的操作接口如`splice`、`remove_if`、`unique`、`merge`、`sort`和`reverse`。通过示例代码,读者可以更好地理解如何使用这些接口。最后,作者总结了`list`的特点和适用场景,并预告了后续关于`list`模拟实现的文章。
69 7
|
3月前
|
存储 编译器 C语言
【c++丨STL】vector的使用
本文介绍了C++ STL中的`vector`容器,包括其基本概念、主要接口及其使用方法。`vector`是一种动态数组,能够根据需要自动调整大小,提供了丰富的操作接口,如增删查改等。文章详细解释了`vector`的构造函数、赋值运算符、容量接口、迭代器接口、元素访问接口以及一些常用的增删操作函数。最后,还展示了如何使用`vector`创建字符串数组,体现了`vector`在实际编程中的灵活性和实用性。
135 4
|
3月前
|
C语言 C++ 容器
【c++丨STL】string模拟实现(附源码)
本文详细介绍了如何模拟实现C++ STL中的`string`类,包括其构造函数、拷贝构造、赋值重载、析构函数等基本功能,以及字符串的插入、删除、查找、比较等操作。文章还展示了如何实现输入输出流操作符,使自定义的`string`类能够方便地与`cin`和`cout`配合使用。通过这些实现,读者不仅能加深对`string`类的理解,还能提升对C++编程技巧的掌握。
140 5
|
3月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
89 2
|
2月前
|
存储 编译器 C语言
【c++丨STL】vector模拟实现
本文深入探讨了 `vector` 的底层实现原理,并尝试模拟实现其结构及常用接口。首先介绍了 `vector` 的底层是动态顺序表,使用三个迭代器(指针)来维护数组,分别为 `start`、`finish` 和 `end_of_storage`。接着详细讲解了如何实现 `vector` 的各种构造函数、析构函数、容量接口、迭代器接口、插入和删除操作等。最后提供了完整的模拟实现代码,帮助读者更好地理解和掌握 `vector` 的实现细节。
67 0
|
5月前
|
Java 索引
java基础(13)String类
本文介绍了Java中String类的多种操作方法,包括字符串拼接、获取长度、去除空格、替换、截取、分割、比较和查找字符等。
59 0
java基础(13)String类
|
4月前
|
Java
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
本文深入探讨了Java中方法参数的传递机制,包括值传递和引用传递的区别,以及String类对象的不可变性。通过详细讲解和示例代码,帮助读者理解参数传递的内部原理,并掌握在实际编程中正确处理参数传递的方法。关键词:Java, 方法参数传递, 值传递, 引用传递, String不可变性。
91 1
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性