【C++要笑着学】深浅拷贝 | string 模拟实现 | 传统写法与现代写法(三)

简介: 本章将正式介绍深浅拷贝,在模拟实现 string 的同时带着去理解深浅拷贝。我们模拟实现 string类不是为了造更好的轮子,而是为了去学习它,理解它的本质!你自己造一次,心里会更清楚,也有利于加深对 string 的理解。

Ⅵ. operator 运算符重载


0x00 引入

学日期类的时候我们就说过,我们只需实现 < 和 ==,剩下的都可以复用解决。


0x01 operator<

💬 我们在全局实现:

/* s1 < s2*/
bool operator<(const string& s1, const string& s2) {
  size_t i1 = 0, i2 = 0;
  while (i1 < s1.size() && i1 < s2.size()) {
  if (s1[i1] < s2[i2]) {
    return true;
  } 
  else if (s1[i1] > s2[i2]) {
    return false;
  }
  else {
    i1++;
    i2++;
  }
  }
  return i2 < s2.size() ? true : false;
}

当然,我们还可以实现的更简单些,直接用 strcmp 偷个懒:

/* s1 < s2*/
bool operator<(const string& s1, const string& s2) {
  return strcmp(s1.c_str(), s2.c_str()) < 0;
}

0x02 operator=

💬 全局作用域下:

/* s1 == s2 */
bool operator==(const string& s1, const string& s2) {
  return strcmp(s1.c_str(), s2.c_str()) == 0;
}

0x03 剩下的直接复用

💬 operator<=


/* s1 <= s2 */
  bool operator<=(const string& s1, const string& s2) {
  return s1 < s2 || s1 == s2;
  }

💬 operator>

/* s1 > s2 */
  bool operator>(const string& s1, const string& s2) {
  return !(s1 <= s2);
  }

💬 operator>=

/* s1 >= s2 */
  bool operator>=(const string& s1, const string& s2) {
  return !(s1 < s2);
  }

💬 operator!=

/* s1 != s2 */
  bool operator!=(const string& s1, const string& s2) {
  return !(s1 == s2);
  }

Ⅶ.  流插入和流提取


0x00 引入

我们当时实现日期类的流插入和流提取时,也详细讲过这些,当时讲解了友元。


在友元那一章我们说过 "占参问题" ,这里就不再多做解释了。


如需复习猛戳 👇


【C++要笑着学】友元 | 初始化列表 | 关键字explicit | 静态成员static | 内部类


如果我们重载成成员函数,第一个位置就会被隐含的 this 指针占据。


这样实现出来的流插入必然会不符合我们的使用习惯,所以我们选择在全局实现。


在全局里不存在隐含的 this 指针了。


0x01 operator<< 的实现

💬 operator<<

// cout << s1  →  operator<<(cout, s1)
ostream& operator<<(ostream& out, const string& s) {
  //for (auto ch : s) {
  //  out << ch;
  //}
  for (size_t i = 0; i < s.size(); i++) {
  out << s[i];
  }
  return out;
}

0x02 operator>> 的实现

💬 operator>>


// cin >>
istream& operator<<(istream& in, string& s) {
  char ch = in.get();
  while (ch == '\n') {
  s += ch;
  ch = in.get();
  }
  return in;
}

Ⅷ.  完整代码

