【C++】string学习 — 手搓string类项目

简介: C++ 的 string 类是 C++ 标准库中提供的一个用于处理字符串的类。它在 C++ 的历史中扮演了重要的角色,为字符串处理提供了更加方便、高效的方法。



1 string类介绍

C++ 的 string 类是 C++ 标准库中提供的一个用于处理字符串的类。它在 C++ 的历史中扮演了重要的角色,为字符串处理提供了更加方便、高效的方法。

在 C++ 的早期版本中,字符串处理并不是一个简单的事情。在 C++ 的最初版本中,字符串被处理为 char* 类型的指针,这使得字符串处理变得非常复杂,容易出错。例如,简单的字符串连接操作都需要手动管理内存,这无疑增加了编程的难度。

为了解决这个问题,C++98 引入了 头文件,其中包含了 string 类。这个类的引入,可以说是一场革命,因为它提供了一个安全、方便、可移植的字符串处理方式。也为以后STL的出现埋下了伏笔…


在现实生活中,string也有着大量的应用:

  1. 社交媒体: 当你在社交媒体上发布状态或评论时,你输入的文字内容会存储在一个 string 变量中。例如,你可能会写一条消息 like “I had a great day at the park!”,这条消息就是存储在一个 string 变量中的。
  2. 、电子邮件: 当你写一封电子邮件时,正文内容、主题行和收件人地址等都可能是 string 类型的。例如,你可能会写一封主题为 “Meeting Invitation” 的邮件,内容为 “Dear John, please join us for a meeting at 10am tomorrow.”,这些内容都是以 string 形式存储的。
  3. 购物车: 在在线购物时,你的购物车中商品的名称、价格和数量等信息通常会存储在 string 类型的变量中。例如,你的购物车中可能有 “T-shirt”、" Jeans" 和 “Shoes” 等商品,这些商品名称都是以 string 形式存储的。
  4. 等等等…

这里我们不管他的底层:basic_string模板类的别名,typedef basic_stringstring;

我们通过类与对象的相关知识来尝试完成 string项目!我们将通过先描述,在落地的原则开始,只有明白了功能模块,才能流畅的写出string。

2 功能描述

首先我们必须明白我们需要什么功能,所以我们可以熟悉一下官方的常用接口:string使用手册

当然,从实际出发不失为一种更好的选择,想象一下使用场景:

  1. 第一,因为本质是字符串,所以我们需要成员函数 char* _str,并且要做到很好控制的话还需要 数据大小size_t _size 和容量 size_t capacity。
  2. 第二,构造函数,析构函数必须要有的,而且构造函数需要支持多种构造方法(常量字符串,拷贝构造,空类构造)。
  3. 第三,我们一定要支持输入字符串来构造string类和输出string,这就需要做到<< >>的重载了。
  4. 第亖,要想实现输入>>的重载,就要辅助实现push_back尾插函数,实现了尾插那 +=的操作重载也就完成了。
  5. 第五,我们还需要通过对比大小的一系列操作符(== <= >= > < !=)的重载。
  6. 第六,根据字符串输入的特性,为了我们可以从一行中正确读取数据,我们还需getline函数来实现功能。
  7. 第七,回到最基础的功能增删查改,所以我们可以增加指定位置插入,指定位置删除,查找字符串等功能。
  8. 第八,对于C++新增特性迭代器,我们也可以用指针模拟实现一下。
  9. 第九,既然支持了迭代器,那最原始的小标操作也要支持一下。

以上就是对一个字符串类可能需要的功能的全面总结,通过实现这些功能,我们可以创建出一个既实用又灵活的字符串操作工具

接下来,我们将根据之前列出的功能需求,逐步实施我们的字符串模拟项目。在编写代码的过程中,我们必须保持细心和谨慎,这样可以避免后期出现不必要的调试困扰。

3 代码实现

在实现这个项目的过程中,我们需要注意以下几点:

  1. 保持代码的清晰和可读性(重中之重):在编写代码时,要注意命名规范、代码结构和注释,使得其他人能够轻松理解我们的代码。
  2. 模块化设计:将代码分为多个模块,每个模块负责一个特定的功能。这样可以降低代码的复杂度,也便于后期的维护和扩展。
  3. 充分测试一定一定!!!):在代码实现完成后,要进行充分的测试,确保每个功能的正确性和稳定性。我们可以使用单元测试和集成测试来验证代码的质量。
  4. 优化性能:在保证功能实现的基础上,尽量优化代码的性能。我们可以关注一些常见的性能瓶颈,如内存分配、字符串拼接等,并寻求优化的方法

