【C++从0到王者】第十站:手撕string(下)

简介: 【C++从0到王者】第十站:手撕string

十四、比较大小

1.operator<

如下代码所示,我们先进行依次比较,若前面可以分出胜负自然好说,但若分不出胜负,那么就只有一种情况是满足小于的情况,即第一个没有值了,第二个还有值,其余皆为不满足小于的情况

bool operator<(const string& s) const
    {
      size_t i1 = 0;
      size_t i2 = 0;
      while (i1 < _size && i2 < s._size)
      {
        if (_str[i1] < s._str[i2])
        {
          return true;
        }
        else if (_str[i1] > s._str[i2])
        {
          return false;
        }
        else
        {
          i1++;
          i2++;
        }
      }
      return (i1 == _size) && (i2 != s._size);
      //return _size<s._size;
    }

上面是我们自己手撕的代码,同样的我们也可以利用c语言中的一些库来更方便的完成这个代码

下面这串代码虽然短小,但是可能不是很好理解,我们第一行的目的就是先求出两个对象最小的size,然后让两个对象的前size个进行比较,如果相等的话,那么ret就是0,我们就只需要确认一下第一个的size是否小于第二个的size即可。如果不相等,那么我们看ret是否小于0,小于既是正确的

bool operator<(const string& s) const
{
  int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);
  return ret == 0 ? _size < s._size : ret < 0;
}

2.operator==(const string& s)

这个就比较简单了,我们先比较size,如果相等的条件下,两个串也相等,那么就是相等了

bool operator==(const string& s) const
    {
      return _size == s._size && (memcmp(_str, s._str, _size) == 0);
    }

3.其他比较

解决了前两个,其实其他的就可以直接进行复用了,如下代码所示

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);
    }

十五、赋值运算符重载

1.传统写法

如下所示,是我们比较传统的写法,它跟拷贝构造函数是差不多的,我们不管原来的是什么样子,反正总归最后基本就是把右边的复制了一份。那么我们就直接开空间,释放原来的空间,随之拷贝数据即可。它与拷贝构造函数的区别就在于,它是已经存在的两个变量之间的赋值拷贝,这个过程是需要释放掉被赋值的对象原来的数据的。而拷贝构造函数是用一个已经存在的去构造一个不存在的,以前这个也不存在,所以就不需要释放空间。

总归就是一个深拷贝的写法

//赋值运算符重载
    string& operator=(const string& s)
    {
      if (this != &s)
      {
        char* tmp = new char[s._capacity + 1];
        delete[] _str;
        _str = tmp;
        memcpy(_str, s._str, s._size + 1);
        _size = s._size;
        _capacity = _capacity;
      }
      return *this;
    }

2.现代写法

如下代码所示,这段代码就充分利用了拷贝构造函数,然后移花接木即可,顺便原来的空间也可以因为tmp的析构而带走,充分的榨干了tmp的作用。

string& operator=(const string& s)
    {
      if (this != &s)
      {
        string tmp(s);
        std::swap(_str, tmp._str);
        std::swap(_size, tmp._size);
        std::swap(_capacity, tmp._capacity);
      }
      return *this;
    }

但是要注意,我们不可以这样写:

程序会崩溃的

这是因为swap函数中是通过三次赋值操作来实现的,而赋值操作又通过swap来实现,实现了死递归。导致栈溢出了

事实上,库里面也自己提供了两个string对象的交换

而我们如果需要自己手动实现swap的话,也正好就是前面的代码

void swap(string& s)
    {
      std::swap(_str, s._str);
      std::swap(_size, s._size);
      std::swap(_capacity, s._capacity);
    }
    string& operator=(const string& s)
    {
      if (this != &s)
      {
        string tmp(s);
        swap(tmp);
      }
      return *this;
    }

上面这段代码还可以继续进行简化

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

这段代码我们就厉害了,直接在传参的过程中调用拷贝构造,可谓精简到极致了

十六、拷贝构造的现代写法

