C++STL——string类与模拟实现(下)

简介: C++STL——string类与模拟实现

访问及遍历操作

operator[]返回对象下标的元素
begin+ end迭代器,begin是字符串最开始的元素,end是字符串末尾的‘\0’
rbegin + rend反向迭代器

char& operator[] (size_t pos);

const char& operator[] (size_t pos) const;

iterator begin();

const_iterator begin() const;

iterator end();

const_iterator end() const;

reverse_iterator rbegin();

const_reverse_iterator rbegin() const;

reverse_iterator rend();

const_reverse_iterator rend() const;

#include <iostream>
#include <string>
using namespace std;
int main()
{
  string s("qwert");
  cout << s[2] << endl;
  s = "123456";
  string::iterator left1 = s.begin();//可读可写
  string::const_iterator left2 = s.begin();//可读不可写
  while (left1 != s.end())
  {
    *left1 += 1;
    cout << *left1;
    left1++;
  }
  cout << endl;
  while (left2 != s.end())
  {
    cout << *left2;
    left2++;
  }
  cout << endl;
  string::reverse_iterator left3 = s.rbegin();
  string::const_reverse_iterator left4 = s.rbegin();
  while (left3 != s.rend())
  {
    *left3 += 1;
    cout << *left3;
    left3++;
  }
  cout << endl;
  while (left4 != s.rend())
  {
    cout << *left4;
    left4++;
  }
  cout << endl;
  return 0;
}

C++11语法中的for循环就是与迭代器有关。

这里还有一个类似于operator[]的接口是at,找到的就返回,找不到会抛异常。

string中的swap与C++库中的swap的区别

string::swap

void swap (string& x, string& y);

交换两个同类对象中的成员

#include <iostream>
#include <assert.h>
using namespace std;
int main()
{
  string s1("asdq7777777777777777777");
  string s2("qwe");
  s1.swap(s2);
  cout << s1 << endl;
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
  cout << s2 << endl;
  cout << s2.size() << endl;
  cout << s2.capacity() << endl;
  return 0;
}

C++库swap

这是一个模板

template <class T> void swap ( T& a, T& b )
{
  T c(a); a=b; b=c;//这里其实就相当于调用了构造函数,因为T会有自定义类型
}

平时我们的内置类型都是不需要进行析构和构造的,但是内置类型进入模板之后就需要了。

使用时要注意深浅拷贝问题,自定义类型要调用 = 。

非成员函数

operator>>
operator<<流插入流提取

istream& operator>> (istream& is, string& str);

ostream& operator<< (ostream& os, const string& str);

这个就很熟悉了上面不知道用了多少。

string类的模拟实现