总之,在实现这个项目的过程中,我们要注重代码的质量、可读性和可维护性。只有这样,我们才能构建出一个高效、稳定且易于扩展的字符串模拟类。接下来,让我们开始编写代码吧!

3.0 基础框架

我们先根据功能写一下大概的功能框架,方便书写:(其中许多函数需要重载多个)

#pragma once

#include<iostream>
#include<string.h>
#include<assert.h>

using namespace std;



namespace bit {

  class string {

  public:
  //默认结尾
    static const int npos;
  //构造函数
    string() :
    {
    }
    string(const char* str = "")  
    {
    }
    string(const string& s = "") 
    {
    }
  //析构函数
    ~string() {
    }
  //取等操作
    string& operator=(string s) {
    }

    //迭代器模拟
    char* begin() {
    }
    char* end() {
    }
    //逆转迭代器
    char* rbegin() {
    }
    char* rend() {
    }
    
    //交换
    void swap(string& s) {
    }
  

    //从pos位置开始搜索寻找ch第一次出现的位置
    size_t find(const char ch, size_t pos = 0) {
    }
    //从pos位置开始搜索寻找 字符串s 第一次出现的位置
    size_t find(const char* s, size_t pos = 0) {
    }
    //在pos位置插入字符
    void insert(const char ch, size_t pos = 0) {
    }
    //在pos位置插入字符串
    void insert(const char* s, size_t pos = 0) {
    }

    //返回成员变量
    size_t size() const{
    }
    size_t capacity() const {
    }
    //扩容操作
    void reserve(size_t n){
    }
    //重置数据大小
    void resize(size_t n , char ch = '\0') {
    }
    //在pos位置后消除 n 个元素
    void erase(size_t pos, size_t n = npos) {
    }
    //尾插
    void push_back(const char* s) {
    }
    void push_back(char s) {
    }
    //清空
    void clear() {
    }
    //+=操作符重载
    void operator+= (const char* s) {
    }
    void operator+= (char s) {
    }
  //运算符重载
  friend ostream& operator<< (ostream& out, const string& str);
  bool operator==(const string& s) {
  }
  bool operator>(const string& s) {
  }
  bool operator>=(const string& s) {
  }
  bool operator<=(const string& s) {
  }
  bool operator<(const string& s) {
  }
  bool operator!=(const string& s) {
  }
//私有成员函数
  private:
    char* _str;
    size_t _size;
    size_t _capacity;
  };

  //流操作符重载
  ostream& operator<< (ostream& out, const bit::string& str) {
  }
  istream& operator>> (istream& in,  bit::string& str) {
  }
  //获取一行
  istream& getline(istream& in, string& s) {
  }
  //结尾赋值
  const int string::npos = -1;

框架写好,我们就可以开始逐个实现,一定注意其中的逻辑,不要刻意去实现一个功能,要联系其他功能,看看是否存在联系,进而通过调用函数简便我们的实现过程。

3.1 构造函数 和 析构函数

构造函数我们使用全却省,拷贝构造2个:这里注意初始化列表的使用

因为涉及了指针操作,所以必要的初始化是十分需要的

全缺省构造函数十分好用


//常量字符串构造
string(const char* str = "")
  :_str(new char[strlen(str) + 1]),
  _size(strlen(str)),
  _capacity(strlen(str) + 1)
{
//调用函数简单完成
  strcpy(_str, str);
}
//拷贝构造
//这里使用到了 = 重载,所以它测试可以等到实现操作符重载之后在实现。
string(const string& s = "") :
  _str(new char[s._capacity + 1])
  ,_size(0)
  ,_capacity(0)
{
  strcpy(_str, s._str);
  _size = s._size;
  _capacity = s._capacity;
}

析构函数就简单的多:正常释放空间即可

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

再来增加一些获取私有变量的函数:

//返回成员变量
  size_t size() const{
    return _size;
  }
  size_t capacity() const{
    return _capacity;
  }

3.2 流操作符重载 和 尾插扩容

接下来我们实现一下流操作符,方便我们可以快速进行一下测试。

对于流操作我们应该写在全局,这就可以正常的传入参数,不然就会报错哦。

//简单打印即可,注意设置友元哦
ostream& operator<< (ostream& out, const bit::string& str) {
  out << str._str;
  return out;
}
//这里是优化版本,可以避免频繁开空间,优化性能
istream& operator>> (istream& in,  bit::string& str) {
  str.clear();
  char* buff = new char[128];
  char ch;
  ch = in.get();

  int count = 0;
  //先存入中间数组再存入string中
  while (ch != '\n' && ch != ' ') {

    buff[count++] = ch;
      ch = in.get();
    if (count >= 127) {
      buff[127] = '\0';
      str.push_back(buff);
      count = 0;
    }
  }

  buff[count] = '\0';
  str.push_back(buff);

  return in;
}

这里我们发现我们需要实现一下尾插操作才好进行流输入操作。看,这样一步一步我们就可以完成所需功能。

尾插 push_back