如下代码所示,是我们较为传统的写法,也是上面的写法。

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

上面这段代码,其实也可以进一步的进行简化。即现代写法

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

注意在这段代码中,初始化列表必须得写法,否则在调用赋值运算符重载的过程中,由于this所指向的对象未初始化,导致其的_str随意指向一块空间,交换以后,析构的过程中直接崩溃。故必须得加初始化

其次在这段代码中,还有一个问题是如果s中的_str中间有一个’\0’字符,那么也会出现问题。导致后面的部分无法拷贝上去。

综上所述,对于拷贝构造函数,还是传统写法更优一些

十七、string模拟实现完整代码

下面是类实现的具体代码

#pragma once
#include<iostream>
#include<string.h>
#include<assert.h>
using namespace std;
namespace String
{
  class string
  {
  public :
    /*********************************************/
    //迭代器
    typedef char* iterator;
    iterator begin()
    {
      return _str;
    }
    iterator end()
    {
      //return _str + strlen(_str);
      return _str + _size;
    }
    //const 迭代器
    typedef const char* const_iterator;
    const_iterator begin() const
    {
      return _str;
    }
    const_iterator end() const
    {
      //return _str + strlen(_str);
      return _str + _size;
    }
    /**************************************************************************************/
    //分开的构造函数写法
    给一个字符串去构造一个string
    //string(const char* str)
    //  :_str(new char[strlen(str) + 1])
    //  , _size(strlen(str))
    //  , _capacity(strlen(str))
    //{
    //  strcpy(_str, str);
    //}
    无参的默认构造函数
    //string()
    //  :_str(new char[16])
    //  ,_size(0)
    //  ,_capacity(15)
    //{
    //  memcpy(_str, "\0\0\0\0", 4);
    //}
    /**************************************************************************************/
    //以上合二为一的构造函数写法
    string(const char* str = "")
      :_str(new char[strlen(str) + 1])
      , _size(strlen(str))
      , _capacity(strlen(str))
    {
      //strcpy(_str, str);
      memcpy(_str, str, _size + 1);
    }
    //拷贝构造函数
    string(const string& s)
    {
      _str = new char[s._capacity + 1];
      memcpy(_str, s._str, s._size + 1);
      _size = s._size;
      _capacity = s._capacity;
    }
    //string(const string& s)
    //  :_str(nullptr)
    //  ,_size(0)
    //  ,_capacity(0)
    //{
    //  string tmp(s._str);
    //  swap(tmp);
    //}
    /**************************************************************************************/
    //析构函数
    ~string()
    {
      delete[] _str;
      _str = nullptr;
      _size = 0;
      _capacity = 0;
    }
    /**************************************************************************************/
    //获取string中的字符串
    const char* c_str() const 
    {
      return _str;
    }
    /**************************************************************************************/
    //获取当前有效元素个数
    size_t size() const 
    {
      return _size;
    }
    /**************************************************************************************/
    //operator[]
    //读写接口
    char& operator[](size_t pos)
    {
      assert(pos < _size);
      return _str[pos];
    }
    //只读接口
    const char& operator[](size_t pos) const
    {
      assert(pos < _size);
      return _str[pos];
    }
    /**************************************************************************************/
    //增删查改之增
    void reserve(size_t n)
    {
      if (n > _capacity)
      {
        char* tmp = new char[n + 1];
        //strcpy(tmp, _str);
        memcpy(tmp, _str, _size + 1);
        _capacity = n;
        delete[] _str;
        _str = tmp;
      }
    }
    //pushback
    void pushback(char ch)
    {
      if (_size == _capacity)
      {
        int newcapacity = _capacity == 0 ? 4 : _capacity * 2;
        reserve(newcapacity);
      }
      _str[_size++] = ch;
      _str[_size] = '\0';
    }
    void append(const char* str)
    {
      int len = strlen(str);
      if (len + _size > _capacity)
      {
        int newcapacity = (len+_size) ;
        reserve(newcapacity);
      }
      //while (len--)
      //{
      //  _str[_size++] = *str++;
      //}
      //_str[_size] = '\0';
      //strcpy(_str + _size, str);
      memcpy(_str + _size, str, len + 1);
      _size += len;
    }
    string& operator+=(char ch)
    {
      pushback(ch);
      return *this;
    }
    string& operator+=(const char* str)
    {
      append(str);
      return *this;
    }
    void insert(size_t pos, size_t n, char ch)
    {
      assert(pos <= _size);
      if (_size + n > _capacity)
      {
        int newcapacity = _size + n;
        reserve(newcapacity);
      }
      int count = _size - pos + 1;
      int size = _size;
      while (count--)
      {
        _str[size + n] = _str[size];
        size--;
      }
      int i = pos;
      for (i = pos; i < pos + n; i++)
      {
        _str[i] = ch;
      }
      _size += n;
    }
    void insert(size_t pos, const char* str)
    {
      assert(pos <= _size);
      int len = strlen(str);
      if (_size + len > _capacity)
      {
        int newcapacity = _size + len;
        reserve(newcapacity);
      }
      int count = _size - pos + 1;
      int size = _size;
      while (count--)
      {
        _str[size + len] = _str[size];
        size--;
      }
      int i = pos;
      for (i = pos; i < pos + len; i++)
      {
        _str[i] = *str;
        str++;
      }
      _size += len;
    }
    void insert1(size_t pos, size_t n, char ch)
    {
      assert(pos <= _size);
      if (_size + n > _capacity)
      {
        int newcapacity = _size + n;
        reserve(newcapacity);
      }
      int end = _size;
      while (end >= (int)pos)
      {
        _str[end + n] = _str[end];
        end--;
      }
      int i = pos;
      for (i = pos; i < pos + n; i++)
      {
        _str[i] = ch;
      }
      _size += n;
    }
    void insert2(size_t pos, size_t n, char ch)
    {
      assert(pos <= _size);
      if (_size + n > _capacity)
      {
        int newcapacity = _size + n;
        reserve(newcapacity);
      }
      int end = _size;
      while (end >= pos && end != npos)
      {
        _str[end + n] = _str[end];
        end--;
      }
      int i = pos;
      for (i = pos; i < pos + n; i++)
      {
        _str[i] = ch;
      }
      _size += n;
    }
    /*****************************************************************************/
    //增删查改之删
    void erase(int pos, size_t len = npos)
    {
      assert(pos <= _size);
      if (len == npos || pos + len > _size)
      {
        _str[pos] = '\0';
        _size = pos;
      }
      else
      {
        int end = pos + len;
        while (end <= _size)
        {
          _str[pos++] = _str[end++];
        }
        _size -= len;
      }
    }
    void clear()
    {
      _str[0] = '\0';
      _size = 0;
    }
    /*****************************************************************************/
    //查
    size_t find(char ch, size_t pos = 0)
    {
      assert(pos < _size);
      for (int i = pos; i < _size; i++)
      {
        if (_str[i] == ch)
        {
          return i;
        }
      }
      return npos;
    }
    size_t find(const char* str, size_t pos = 0)
    {
      assert(pos < _size);
      char* ptr = strstr(_str + pos, str);
      if (ptr)
      {
        return (ptr - _str);
      }
      else
      {
        return npos;
      }
    }
    //提取出指定位置的串
    string substr(size_t pos = 0, size_t len = npos)
    {
      assert(pos < _size);
      size_t n = len;
      if (len == npos || pos + len > _size)
      {
        n = _size - pos;
      }
      string tmp;
      tmp.reserve(n);
      for (size_t i = pos; i < pos + n; i++)
      {
        tmp += _str[i];
      }
      return tmp;
    }
    //调整size
    void resize(size_t n, char ch = '\0')
    {
      if (n <= _size)
      {
        _size = n;
        _str[_size] = '\0';
      }
      else
      {
        reserve(n);
        for (int i = _size; i < n; i++)
        {
          _str[i] = ch;
        }
        _size = n;
        _str[_size] = '\0';
      }
    }
    //比较大小
    //bool operator<(const string& s)
    //{
    //  size_t i1 = 0;
    //  size_t i2 = 0;
    //  while (i1 < _size && i2 < s._size)
    //  {
    //    if (_str[i1] < s._str[i2])
    //    {
    //      return true;
    //    }
    //    else if (_str[i1] > s._str[i2])
    //    {
    //      return false;
    //    }
    //    else
    //    {
    //      i1++;
    //      i2++;
    //    }
    //  }
    //  return (i1 == _size) && (i2 != s._size);
    //  //return _size<s._size;
    //}
    bool operator<(const string& s) const 
    {
      int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);
      return ret == 0 ? _size < s._size : ret < 0;
    }
    bool operator==(const string& s) const 
    {
      return _size == s._size && (memcmp(_str, s._str, _size) == 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);
    }
    //赋值运算符重载
    //string& operator=(const string& s)
    //{
    //  if (this != &s)
    //  {
    //    char* tmp = new char[s._capacity + 1];
    //    delete[] _str;
    //    _str = tmp;
    //    memcpy(_str, s._str, s._size + 1);
    //    _size = s._size;
    //    _capacity = _capacity;
    //  }
    //  return *this;
    //}
    void swap(string& s)
    {
      std::swap(_str, s._str);
      std::swap(_size, s._size);
      std::swap(_capacity, s._capacity);
    }
    //string& operator=(const string& s)
    //{
    //  if (this != &s)
    //  {
    //    string tmp(s);
    //    swap(tmp);
    //  }
    //  return *this;
    //}
    string& operator=(string s)
    {
      swap(s);
      return *this;
    }
  private:
    char* _str;
    size_t _size;
    size_t _capacity;
  public:
    const static size_t npos;
  };
  const size_t string::npos = -1;
  ostream& operator<<(ostream& out, const string& s)
  {
    //for (size_t i = 0; i < s.size(); i++)
    //{
    //  out << s[i];
    //}
    //out << endl;
    //return out;
    for (auto ch : s)
    {
      out << ch;
    }
    out << endl;
    return out;
  }
  istream& operator>>(istream& in, string& s)
  {
    s.clear();
    char ch = in.get();
    while (ch == ' ' || ch == '\n')
    {
      ch = in.get();
    }
    char buff[128] = { 0 };
    int i = 0;
    while (ch != ' ' && ch != '\n')
    {
      //s += ch;
      buff[i++] = ch;
      if (i == 127)
      {
        buff[i] = '\0';
        s += buff;
        i = 0;
      }
      ch = in.get();
    }
    if (i != 0)
    {
      buff[i] = '\0';
      s += buff;
    }
    return in;
  }
}