#include <iostream>
#include <assert.h>
using namespace std;
namespace chaos
{
  class string {
  public:
  /* 构造函数 */
  string(const char* str = "")
    : _size(strlen(str))        // 计算出字符串str的大小
    , _capacity(_size) {        // 初始容量等于字符串大小
    _str = new char[_capacity + 1];   // 开辟一块 "容量+1" 大小的空间 (_capacity存的是有效字符)
    strcpy(_str, str);                // 将传入的字符串str复制到 _str中
  }
  void Swap(string& tmp) {
    swap(_str, tmp._str);
    swap(_size, tmp._size);
    swap(_capacity, tmp._capacity);
  }
  /* 拷贝构造函数:s2(s1) 
  string(const string& src)
    : _size(src._size)                // 拷贝string大小
    , _capacity(src._capacity) {      // 拷贝string容量
    // 拷贝string内容
    _str = new char[src._capacity + 1];        // 开辟一块和src相同容量的空间
    strcpy(_str, src._str);        // 将src中的_str内容拷贝到自己的_str中
  }
  */
  string(const string& src)
    : _str(nullptr)
    , _size(0)
    , _capacity(0) {
    string tmp(src._str);   // 拷贝构造一个src
    Swap(tmp);              // 现代写法:交换
  }
  /* 赋值重载:s1 = s3
  string& operator=(const string& src) {
    // 防止自己跟自己赋值
    if (this != &src) {
    // 1. 暂时用tmp开辟一块相同的空间
    char* tmp = new char[src._capacity + 1];
    // 2. 把src的值复制给tmp
    strcpy(tmp, src._str);
    // 3. 释放this原空间
    delete[] _str;
    // 4. 没翻车,把tmp交付给_src
    _str = tmp;
    _size = src._size;
    _capacity = src._capacity;
    }
    return *this;
  }
  string& operator=(const string& src) {
    // 防止自己跟自己赋值
    if (this != &src) {
    string tmp(src);   // 复用拷贝构造
    Swap(tmp);
    }
    return *this;
  }
  */
  string& operator=(string src) {
    Swap(src);    // 正好调用拷贝构造,不如让形参充当tmp
    return *this;
  }
  /* 返回C格式的字符串:c_str */
  const char* c_str() const {
    return _str;
  }
  /* 求字符串大小:size() */
  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];
  }
  /* 迭代器 */
  typedef char* iterator;
  iterator begin() { 
    return _str;            // 返回第一个字符的位置
  }
  iterator end() {
    return _str + _size;    // 返回最后一个字符的位置
  }
  /* const迭代器 */
  typedef const char* const_iterator;
  const_iterator begin() const {
    return _str;
  }
  const_iterator end() const {
    return _str + _size;
  }
  /* reserve() */
  void reserve(size_t new_capacity) {
    if (new_capacity > _capacity) {               // 检查是否真的需要扩容
    char* tmp = new char[new_capacity + 1];   // 开空间
    strcpy(tmp, _str);        // 先搬运数据到tmp
    _str = tmp;          // 没翻车,递交给_str
    _capacity = new_capacity;      // 更新容量
    }
  }
  /* 字符尾插:push_back() */
  void push_back(char append_ch) {
    /*
    if (_size == _capacity) {                         // 检查是否需要扩容
    reserve(_capacity == 0 ? 4 : _capacity * 2);  // 首次给4,其他情况默认扩2倍
    }
    _str[_size] = append_ch;     // 插入要追加的字符
    _size++;       
    _str[_size] = '\0';          // 手动添加'\0'
    */
    insert(_size, append_ch);
  }
  /* 字符串追加:append() */
  void append(const char* append_str) {
    /*
    size_t len = strlen(append_str);      // 计算出要追加的字符串的长度
    if (_size + len > _capacity) {    // 检查是否需要扩容
    reserve(_size + len);
    }
    strcpy(_str + _size, append_str);      // 首字符+大小,就是'\0'位置
    _size += len;         // 更新大小
    */
    insert(_size, append_str);
  }
  /* operator+= */
  string& operator+=(char append_ch) {
    push_back(append_ch);
    return *this;
  }
  string& operator+=(const char* append_str) {
    append(append_str);
    return *this;
  }
  /* insert */
  string& insert(size_t pos, char append_ch) {
    assert(pos <= _size);
    if (_size == _capacity) {  // 检查是否需要扩容
    reserve(_capacity == 0 ? 4 : _capacity * 2);
    }
    // 向后挪动数据
    size_t end = _size + 1;
    while (end > pos) {
    _str[end] = _str[end - 1];
    end--;
    }
    // 插入
    _str[pos] = append_ch;
    _size++;
    return *this;
  }
  string& insert(size_t pos, const char* append_str) {
    assert(pos <= _size);
    size_t len = strlen(append_str);
    if (_size + len > _capacity) {    // 检查是否需要增容
    reserve(_size + len);
    }
    // 向后挪动数据
    size_t end = _size + len;
    while (end > pos + len - 1) {
    _str[end] = _str[end - len];
    end--;
    }
    // 插入
    strncpy(_str + pos, append_str, len);
    _size += len;
    return *this;
  }
  /* resize */
  void resize(size_t new_capacity, char init_ch = '\0') {
    // 如果欲增容量比_size小
    if (new_capacity <= _size) {
    _str[new_capacity] = '\0';      // 拿斜杠零去截断
    _size = new_capacity;           // 更新大小
    }
    // 欲增容量比_size大
    else {
    if (new_capacity > _capacity) {
      reserve(new_capacity);
    }
    // 起始位置,初始化字符,初始化个数
    memset(_str + _size, init_ch, new_capacity - _size);
    _size = _capacity;
    _str[_size] = '\0';
    }
  }
  /* find */
  size_t find(char aim_ch) {
    for (size_t i = 0; i < _size; i++) {
    if (aim_ch == _str[i]) {
      // 找到了
      return i;    // 返回下标
    }
    }
    // 找不到
    return npos;
  }
  size_t find(const char* aim_str, size_t pos = 0) {
    const char* ptr = strstr(_str + pos, aim_str);
    if (ptr == nullptr) {
    return npos;
    }
    else {
    return ptr - _str;  // 减开头
    }
  }
  /* 删除:erase */
  string& erase(size_t pos, size_t len = npos) {
    assert(pos < _size);
    if (len == pos || pos + len >= _size) {
    _str[pos] = '\0';    // 放置\0截断
    _size = pos;
    }
    else {
    strcpy(_str + pos, _str + pos + len);
    _size -= len;
    }
    return *this;
  }
  /* 析构函数 */
  ~string() {
    if (_str != nullptr) {
    delete[] _str;
    _str = nullptr;
    }
    _size = _capacity = 0;
  }
  private:
  /* 成员变量 */
  char* _str;
  size_t _size;
  size_t _capacity;
  public:
  static const size_t npos;
  };
  /* 初始化npos */
  const size_t string::npos = -1;   // 无符号整型的-1,即整型最大值
  /* s1 < s2*/
  bool operator<(const string& s1, const string& s2) {
  /*
  size_t i1 = 0, i2 = 0;
  while (i1 < s1.size() && i1 < s2.size()) {
    if (s1[i1] < s2[i2]) {
    return true;
    }
    else if (s1[i1] > s2[i2]) {
    return false;
    }
    else {
    i1++;
    i2++;
    }
  }
  return i2 < s2.size() ? true : false;
  */
  return strcmp(s1.c_str(), s2.c_str()) < 0;
  }
  /* s1 == s2 */
  bool operator==(const string& s1, const string& s2) {
  return strcmp(s1.c_str(), s2.c_str()) == 0;
  }
  /* s1 <= s2 */
  bool operator<=(const string& s1, const string& s2) {
  return s1 < s2 || s1 == s2;
  }
  /* s1 > s2 */
  bool operator>(const string& s1, const string& s2) {
  return !(s1 <= s2);
  }
  /* s1 >= s2 */
  bool operator>=(const string& s1, const string& s2) {
  return !(s1 < s2);
  }
  /* s1 != s2 */
  bool operator!=(const string& s1, const string& s2) {
  return !(s1 == s2);
  }
  // cout << s1  →  operator<<(cout, s1)
  ostream& operator<<(ostream& out, const string& s) {
  /*
  for (auto ch : s) {
    out << ch;
  }
  */
  for (size_t i = 0; i < s.size(); i++) {
    out << s[i];
  }
  return out;
  }
  // cin >>
  istream& operator<<(istream& in, string& s) {
  char ch = in.get();
  while (ch == '\n') {
    s += ch;
    ch = in.get();
  }
  return in;
  }
  /* 测试用 */
  void test_string1() {
  string s1("hello world");
  string s2(s1);
  cout << s1.c_str() << endl;
  cout << s2.c_str() << endl;
  string s3("pig");
  cout << s3.c_str() << endl;
  s1 = s3;
  cout << s1.c_str() << endl;
  }
  void test_string2() {
  string s1("hello world");
  string s2;
  for (size_t i = 0; i < s1.size(); i++) {
    cout << s1[i] << " ";
  }
  cout << endl;
  }
  void test_string3() {
  string s1("hello world");
  string s2;
  s1[0] = 'F';
  for (size_t i = 0; i < s1.size(); i++) {
    cout << s1[i] << " ";
  }
  cout << endl;
  }
  void test_string4() {
  string s1("hello world");
  // 迭代器写
  string::iterator it = s1.begin();
  while (it != s1.end()) {
    *it += 1;
    it++;
  }
  // 迭代器读
  it = s1.begin();   // 重置起点
  while (it != s1.end()) {
    cout << *it << " ";
    it++;
  }
  }
  void test_string5() {
  string s1("hello world");
  cout << s1.c_str() << endl;
  s1.push_back('!');
  cout << s1.c_str() << endl;
  s1.push_back('A');
  cout << s1.c_str() << endl;
  }
  void test_string6() {
  string s1("hello world");
  cout << s1.c_str() << endl;
  s1 += '!';
  cout << s1.c_str() << endl;
  s1 += "this is new data";
  cout << s1.c_str() << endl;
  }
  void test_string7() {
  string s1("hello world");
  cout << s1.c_str() << endl;
  s1.insert(0, 'X');
  cout << s1.c_str() << endl;
  s1.insert(0, "hahahaha");
  cout << s1.c_str() << endl;
  }
  void test_string8() {
  string s1("hello world");
  cout << s1.c_str() << endl;
  s1.erase(5, 2);   // 从第五个位置开始,删两个字符
  cout << s1.c_str() << endl;
  s1.erase(5, 20);  // 从第五个位置开始,删完
  cout << s1.c_str() << endl;
  }
}