  //插入字符串
void push_back(const char* s) {
    //先扩容!!!
  while (_size + strlen(s) >= _capacity) {
    reserve(_capacity == 0 ? 4 : 2 * _capacity);
  }
    //然后依次读入即可
  for (size_t i = 0; i < strlen(s); i++) {
    _str[_size++] = s[i];
  }
  _str[_size] = '\0';

}
    //插入单个字符
void push_back(char s) {
  while (_size + 1 >= _capacity) {
    reserve(_capacity == 0 ? 4 : 2 * _capacity);
  }
  _str[_size++] = s;
  _str[_size] = '\0';
}

可以想到,要实现尾插,扩容是必不可少的!!

再补充一个更改数据大小的函数。

扩容 reserve

    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') {
      
      if ( n <= _size ) {
        _str[n] = '\0';
        _size = n;

      }
      else {
        reserve(n);
        for (size_t i = _size; i < n; i++)
        {
          _str[i] = ch;
        }
        _str[n] = '\0';
        _size = n;
      }

    }

这样我们 就初步完成了局部的可执行程序。接下来我们进行第一次测试来看是否能够成功运行

void test_string1(){
  bit::string s1("123 456");
  bit::string s2("");
  cout << "\n-------流输出测试--------\n";
  cout << s1 << endl;
  cout << s1.size() << ' ' << s1.capacity();
  s1.push_back("abcdefg");
  cout << "\n-------尾插测试--------\n";
  cout << s1 << endl;
  cout << s1.size() << ' ' << s1.capacity();
  cout << "\n-------流输入测试--------\n";
  cin >> s2;
  cout << s2 << endl;
  cout << s2.size() << ' ' << s2.capacity();
}

来看效果:

这样我们初步就完成了我们基础功能。接下来我们继续实现!

3.4 运算符重载

这部分比较简单:

实现两个之后,就可以来回使用完成六个函数的书写!这是运算逻辑的体现,让我们的代码变得简洁明朗,减少冗杂,增加代码的可读性。


  bool operator==(const string& s) {
    if (strcmp(_str, s._str) == 0) return true;
    else return false;
  }
  bool operator>(const string& s) {
    if (strcmp(_str, s._str) > 0) return true;
    else return false;
  }
  bool operator>=(const string& s) {
    return _str == s._str || _str > s._str;
  }
  bool operator<=(const string& s) {
    return !(_str > s._str);
  }
  bool operator<(const string& s) {
    return !(_str >= s._str);
  }
  bool operator!=(const string& s) {
    return !(_str == s._str);
  }
  