下面是测试的代码

#define _CRT_SECURE_NO_WARNINGS 1
#include "String.h"
#include <string>
void teststring1()
{
  String::string s1("hello world");
  cout << s1.c_str() << endl;
  String::string s2;
  cout << s2.c_str() << endl;
  //string s;
  //cout << s << endl;
  int i = 0;
  //for (i = 0; i < s1.size(); i++)
  //{
  //  s1[i]++;
  //}
  //cout << endl;
  for (i = 0; i < s1.size(); i++)
  {
    cout << s1[i] << ' ';
  }
  cout << endl;
  //const String::string s3("hello hello");
  //for (i = 0; i < s3.size(); i++)
  //{
  //  s3[i]++;
  //}
  //cout << endl;
  String::string::iterator it = s1.begin();
  while (it != s1.end())
  {
    cout << *it << ' ';
    it++;
  }
  cout << endl;
  for (auto ch : s1)
  {
    cout << ch << ' ';
  }
}
void teststring2()
{
  const String::string s1("hello world");
  String::string::const_iterator it = s1.begin();
  while (it != s1.end())
  {
    cout << *it << ' ';
    it++;
  }
  cout << endl;
  for (auto ch : s1)
  {
    cout << ch << ' ';
  }
}
void teststring3()
{
  String::string s1("hello world");
  cout << s1.c_str() << endl;
  s1.pushback('!');
  cout << s1.c_str() << endl;
  s1.append("hello world");
  cout << s1.c_str() << endl;
  String::string s2("hello world");
  cout << s2.c_str() << endl;
  s2 += '!';
  cout << s2.c_str() << endl;
  s2 += "hello world";
  cout << s2.c_str() << endl;
  String::string s3("hello world");
  cout << s3.c_str() << endl;
  s3.insert(0, 5, 'x');
  cout << s3.c_str() << endl;
  s3.insert1(0, 5, 'e');
  cout << s3.c_str() << endl;
  s3.erase(5, 4);
  cout << s3.c_str() << endl;
}
void teststring4()
{
  String::string url = "ftp://www.baidu.com/?tn=65081411_1_oem_dg";
  size_t pos1 = url.find("://");
  if (pos1 != String::string::npos)
  {
    String::string protocol = url.substr(0, pos1);
    cout << protocol.c_str() << endl;
  }
  size_t pos2 = url.find('/', pos1 + 3);
  if (pos2 != String::string::npos)
  {
    String::string domain = url.substr(pos1 + 3, pos2 - (pos1 + 3));
    String::string uri = url.substr(pos2 + 1);
    cout << domain.c_str() << endl;
    cout << uri.c_str() << endl;
  }
}
void teststring5()
{
  String::string s("hello world");
  s.resize(8);
  cout << s.c_str() << endl;
  s.resize(13, 'x');
  cout << s.c_str() << endl;
  s.resize(20, 'y');
  cout << s.c_str() << endl;
  cout << s;
}
void teststring6()
{
  String::string s("hello world");
  s += '\0';
  s += "xxxxxx";
  cout << s.c_str() << endl;
  cout << s << endl;
}
void teststring7()
{
  String::string s;
  cin >> s;
  cout << s << endl;
  cin >> s;
  cout << s << endl;
}
void teststring8()
{
  //String::string s1("hello world");
  //s1 += '\0';
  //s1 += "xxxxx";
  //String::string s2("hello world");
  //s2 += '\0';
  //s2 += "yyyyy";
  //cout << (s2 < s1) << endl;
  //cout << (s2 == s1) << endl;
  String::string s1("hello");
  String::string s2("hello");
  cout << (s1 < s2) << endl;
  cout << (s1 > s2) << endl;
  cout << (s1 == s2) << endl << endl;
  String::string s3("hello");
  String::string s4("helloxxx");
  cout << (s3 < s4) << endl;
  cout << (s3 > s4) << endl;
  cout << (s3 == s4) << endl << endl;
  String::string s5("helloxxx");
  String::string s6("hello");
  cout << (s5 < s6) << endl;
  cout << (s5 > s6) << endl;
  cout << (s5 == s6) << endl << endl;
  s6 = s5;
  cout << s6 << endl;
}
int main()
{
  //teststring1();
  //teststring2();
  //teststring3();
  //teststring4();
  teststring8();
  return 0;
}

好了本期内容就到这里了

本期内容确实比较硬核,希望读者能够认真消化

好了我们下期内容再见!!!

相关文章
|
1月前
|
C++ 容器
|
1月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
22 1
|
1月前
|
C++ 容器
|
1月前
|
C++ 容器
|
1月前
|
存储 C++ 容器
|
1月前
|
安全 C语言 C++
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
36 4
|
1月前
|
存储 编译器 程序员
【C++篇】手撕 C++ string 类:从零实现到深入剖析的模拟之路
【C++篇】手撕 C++ string 类:从零实现到深入剖析的模拟之路
65 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的使用与理解
下一篇
无影云桌面