C++之模拟实现string(上)

简介: C++之模拟实现string(上)

前言

因为学习了string的相关知识,了解了string大部分接口的底层实现原理,所以我决定自己模拟实现一个mini版的string类,用来加深对string各方面知识的理解。

如果有错误或不足之处,还望各位读者小伙伴们指出。


一、包含的相关头文件

#include<iostream>
#include<assert.h>
#include<cstring>
using std::ostream;
using std::istream;
using std:: cout;
using std:: endl;

二、构造和析构

1.构造函数

//构造
    string(const char* str = "")
    {
      size_t len = strlen(str);
      _capacity = _size = len;
      _str = new char[_capacity + 1];
      strcpy(_str, str);
    }

2.拷贝构造

1.传统写法

该对象自己一点一点的进行深拷贝

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

2.现代写法

找一个中间对象,让这个中间对象用参数的值进行直接构造,再将这个中间对象的内容与自己的内容进行交换。相较于传统写法,现代写法更加简洁。

//拷贝构造
    string(const string& s)
      :_str(nullptr),//此处要注意将该对象的地址赋值为nullptr,否则析构中间对象时会因为发生野指针的访问而导致程序崩溃。
      _size(0),
      _capacity(0)
    {
      string temp(s);
      swap(temp);
    }

此处的swap用的是string自己实现的swap,为什么不用库里的swap呢?因为库里的swap是进行深拷贝,会降低效率。我们自己实现的swap只需要进行浅拷贝,即将它们对应的资源进行交换即可。

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

3.赋值运算符重载

1.传统写法

//赋值运算符重载
    string& operator=(const string &s)
    {
      if (this != &s)
      {
        char* temp = new char[s._capacity + 1];//用一个中间值记录新开辟的空间,如果开辟空间成功,再将该空间的地址给_str。避免因为开辟空间失败,_str原本的内容也销毁的情况发生。
        strcpy(temp, s._str);
        delete[] _str;
        _str = temp;
        _capacity = s._capacity;
        _size = s._size;
      }
      return *this;
    }

2.现代写法

与拷贝构造的现代写法思想类似,可以使程序更加简洁。并且此处不需要专门定义一个中间对象,因为传值传参传过来的形参是实参拷贝构造出来的对象,它就是一个很好的中间对象,我们直接和它进行交换即可。

//赋值运算符重载
    string& operator=(string s)
    {
      swap(s);
      return *this;
    }

4.析构函数

//析构
    ~string()
    {
      delete[] _str;
      _str = nullptr;//释放指针所指向的空间后要将该指针置为空(避免出现野指针的非法访问)
      _size = _capacity = 0;
    }

三、iterator

迭代器是一个使用起来像指针的东西,实际上string的迭代器就是char*类型的指针,因此我们先在最开始进行typedef

typedef char* iterator;

定义两个迭代器:分别指向字符串开头和结尾

iterator begin()
    {
      return _str;
    }
    iterator end()
    {
      return _str + _size;
    }

四、modify

1.push_back(尾插一个字符)

string只提供了push_back的接口没有提供头插的接口,因为头插需要移动数据,效率很低,所以尽量不要在string中使用头插。

void push_back(char c)
    {
      if (_size == _capacity)
      {
        size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;//要考虑到如果初始字符串的容量为0的情况
        reserve(newcapacity);
        _capacity = newcapacity;
      }
      _str[_size++] = c;
      _str[_size] = '\0';
    }

2.append(尾插一个字符串)

void append(const char* str)
    {
      size_t len = strlen(str);
      if (len + _size > _capacity)
      {
        reserve(len + _size);
      }
      strcpy(end(), str);
      _size += len;
    }

3.运算符重载+=

1.尾插字符

尾插一个字符(复用尾插)

string& operator+=(char c)
    {
      push_back(c);
      return *this;
    }

2.尾插字符串

尾插一个字符串(复用append)

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

4.clear

清除string中所有元素

void clear()
    {
      _size = 0;
      _str[0] = '\0';
    }

5.insert

1.插入一个字符

在字符串某位置插入一个字符:

// 在pos位置上插入字符c/字符串str
    string& insert(size_t pos, char c)
    {
      assert(pos <= _size);
      if (_size + 1 > _capacity)
      {
        reserve(_size + 1);
      }
      for (size_t i = _size + 1; i > pos; --i)//要注意避免i减为-1变成一个极大的无符号数,导致死循环
      {
        _str[i] = _str[i - 1];
      }
      _str[pos] = c;
      ++_size;
      return *this;
    }

2.插入一个字符串

在字符串某位置插入一个字符串:

string& insert(size_t pos, const char* str)
    {
      assert(pos <= _size);
      size_t i = 0;
      size_t len = strlen(str);
      if (_size + len > _capacity)
      {
        reserve(_size + len);
      }
      for (i = _size + len; i > pos + len - 1; --i)
      {
        _str[i] = _str[i - len];
      }
      strncpy(_str + pos, str, len);
      _size += len;
      return *this;
    }

6.erase

删除从字符串某位置开始,某长度的子串(如果不说明删除子串的长度,则默认从开始位置到最后的所有内容都删除)

// 删除pos位置上的元素
    string& erase(size_t pos, size_t len = npos)
    {
      if (len == npos || pos + len > _size)
      {
        _str[pos] = '\0';
        _size = pos;
      }
      else
      {
        strcpy(_str + pos, _str + pos + len);
        _size -= len;
      }
      return *this;
    }


相关文章
|
2月前
|
C语言 C++ 容器
【c++丨STL】string模拟实现(附源码)
本文详细介绍了如何模拟实现C++ STL中的`string`类,包括其构造函数、拷贝构造、赋值重载、析构函数等基本功能,以及字符串的插入、删除、查找、比较等操作。文章还展示了如何实现输入输出流操作符,使自定义的`string`类能够方便地与`cin`和`cout`配合使用。通过这些实现,读者不仅能加深对`string`类的理解,还能提升对C++编程技巧的掌握。
89 5
|
2月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
69 2
|
3月前
|
C++ 容器
|
3月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
35 1
|
3月前
|
C++ 容器
|
3月前
|
C++ 容器
|
3月前
|
存储 C++ 容器
|
3月前
|
安全 C语言 C++
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
59 4
|
3月前
|
存储 编译器 程序员
【C++篇】手撕 C++ string 类:从零实现到深入剖析的模拟之路
【C++篇】手撕 C++ string 类:从零实现到深入剖析的模拟之路
87 2
|
3月前
|
编译器 C语言 C++
【C++】C++ STL 探索:String的使用与理解(三)
【C++】C++ STL 探索:String的使用与理解