string& operator=(string s) {
  //现代写法  
  //swap(tmp);

  char* tmp = new char[s._capacity + 1];
  strcpy(tmp, s._str);

  delete[] _str;

  _str = tmp;
  _size = s._size;
  _capacity = s._capacity;

  return *this;

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

然后我们来完成十分常用的+=操作,不用多说,这个底层逻辑和push_back是一致的,所以在底层直接调用即可,这样也变向保证了代码的鲁棒性,只需对一个功能做出维护,既可以扩展出其他接口。

注意这里面 的 = 重载,现代写法更加简单 只需一步 swap即可。这十分巧妙,通过调用不同函数就帮助我们改善了代码的复杂性。

//提供两个重载,让其使用体验更好
void operator+= (const char* s) {

    push_back(s);

}
void operator+= (char ch) {

    push_back(ch);

}

然后,再来测试一下,保证我们的功能可以正常使用,一定一定要测试哦!测试十分重要,千万不能忽视!!!小心驶得万年船!

void test_string2(){
  bit::string s1("123 456");
  bit::string s2("123");
  
  cout<< "-----------比较测试--------------\n" ;
  cout << (s2 == s1) << endl;
  cout << (s2 < s1) << endl;
  cout << (s2 > s1) << endl;
  cout << (s2 <= s1) << endl;
  cout << (s2 >= s1) << endl;
  cout << (s2 != s1) << endl;
  cout<< "-----------+=测试--------------\n" ;
  bit::string s3("123456789");
  s3 += "abc";
  char ch = '1';
  s3 += ch;
  cout << s3;

}

我们进行了每个操作符的测试和+= 单个字符 与字符串的测试。正常通过测试,返回的0 1 值符合我们的要求。

3.5 实用功能

上述我们已经实现了基本的功能,接下来我们要加入一些比较实用的功能,比如查找,指定位置插入,指定位置删除,获取一行的字符。这些函数大大加强了string 的可操作性,让string更加使用,与普通的 char 类型拉开差距!

注意我们都要提供两种重载,保证单个字符和字符串都可以正常进行操作

//指定位置删除
void erase(size_t pos, size_t n = npos) {
    assert(pos < _size);
    //n >= _size - pos 防止溢出!!!
    if (n == npos || n >= _size - pos) {
      _str[pos] = '\0';
      _size = pos;
    }
    else 
    {
      strcpy(_str + pos, _str + pos + n);
      _size -= n;
    }

}

//在pos位置插入字符串
  void insert(const char* s, size_t pos = 0) {
    assert(pos < _size);
    int len = strlen(s);
    //保证容量足够不然会发生报错哦
    while (_size + len >= _capacity)
    {
      reserve(_capacity == 0 ? 4 : 2 * _capacity);
    }
    //挪动数据
    size_t end = _size + len;
    while (end > pos + len - 1 ) {
      _str[end] = _str[end - len];
      end--;
    }
    //拷贝到指定位置,不要拷贝‘\0’
    strncpy(_str + pos, s, len);

    _str[_size + strlen(s)] = '\0';
    _size += strlen(s);

  }
  //在pos位置插入字符
  void insert(const char ch, size_t pos = 0) {
    assert(pos < _size);

    while (_size +1 >= _capacity)
    {
      reserve(_capacity == 0 ? 4 : 2 * _capacity);
    }
    //普通写法?!?!
    /*int end = _size ;
    while (end >= (int)pos)
    {
      _str[end + 1] = _str[end];
      --end;
    }*/
    //使用这个避免发生类型转换
    size_t end = _size + 1;
    while (end > pos)
    {
      _str[end] = _str[end - 1];
      --end;
    }

    _str[pos] = ch;
    ++_size;
  }
    //从pos位置开始搜索寻找ch第一次出现的位置
    size_t find(const char ch, size_t pos = 0) {
      assert(pos < _size);

      for (size_t i = pos; i < _size; i++) {
        if (_str[i] == ch) return i;
      }
      return npos;
    }

    //从pos位置开始搜索寻找 字符串s 第一次出现的位置
    size_t find(const char* s, size_t pos = 0) {
      assert(pos < _size);
      const char* p = strstr(_str, s);
      if (p) return p - _str;
      else return npos;
    }

写入了这么多功能,快来进行测试一波!

void test_string3() {
  bit::string s1("123 456 abc");
  cout << "---------find测试-------------\n";
  cout << s1.find('1') << endl;
  cout << s1.find("457") << endl;
  cout << "---------insert测试-------------\n";
  bit::string s2("123");
  s1.insert("jkl465798", 0);
  cout << s1 << endl;
  cout << "---------resize测试-------------\n";
  cout << s2 << endl;
  cout << s2.capacity() << endl;
  s1.resize(4);
  cout << s2 << endl;
  cout << s2.size() << ' ' << s2.capacity() << endl;
  s1.resize(10, 'c');
  cout << s2 << endl;
  cout << s2.size() << ' ' << s2.capacity() << endl;
  cout << "---------erase测试-------------\n";
  
  s2.erase(2, 5);
  cout << s2 << endl;
  cout << s2.size() << ' ' << s2.capacity() << endl;

我们成功完成了功能的扩展,实现了增删查改的重要部分,这下子我们的string就完成了绝大部分,接下来在补充上迭代器就更好了。

3.6 迭代器模拟

C++中的迭代器是用于访问容器元素的一种抽象指针。迭代器具有五个基本特性:

  1. 迭代器类型:迭代器是一个实现了迭代器协议的对象,它有一个类型,该类型指示它所指向的元素的类型。例如,在std::string中,迭代器类型是std::string::iterator
  2. 解引用:迭代器可以解引用,这意味着可以通过解引用迭代器来访问它所指向的元素。在std::string中,解引用迭代器可以访问字符串中的字符。
  3. 箭头操作符:大多数迭代器都支持箭头操作符->,用于访问迭代器所指向对象的成员。在std::string中,箭头操作符可以用于访问字符串中字符的成员函数,如std::string::iterator>std::string::value_type::operatorchar()
  4. 增加和减少:迭代器可以通过增加(++)和减少(–)操作符来遍历容器。在std::string中,增加迭代器会移动到下一个字符,减少迭代器会移动到前一个字符。
  5. 比较:迭代器可以比较,以确定它们是否指向同一个元素或是否在容器中相邻。在std::string中,两个迭代器可以通过比较操作符(==、!=)来比较它们是否相等,或者通过比较操作符(<、<=、>、>=)来比较它们的相对位置。

所以我们可以简单通过指针来模拟实现一下,让其可以初步使用即可。

依旧给出两套重载,保证常量与非常量的正常访问

    //迭代器模拟
    typedef char* iterator;
    typedef const char* const_iterator;

    iterator begin() {
      return _str;
    }
    iterator end() {
      return _str + _size;
    }
    iterator rbegin() {
      return _str + _size;
    }
    iterator rend() {
      return _str;
    }
    const_iterator begin() const {
      return _str;
    }
    const_iterator end() const{
      return _str + _size;
    }
    const_iterator rbegin() const{
      return _str + _size;
    }
    const_iterator rend() const{
      return _str;
    }
    //提供下标访问 传回引用,可读可写
    char& operator[](size_t i) {
    //保证数组不越界!比普通数组越界更好,
    //普通数组是抽查 , 不够稳定!!!
      assert(i < _size);
      return _str[i];
    }
  //保证可以对常量string进行操作
    const char& operator[](size_t i) const {
      assert(i < _size);
      return _str[i];
    }

来进行测试一下:(const 变量与普通变量都进行测试)

void test_string4(){

  bit::string s1("123456789");
  for (auto ch : s1) {
    cout << ch << ' ';
  }

  reverse(s1.begin(), s1.end());
  cout << endl;

  for (size_t i = 0; i < s1.size();i++) {
    cout << s1[i] << " ";
  }
  cout << endl;
  const bit::string s2("abcdefg");
  for (auto ch : s2) {
    cout << ch << ' ';
  }

  cout << endl;

  for (size_t i = 0; i < s2.size();i++) {
    cout << s2[i] << " ";
  }

}

这下也间接证明了基于范围的for循环是以迭代器为底层的。并且我们实现了[ ] 的成功可读可写访问

总结

实现string类的过程就像是在黑暗中寻找光明,每一个难题都是我前进路上的绊脚石,但我没有退缩,我勇往直前。我看着那些曾经困扰着我的问题,一步步被我解决,就像是看着黑暗中的光明一点点被我点亮。那种成就感,那种喜悦,无法用言语表达!!!


肆无忌惮的放任自己,这样得来的自由,终将在现实中轰然倒塌。而自律赢来的,是你对现实的自主感,是真正的自由。


我看着电脑屏幕上的代码,每一个字符都像是我的朋友,我的伙伴。我用心去理解它们,去掌握它们,去运用它们。我发现,当我用自律的态度去面对这些代码时,它们不再是我眼中的难题,而是我手中的工具,是我实现梦想的桥梁。

我知道,这只是我开始,这只是我旅程的一小步。前方还有更多的挑战等待着我,有更多的困难需要我去克服。但我不再害怕,因为我知道,只要我保持着自律的态度,我就能够战胜一切。

这里提供一下源代码:

#pragma once

#include<iostream>
#include<string.h>
#include<assert.h>

using namespace std;



namespace bit {

  class string {

  public:
    static const int npos;
  //构造函数
    //string() :
    //_str(new char[1]),
    //_size(0),
    //_capacity(0)
    //{
    //  _str[0] = '\0';
    //}
    
    //一个全缺省即可
    string(const char* str = "")
      :_str(new char[strlen(str) + 1]),
      _size(strlen(str)),
      _capacity(strlen(str) + 1)
    {
      strcpy(_str, str);
    }

    string(const string& s = "") :
      _str(new char[s._capacity + 1])
      ,_size(0)
      ,_capacity(0)
    {
      strcpy(_str, s._str);
      _size = s._size;
      _capacity = s._capacity;
    }

    ~string() {
      delete[] _str;

      _str = nullptr;
      _size = 0;
      _capacity = 0;
    }


    string& operator=(string s) {
      //现代写法  
      //swap(tmp);

      char* tmp = new char[s._capacity + 1];
      strcpy(tmp, s._str);

      delete[] _str;

      _str = tmp;
      _size = s._size;
      _capacity = s._capacity;

      return *this;

    }

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

    //迭代器模拟
    typedef char* iterator;
    typedef const char* const_iterator;

    iterator begin() {
      return _str;
    }
    iterator end() {
      return _str + _size;
    }
    iterator rbegin() {
      return _str + _size;
    }
    iterator rend() {
      return _str;
    }
    const_iterator begin() const {
      return _str;
    }
    const_iterator end() const{
      return _str + _size;
    }
    const_iterator rbegin() const{
      return _str + _size;
    }
    const_iterator rend() const{
      return _str;
    }

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

    //从pos位置开始搜索寻找ch第一次出现的位置
    size_t find(const char ch, size_t pos = 0) {
      assert(pos < _size);

      for (size_t i = pos; i < _size; i++) {
        if (_str[i] == ch) return i;
      }
      return npos;
    }

    //从pos位置开始搜索寻找 字符串s 第一次出现的位置
    size_t find(const char* s, size_t pos = 0) {
      assert(pos < _size);
      const char* p = strstr(_str, s);
      if (p) return p - _str;
      else return npos;
    }
    //在pos位置插入字符
    void insert(const char ch, size_t pos = 0) {
      assert(pos < _size);

      while (_size +1 >= _capacity)
      {
        reserve(_capacity == 0 ? 4 : 2 * _capacity);
      }
      size_t end = _size + 1;

      while (end > pos)
      {
        _str[end] = _str[end - 1];
        --end;
      }

      _str[pos] = ch;
      ++_size;


    }
    //在pos位置插入字符串
    void insert(const char* s, size_t pos = 0) {
      assert(pos < _size);
      int len = strlen(s);

      while (_size + len >= _capacity)
      {
        reserve(_capacity == 0 ? 4 : 2 * _capacity);
      }
      
      size_t end = _size + len;

      while (end > pos + len - 1 ) {
        _str[end] = _str[end - len];
        end--;
      }

      strncpy(_str + pos, s, len);

      _str[_size + strlen(s)] = '\0';
      _size += strlen(s);

    }

    //返回成员变量
    size_t size() const{
      return _size;
    }
    size_t capacity() const{
      return _capacity;
    }
    //扩容操作
    
    //错误示范
    //void reserve(size_t newcapacity)
    //{
    //  char* tmp = new char[newcapacity + 1];
    //  strcpy(tmp, _str);
    //  _capacity = newcapacity + 1;
    //  delete _str;
    //  _str = tmp;
    //}

    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') {
      
      if ( n <= _size ) {
        _str[n] = '\0';
        _size = n;

      }
      else {
        reserve(n);
        for (size_t i = _size; i < n; i++)
        {
          _str[i] = ch;
        }
        _str[n] = '\0';
        _size = n;
      }

    }
    //在pos位置后消除 n 个元素
    void erase(size_t pos, size_t n = npos) {
      assert(pos < _size);
      //防止溢出!!!
      if (n == npos || n >= _size - pos) {
        _str[pos] = '\0';
        _size = pos;
      }
      else 
      {
        strcpy(_str + pos, _str + pos + n);
        _size -= n;
      }

    }

    void push_back(const char* s) {
      while (_size + strlen(s) >= _capacity) {
        reserve(_capacity == 0 ? 4 : 2 * _capacity);
      }

      for (size_t i = 0; i < strlen(s); i++) {
        _str[_size++] = s[i];
      }
      _str[_size] = '\0';

    }
    
    void push_back(char s) {
      while (_size + 1 >= _capacity) {
        reserve(_capacity == 0 ? 4 : 2 * _capacity);
      }
      _str[_size++] = s;
      _str[_size] = '\0';

    }
    //清空
    void clear() {
      _str[0] = '\0';
      _size = 0;
      _capacity = 0;
    }

    void operator+= (const char* s) {

      push_back(s);

    }
    void operator+= (char s) {

      push_back(s);

    }
  //运算符重载
  friend ostream& operator<< (ostream& out, const string& str);

  bool operator==(const string& s) {
    if (strcmp(_str, s._str) == 0) return true;
    else return false;
  }

  bool operator>(const string& s) {
    if (strcmp(_str, s._str) > 0) return true;
    else return false;
  }
  bool operator>=(const string& s) {
    return _str == s._str || _str > s._str;
  }
  bool operator<=(const string& s) {
    return !(_str > s._str);
  }
  bool operator<(const string& s) {
    return !(_str >= s._str);
  }
  bool operator!=(const string& s) {
    return !(_str == s._str);
  }

  private:
    char* _str;
    size_t _size;
    size_t _capacity;
  };

  //运算符重载
  ostream& operator<< (ostream& out, const bit::string& str) {
    out << str._str;
    return out;
  }

  istream& operator>> (istream& in,  bit::string& str) {
    str.clear();
    char* buff = new char[128];
    char ch;
    ch = in.get();

    int count = 0;

    while (ch != '\n' && ch != ' ') {

      buff[count++] = ch;
      ch = in.get();
      if (count >= 127) {
        buff[127] = '\0';
        str.push_back(buff);
        count = 0;
      }
    }

    buff[count] = '\0';
    str.push_back(buff);

    return in;
  }

  istream& getline(istream& in, string& s) {
    char ch;
    ch = in.get();

    while (ch != '\n') {
      s += ch;
      ch = in.get();
    }
    return in;
  }

  const int string::npos = -1;

}

Thanks♪(・ω・)ノ谢谢阅读!!!

谢谢Thanks♪(・ω・)ノ!

下一篇文章见!!!

期待与你一起进步!

相关文章
|
8天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
34 4
|
9天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
32 4
WK
|
18天前
|
机器学习/深度学习 人工智能 算法
那C++适合开发哪些项目
C++ 是一种功能强大、应用广泛的编程语言,适合开发多种类型的项目。它在游戏开发、操作系统、嵌入式系统、科学计算、金融、图形图像处理、数据库管理、网络通信、人工智能、虚拟现实、航空航天等领域都有广泛应用。C++ 以其高性能、内存管理和跨平台兼容性等优势,成为众多开发者的选择。
WK
39 1
|
23天前
|
编译器 C语言 C++
配置C++的学习环境
【10月更文挑战第18天】如果想要学习C++语言,那就需要配置必要的环境和相关的软件,才可以帮助自己更好的掌握语法知识。 一、本地环境设置 如果您想要设置 C++ 语言环境,您需要确保电脑上有以下两款可用的软件,文本编辑器和 C++ 编译器。 二、文本编辑器 通过编辑器创建的文件通常称为源文件,源文件包含程序源代码。 C++ 程序的源文件通常使用扩展名 .cpp、.cp 或 .c。 在开始编程之前,请确保您有一个文本编辑器,且有足够的经验来编写一个计算机程序,然后把它保存在一个文件中,编译并执行它。 Visual Studio Code:虽然它是一个通用的文本编辑器,但它有很多插
|
1月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
27 4
|
1月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
23 4
|
1月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
21 1
|
1月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
1月前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)
|
1月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
53 1