【C++】模拟实现string

简介: 【C++】模拟实现string

本章我们将模拟实现string类,但不一定非要与库中完全相同。我们将其中重要的、常用的接口进行模拟实现,旨在加深string类的学习与记忆。

🌛定义string类

为了区别于标准库中的string,这里使用自己的命名空间,在自己的命名空间模拟实现string


string类包含这三个基本成员:

  • char* _str 字符数组;
  • size_t _size 有效字符大小;
  • size_t _capacity 容量;


此外还需声明一个static成员npos,npos为将来实现的某些成员函数的缺省值,值位-1;

namespace yxb
{
  class string
  {
  public:
 
    private:
    char* _str;
    size_t _capacity;
    size_t _size;
        
        //类中声明
    static const size_t npos;
  };
 
        //类外定义
    const size_t string::npos = -1;
}


🌛构造函数

//构造函数
  string(const char* str = "")  //使用缺省值
    :_size(strlen(str))
  {
    _capacity = _size == 0 ? 3 : _size;  //_capacity初始值不能为0
    _str = new char[_capacity + 1];  //为'\0'预留位置
    strcpy(_str, str);
  }


注意

  • _capacity的值不能初始化为0,因为扩容时可能出现0*n=0的情况。

🌛拷贝构造函数


拷贝构造虽然编译器会自动实现,但是自动实现的拷贝构造为浅拷贝,对于string类中,成员变量会申请资源的情况,浅拷贝是行不通的,所以需要我们自己实现。

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


🌛赋值函数

  //s1 = s3   s1 = s1
    string& operator=(const string& s)
    {
      if (this != &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()
    {
      delete[] _str;
      _str = nullptr;
      _capacity = _size = 0; 
    }

🌛[]操作符重载

注意应对const对象与非const对象须实现不同的重载函数。

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

🌛c_str、size、capacity函数

  • c_str:返回C风格的字符串。
  • size:返回_size;
  • capacity:返回_capacity;
  const char* c_str()
  {
    return _str;
  }
  
  size_t size() const
  {
    return _size;
  }
 
  size_t capacity() const
  {
    return _capacity;
  }


🌛比较运算符重载

  //比较运算符重载
  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;
  }
 
  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);
  }

🌛resize与reserve函数

  • resize:扩容并初始化;
  • reserve:只扩容;