#include <iostream>
#include <assert.h>
using namespace std;
namespace baiye
{
  class string
  {
  public:
    typedef char* iterator;
    string(const char* s = "")//对象初始化,缺省值有一个隐藏的\0
    {
      _arr = new char[strlen(s) + 1];
      strcpy(_arr, s);
      _size = strlen(_arr);
      _capacity = _size;//实际可以储存的大小,不算\0的位置
    }
    ~string()//析构
    {
      delete[] _arr;
      _arr = nullptr;
      _size = 0;
      _capacity = 0;
    }
    string(const string& s)//拷贝构造
      :_arr(nullptr)
      ,_size(0)
      ,_capacity(0)
    {
      string tmp(s._arr);
      swap(tmp);
    }
    string& operator=(string s)//这里不用引用就是临时变量,我们可以让它成为打工人
    {
      swap(s);
      return *this;
    }
    //iterator
    iterator begin()const
    {
      return _arr;
    }
    iterator end()const
    {
      return _arr + _size - 1;
    }
    // modify
    const char* c_str()const
    {
      return _arr;
    }
    void push_back(char c)
    {
      if (_size == _capacity)
      {
        size_t len = _size == 0 ? 4 : _capacity * 2;//防止空对象扩容失败
        reserve(len);
      }
      _arr[_size] = c;
      _size++;//注意\0
      _arr[_size] = '\0';
    }
    void append(const char* str)
    {
      if (_size + strlen(str) > _capacity)
      {
        reserve(_size + strlen(str) + 1);//不能二倍扩容,万一新加的字符串更大就不行了
      }
      strcpy(_arr + _size, str);
      _size = strlen(_arr);
    }
    string& operator+=(char c)
    {
      push_back(c);
      return *this;
    }
    string& operator+=(const char* str)
    {
      append(str);
      return *this;
    }
    void clear()
    {
      _arr[0] = '\0';
      _size = 0;
    }
    void swap(string& s)
    {
      std::swap(_arr, s._arr);
      std::swap(_size, s._size);
      std::swap(_capacity, s._capacity);
    }
    //capacity
    size_t size()const//返回长度
    {
      return _size;
    }
    size_t capacity()const//返回容量
    {
      return _capacity;
    }
    bool empty()const
    {
      return _size;
    }
    void resize(size_t n, char c = '\0')
    {
      if (n <= _size)
      {
        _arr[n] = '\0';
        _size = n;
      }
      else
      {
        reserve(n);
        while (_size < n)
        {
          _arr[_size] = c;
          _size++;
        }
        _arr[n] = '\0';
        _size = n;
      }
    }
    void reserve(size_t n)
    {
      char* tmp = new char[n + 1];//+1是为了算\0
      strcpy(tmp, _arr);
      delete[] _arr;
      _arr = tmp;
      _capacity = n;
    }
    // access
    char& operator[](size_t x)//可读可写
    {
      assert(x < _size);
      return _arr[x];
    }
    const char& operator[](size_t x)const//可读不可写
    {
      assert(x < _size);
      return _arr[x];
    }
    //relational operators
    int judge(const string& s)
    {
      return strcmp(_arr, s._arr);//1大于,0等于,-1小于
    }
    bool operator<(const string& s)
    {
      int a = judge(s);
      if (a < 0)
        return 1;
      return 0;
    }
    bool operator<=(const string& s)
    {
      int a = judge(s);
      if (a > 0)
        return 0;
      return 1;
    }
    bool operator>(const string& s)
    {
      int a = judge(s);
      if (a > 0)
        return 1;
      return 0;
    }
    bool operator>=(const string& s)
    {
      int a = judge(s);
      if (a < 0)
        return 0;
      return 1;
    }
    bool operator==(const string& s)
    {
      return !judge(s);
    }
    bool operator!=(const string& s)
    {
      return judge(s);
    }
    // 返回c在string中第一次出现的位置
    size_t find(char c, size_t pos = 0) const
    {
      assert(pos < _size);
      while (pos < _size)
      {
        if (_arr[pos] == c)
          return pos;
        pos++;
      }
      return npos;
    }
    // 返回子串s在string中第一次出现的位置
    size_t find(const char* s, size_t pos = 0) const
    {
      assert(pos < _size);
      const char * p =strstr(_arr + pos, s);
      if (p == nullptr)
      {
        return npos;
      }
      else
      {
        return p - _arr;
      }
    }
    // 在pos位置上插入字符c/字符串str,并返回该字符的位置
    string& insert(size_t pos, char c)
    {
      if (_size == _capacity)
      {
        size_t len = _size == 0 ? 4 : _capacity * 2;//防止空对象扩容失败
        reserve(len);
      }
      int end = _size + 1;
      while (end > pos)
      {
        _arr[end] = _arr[end - 1];
        end--;
      }
      _arr[pos] = c;
      _size++;
      return *this;
    }
    string& insert(size_t pos, const char* str)
    {
      int len = strlen(str);
      if (_size + len > _capacity)
      {
        reserve(_size + len + 1);//不能二倍扩容,万一新加的字符串更大就不行了
      }
      int end1 = _size + 1;
      int end2 = _size + len;
      while (end1 > pos)
      {
        _arr[end2] = _arr[end1 - 1];
        end1--;
        end2--;
      }
      strncpy(_arr + pos, str, len);
      _size = strlen(_arr);
      return *this;
    }
    // 删除pos位置上的元素,并返回该元素
    string& erase(size_t pos, size_t len = npos)//nops是默认值
    {
      if (len == npos || pos + len >= _size)//删除pos后面所有的数据
      {
        _arr[pos] = '\0';
        _size = pos;
      }
      else
      {
        strcpy(_arr + pos, _arr + pos + len);
        _size -= len;
      }
      return *this;
    }
  private:
    char* _arr;//字符串储存的地方
    int _size;//大小
    int _capacity;//容量
    const static size_t npos = -1;//C++只允许整形的静态成员在类的内部进行初始化
  };
  ostream& operator<<(ostream& out, const string& s)//不一定非要是友元
  {
    for (int i = 0; i < s.size(); i++)
    {
      out << s[i];
    }
    return  out;
  }
  istream& operator >> (istream& in, string& s)
  {
    s.clear();//清空原来s中的内容
    char buff[128] = { '\0' };//创建一个容纳输入数据的数组
    char c = in.get();
    int i = 0;
    while (c != '\n')
    {
      if (i == 127)
      {
        i = 0;
        s += buff;//数组满了放入s中,防止s不断扩容
      }
      buff[i++] = c;
      c = in.get();
    }
    if (i > 0)
    {
      buff[i] = '\0';
      s += buff;
    }
    return in;
  }
}