相关文章
|
4天前
|
算法 C++ 容器
【C++】string模拟实现
【C++】string模拟实现
12 1
|
4天前
|
C语言 C++
【C++】string类(常用接口)
【C++】string类(常用接口)
21 1
|
3天前
|
存储 算法 搜索推荐
C++|STL简介-string-vector基础运用
C++|STL简介-string-vector基础运用
|
4天前
|
C语言 C++ 容器
C++ string类
C++ string类
9 0
|
4天前
|
编译器 C++
【C++】继续学习 string类 吧
首先不得不说的是由于历史原因,string的接口多达130多个,简直冗杂… 所以学习过程中,我们只需要选取常用的,好用的来进行使用即可(有种垃圾堆里翻美食的感觉)
9 1
|
4天前
|
算法 安全 程序员
【C++】STL学习之旅——初识STL,认识string类
现在我正式开始学习STL,这让我期待好久了,一想到不用手撕链表,手搓堆栈,心里非常爽
16 0
|
4天前
|
存储 安全 测试技术
【C++】string学习 — 手搓string类项目
C++ 的 string 类是 C++ 标准库中提供的一个用于处理字符串的类。它在 C++ 的历史中扮演了重要的角色,为字符串处理提供了更加方便、高效的方法。
18 0
【C++】string学习 — 手搓string类项目
|
4天前
|
存储 C++ 容器
【C++从练气到飞升】09---string语法指南(二)
【C++从练气到飞升】09---string语法指南(二)
|
4天前
|
存储 Linux C语言
【C++从练气到飞升】09---string语法指南(一)
【C++从练气到飞升】09---string语法指南(一)
|
4天前
|
C++
【C++】string类(介绍、常用接口)
【C++】string类(介绍、常用接口)
18 2