//扩容
    void reverse(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')
    {
      if (n <= _size)
      {
        //删除数据,保留前n个
        _size = n;
        _str[_size] = '\0';
      }
      else
      {
        if (n > _capacity)
        {
          reverse(n);
        }
        size_t i = _size;
        while (i < n)
        {
          _str[i++] = ch;
        }
        _size = n;
        _str[_size] = '\0';
      }
    }


🌛push_back、append函数


  • push_back:尾插一个字符;
  • append:尾插一个字符串;
  • +=:尾插一个字符或字符串;
 
    void push_back(char ch)
    {
      if (_size + 1 > _capacity)
      {
        reverse(_capacity * 2);
      }
      _str[_size] = ch;
      _size++;
      _str[_size] = '\0';
    }
 
    void append(const char* str)
    {
      size_t len = strlen(str);
      if (_size + len > _capacity)
      {
        reverse(_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;
    }

🌛insert函数

  • insert:在pos位置插入一个字符或字符串;
  void insert(size_t pos, char ch)
    {
      assert(pos <= _size);
      if (_size + 1 > _capacity)
      {
        reverse(2 * _capacity);
      }
      size_t end = _size + 1;
      while (pos < end)
      {
        _str[end] = _str[end - 1];
        end--;
      }
      _str[pos] = ch;
      _size++;
    }
 
    void insert(size_t pos, const char* str)
    {
      assert(pos <= _size);
      size_t len = strlen(str);
      
      if (_size + len > _capacity)
      {
        reverse(_size + len);
      }
      
      //挪动数据
      size_t end = _size + len;
      while ( end > pos +len-1 )
      {
        _str[end] = _str[end-len];
          end--;
      }
      //拷贝插入 
      strncpy(_str + pos, str, len);
      _size += len;
    }

🌛erase函数

  • erase:删除pos位置向后的n个字符;
  void erase(size_t pos, size_t len = npos)
    {
      if (len == npos || pos + len >= _size)
      {
        _str[pos] = '\0';
        _size = pos;
      }
      //0123456789
      else
      {
        strcpy(_str + pos, _str + pos + len);
        _size -= pos;
      }
    }


🌛find函数

  • find:从pos位置开始向后查找指定字符或字符串,并返回起始位置的下标。
  size_t find(char ch, size_t pos = npos)
  {
    assert(pos < _size);
 
    for (size_t i = pos; i < _size; ++i)
    {
      if (_str[i] == ch)
      {
        return i;
      }
    }
    return npos;
  }
 
  size_t find(const char* str, size_t pos = npos)
  {
    assert(pos < _size);
 
    char* p = strstr(_str, str);
    if (p == nullptr)
    {
      return npos;
    }
    else
    {
      return p - str;
    }
  }

🌛swap函数

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

🌛clean函数

  • clean:清理数据;
  void clean()
  {
    _str[0] = '\0';
    _size = 0;
  }


🌛迭代器

  //迭代器
  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;
  }


🌛>> 与 << 重载

注意这两个函数须定义在类外。

    ostream& operator<<(ostream& out, const string& str)
  {
    for (auto ch : str)
    {
      out << ch;
    }
    
    return out;
  }
 
  istream& operator>>(istream& in, string& str)
  {
    str.clean();
 
    char ch = in.get();
    char buff[128]; //避免因频繁扩容导致效率过低
    size_t i = 0;
    while (ch != ' ' && ch != '\n')
    {
      buff[i++] = ch;
      if (i == 127)
      {
        buff[127] = '\0';
        str += buff;
        i = 0;
      }
      ch = in.get();
    }
 
    if (i != 0)
    {
      buff[i] = '\0';
      str += buff;
    }
 
    return in;
  }

🌛完整代码

#pragma once
#include<assert.h>
 
namespace yxb
{
  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()
      :_str(new char[1])
      , _size(0)
      ,_capacity(0)
    {
      _str[0] = '\0';
    }*/
 
    string(const char* str = "")
      :_size(strlen(str))
    {
      _capacity = _size == 0 ? 3 : _size;
      _str = new char[_capacity + 1];
      strcpy(_str, str);
    }
 
    //拷贝构造s3(s2)
    string(const string& s)
      :_size(s._size)
      ,_capacity(s._capacity)
    {
      _str = new char[s._capacity + 1];
      strcpy(_str, s._str);
    }
 
    //s1 = s3   s1 = s1
    string& operator=(const string& s)
    {
      if (this != &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()
    {
      delete[] _str;
      _str = nullptr;
      _capacity = _size = 0; 
    }
 
    const char* c_str()
    {
      return _str;
    }
 
    char& operator[](size_t pos)
    {
      assert(pos < _size);
      return _str[pos];
    }
 
    const char& operator[](size_t pos)const
    {
      assert(pos < _size);
      return _str[pos];
    }
 
    size_t size()const
    {
      return _size;
    }
 
    size_t capacity()const
    {
      return _capacity;
    }
 
    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);
    }
    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 reverse(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')
    {
      if (n <= _size)
      {
        //删除数据,保留前n个
        _size = n;
        _str[_size] = '\0';
      }
      else
      {
        if (n > _capacity)
        {
          reverse(n);
        }
        size_t i = _size;
        while (i < n)
        {
          _str[i++] = ch;
        }
        _size = n;
        _str[_size] = '\0';
      }
    }
 
    void push_back(char ch)
    {
      if (_size + 1 > _capacity)
      {
        reverse(_capacity * 2);
      }
      _str[_size] = ch;
      _size++;
      _str[_size] = '\0';
    }
 
    void append(const char* str)
    {
      size_t len = strlen(str);
      if (_size + len > _capacity)
      {
        reverse(_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;
    }
 
    void insert(size_t pos, char ch)
    {
      assert(pos <= _size);
      if (_size + 1 > _capacity)
      {
        reverse(2 * _capacity);
      }
      size_t end = _size + 1;
      while (pos < end)
      {
        _str[end] = _str[end - 1];
        end--;
      }
      _str[pos] = ch;
      _size++;
    }
 
    void insert(size_t pos, const char* str)
    {
      assert(pos <= _size);
      size_t len = strlen(str);
      
      if (_size + len > _capacity)
      {
        reverse(_size + len);
      }
      
      //挪动数据
      size_t end = _size + len;
      while ( end > pos +len-1 )
      {
        _str[end] = _str[end-len];
          end--;
      }
      //拷贝插入 
      strncpy(_str + pos, str, len);
      _size += len;
    }
 
    void erase(size_t pos, size_t len = npos)
    {
      if (len == npos || pos + len >= _size)
      {
        _str[pos] = '\0';
        _size = pos;
      }
      //0123456789
      else
      {
        strcpy(_str + pos, _str + pos + len);
        _size -= pos;
      }
    }
    size_t find(char ch, size_t pos = npos)
    {
      assert(pos < _size);
 
      for (size_t i = pos; i < _size; ++i)
      {
        if (_str[i] == ch)
        {
          return i;
        }
      }
      return npos;
    }
 
    size_t find(const char* str, size_t pos = npos)
    {
      assert(pos < _size);
 
      char* p = strstr(_str, str);
      if (p == nullptr)
      {
        return npos;
      }
      else
      {
        return p - str;
      }
    }
 
    void swap(string& s)
    {
      std::swap(_str, s._str);
      std::swap(_size, s._size);
      std::swap(_capacity, s._capacity);
    }
    void clean()
    {
      _str[0] = '\0';
      _size = 0;
    }
 
 
  private:
    char* _str;
    size_t _capacity;
    size_t _size;
 
    static const size_t npos;
  };
 
  const size_t string::npos = -1;
 
  ostream& operator<<(ostream& out, const string& str)
  {
    for (auto ch : str)
    {
      out << ch;
    }
 
    return out;
  }
 
  istream& operator>>(istream& in, string& str)
  {
    str.clean();
 
    char ch = in.get();
    char buff[128]; //避免因频繁扩容导致效率过低
    size_t i = 0;
    while (ch != ' ' && ch != '\n')
    {
      buff[i++] = ch;
      if (i == 127)
      {
        buff[127] = '\0';
        str += buff;
        i = 0;
      }
      ch = in.get();
    }
 
    if (i != 0)
    {
      buff[i] = '\0';
      str += buff;
    }
 
    return in;
  }
 
  void test_string1()
  { 
    string s1;
    string s2("hello world");
    cout << s1.c_str() << endl;
    cout << s2.c_str() << endl;
    s2[0]++;
    cout << s2.c_str() << endl;
 
  }
  void test_string2()
  {
    string s1;
    string s2("hello world");
    cout << s1.c_str() << endl;
    cout << s2.c_str() << endl;
    s1 = s2;
    cout << s1.c_str() << endl;
    cout << s2.c_str() << endl;
  }
  void test_string3()
  {
    string s1;
    string s2("hello world");
    cout << s1.c_str() << endl;
    cout << s2.c_str() << endl;
    
    string::iterator it = s2.begin();
    while (it != s2.end())
    {
      cout << *it << endl;
      ++it;
    }
  }
  void test_string4()
  {
    string s1;
    string s2("hello world");
    string s3("hello");
 
    cout << s1.c_str() << endl;
    cout << s2.c_str() << endl;
    cout << (s3 >= s2) << endl;
  }
  void test_string5()
  {
    string s1;
    string s2("hello world");
    s2.append("yyyyyy");
    cout << s2.c_str() << endl;
  }
  void test_string6()
  {
    string s1("hhh ");
    cout << s1.c_str() << endl;
    s1.resize(10, 'x');
    cout << s1.c_str() << endl;
    s1.resize(100, 'y');
    cout << s1.c_str() << endl;
  }
  void test_string7()
  {
    string s1("123456789");
    cout << s1.c_str() << endl;
 
    s1.insert(2, "wwww");
    cout << s1.c_str() << endl;
 
    s1.insert(0, "ss");
    cout << s1.c_str() << endl;
 
  }
  void test_string8()
  {
    string s1("123456789");
    cout << s1.c_str() << endl;
 
    s1.erase(4, 2);
    cout << s1.c_str() << endl;
 
  }
 
  void test_string9()
  {
    string s1;
    cin >> s1;
    cout << s1 << endl;
  }
}




相关文章
|
27天前
|
C++ 容器
|
17天前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
17 1
|
27天前
|
C++ 容器
|
27天前
|
C++ 容器
|
27天前
|
存储 C++ 容器
|
1月前
|
安全 C语言 C++
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
32 4
|
1月前
|
存储 编译器 程序员
【C++篇】手撕 C++ string 类:从零实现到深入剖析的模拟之路
【C++篇】手撕 C++ string 类:从零实现到深入剖析的模拟之路
58 2
|
1月前
|
编译器 C语言 C++
【C++】C++ STL 探索:String的使用与理解(三)
【C++】C++ STL 探索:String的使用与理解
|
1月前
|
存储 编译器 C++
【C++】C++ STL 探索:String的使用与理解(二)
【C++】C++ STL 探索:String的使用与理解
|
1月前
|
编译器 C语言 C++
【C++】C++ STL 探索:String的使用与理解(一)
【C++】C++ STL 探索:String的使用与理解