深浅拷贝与现代写法

这里最重要的是深浅拷贝的现代写法,之前说过类的默认拷贝函数只能实现浅拷贝无法实现深拷贝,需要自定义拷贝函数,类与对象二

现代写法是让一个新创建的对象帮你拷贝,然后与你进行交换,相当于你有了一个打工人。

string(const string& s)//拷贝构造
  :_arr(nullptr)//将被拷贝的对象进行初始化
  ,_size(0)
  ,_capacity(0)
{
  string tmp(s._arr);//打工人tmp
  swap(tmp);//这里虽然只传参了一个tmp,但是还有一个隐藏的this指针
}
string& operator=(string s)//这里不用引用就是临时变量,我们可以让它成为打工人
{
  swap(s);
  return *this;
}
void swap(string& s)
{
  std::swap(_arr, s._arr);
  std::swap(_size, s._size);
  std::swap(_capacity, s._capacity);
}

之后tmp的成员 _arr指向的就是空指针,进行析构也不会报错,其他成员全都变成0,而需要被拷贝的*this就成功的得到了想要的数据。

相关文章
|
5天前
|
存储 编译器 C++
C ++初阶:类和对象(中)
C ++初阶:类和对象(中)
|
5天前
|
C++
C++(十六)类之间转化
在C++中,类之间的转换可以通过转换构造函数和操作符函数实现。转换构造函数是一种单参数构造函数,用于将其他类型转换为本类类型。为了防止不必要的隐式转换,可以使用`explicit`关键字来禁止这种自动转换。此外,还可以通过定义`operator`函数来进行类型转换,该函数无参数且无返回值。下面展示了如何使用这两种方式实现自定义类型的相互转换,并通过示例代码说明了`explicit`关键字的作用。
|
5天前
|
存储 设计模式 编译器
C++(十三) 类的扩展
本文详细介绍了C++中类的各种扩展特性,包括类成员存储、`sizeof`操作符的应用、类成员函数的存储方式及其背后的`this`指针机制。此外,还探讨了`const`修饰符在成员变量和函数中的作用,以及如何通过`static`关键字实现类中的资源共享。文章还介绍了单例模式的设计思路,并讨论了指向类成员(数据成员和函数成员)的指针的使用方法。最后,还讲解了指向静态成员的指针的相关概念和应用示例。通过这些内容,帮助读者更好地理解和掌握C++面向对象编程的核心概念和技术细节。
|
12天前
|
存储 算法 编译器
[C++] STL简介
[C++] STL简介
10 1
|
5天前
|
存储 C++
C++(五)String 字符串类
本文档详细介绍了C++中的`string`类,包括定义、初始化、字符串比较及数值与字符串之间的转换方法。`string`类简化了字符串处理,提供了丰富的功能如字符串查找、比较、拼接和替换等。文档通过示例代码展示了如何使用这些功能,并介绍了如何将数值转换为字符串以及反之亦然的方法。此外,还展示了如何使用`string`数组存储和遍历多个字符串。
|
12天前
|
缓存 安全 Java
Java String类
Java String类
12 0
|
14天前
|
存储 C++
C++ dll 传 string 类 问题
C++ dll 传 string 类 问题
15 0
|
3月前
|
Java UED
Java中String强转int:一种常见的错误和解决方法
在Java中将非数字字符串转换为整数会导致`NumberFormatException`。要解决这个问题,可以使用`try-catch`捕获异常,正则表达式验证数字格式,或利用异常信息提供错误提示。例如,`Integer.parseInt()`会因遇到非数字字符如`&quot;123abc&quot;`而抛出异常,但通过异常处理或正则`\\d+`可确保安全转换。记得在编程时避免直接强转,以防止程序异常中断。
|
24天前
|
前端开发 Java
成功解决:java.lang.String cannot be cast to java.lang.Integer
这篇文章记录了作者在使用Axios二次封装时遇到的一个Java类型转换问题,即前端传递的字符串参数不能直接转换为Integer类型,文章提供了正确的转换方法来解决这个问题。
成功解决:java.lang.String cannot be cast to java.lang.Integer
|
19天前
|
安全 Java API
Java系类 之 String、StringBuffer和StringBuilder类的区别
这篇文章讨论了Java中`String`、`StringBuffer`和`StringBuilder`三个类的区别,其中`String`是不可变的,而`StringBuffer`是线程安全的可变字符串类,`StringBuilder`是非线程安全的可变字符串类,通常在单线程环境下性能更优。
Java系类 之 String、StringBuffer和StringBuilder类的区别