c++学习之string实现

简介: c++学习之string实现

字符串 - C++引用 (cplusplus.com)这里给出标准官方的string实现,可以看到设计还是较为复杂的,有成员函数,迭代器,修饰符,容量,元素访问,字符串操作等,将字符尽可能的需求都设计出来,我们这里实现string中比较常用且重要的。

成员变量

private:
  char* _str;
  size_t _size;
  size_t _capacity;
public:
  const static size_t npos;//声明一个npos 表示返回找不到的下标 或从-1的下一个开始寻找

1.迭代器实现

这里我们实现了常量迭代器与正常的迭代器,反向迭代器并未实现。对于string这里的迭代器我们可以看到本质上是一个char *类型的指针,在某些地方可能不是指针,且它的用法也是与指针如出一撤。这里对于反向迭代器后面再实现。

    typedef char* iterator;//迭代器
  const typedef 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;
  };

在这里, 我们将char*定义为一个新的类型,并且给出它的成员函数,也就是迭代器的各个位置,从而实现。利用迭代器我们可以对他进行遍历:

Mystring::iterator it = str1.begin();
  while (it != str1.end())
  {
    cout << *it ;
    ++it;
  }
 //范围for
  for (auto tmp : str1)
 {
   cout << tmp ;
 }

同时其实本质上范围for的底层就是利用迭代器实现的,编译器在遇到范围for这样的写法时就会将这一段代码替换成下面迭代器实现的这样的遍历。

2.成员函数

空构造

实际上并不需要实现,我们这里给出它的实现,是了解对于空字符串的构造函数,虽说是空字符串,但不可直接赋值nullptr,否则后面实例化对象调成员函数时就是解引用空指针,空字符串,故这里会报错,实际上实现会给几个字节空间,放\0

Mystring():_str(new char[1]),_capacity(0),_size(0)
  {
  }

有参构造

  //有参构造
  Mystring(const char* str="") : _size(strlen(str)), _capacity(_size)//缺省值直接给"",不给"\0",原先就有\0
  {
    //注意这里的字符串利用深拷贝,且初始化顺序遵循声明顺序
    _str = new char[_capacity + 1];//多开一个给\0
    strcpy(_str, str);
  }

拷贝构造

还是老问题,直接的赋值会造成两次析构,故这里需要进行深拷贝。

//拷贝构造
  //之前讲过,浅拷贝会析构两次,深拷贝解决
  Mystring(const Mystring& s)
  {
    _str = new char[s._capacity + 1];
    memset(_str, 0, s._capacity);
    strcpy(_str, s._str);
    this->_size = s._size;
    this->_capacity = s._capacity;
  }

析构

释放及初始化

//析构
  ~Mystring()
  {
    delete [] _str;
    _str = nullptr;
    _capacity = _size = 0;
  }

重载=

// 赋值重载
  Mystring& operator=(const Mystring& s)
  {
    if (*this != s)
    {
      char* temp = new char[s._capacity+1];
      strcpy(temp, s._str);
      delete []_str;
        _str = temp;
      this->_size = s._size;
      this->_capacity = s._capacity;
    }
    return *this;
  }

这里的赋值重载区分拷贝构造:

用现有对象初始化定义的对象---------拷贝构造

用现有对象赋值给现有对象------------赋值

3.字符串操作

这里实现几个比较重要的,常用的。

c_str

const char* c_str()const
  {
    return _str;
  }

find

这里给出了两种实现,字符寻找与字符串寻找

//寻找某个字符,返回其下标位置
  size_t find(char ch,size_t pos=0)
  {
    for (size_t i = pos; i < _size; i++)
    {
      if (ch == _str[i])
      {
        return i;
      }
    }
    return npos;//找不到,返回-1
  }
  //寻找某个字符串,返回其相同的个数
  size_t find(const char* ch, size_t pos = 0)
  {
    //暴力匹配
    const char* tmp = strstr(_str+pos, ch);
    if (tmp!= nullptr)
    {
      return tmp - _str;//指针相减,找到个数
    }
    else
    {
      return npos;
    }
  }

substr

//从pos位置处取n个字符,或n到 npos
  Mystring substr(size_t pos, size_t len=npos)
  {
    assert(pos < _size);
    Mystring s;
    size_t end = pos + len;
    if (len == pos || len + pos >= _size)
    {
      len = _size - len;
      end = _size;
    }
    s.reserve(len);
    for (size_t i = pos; i <pos+len; i++)
    {
        s += _str[i];//这里利用重载后的+=,本质上就是尾插
    }
    return s;
  }

5.修饰符

push_back

  //尾插字符
  void push_back(char ch)
  {
    //尾插的操作很简单,但是要考虑到扩容的问题
    if (_size == _capacity)
    {
      reserve(_capacity=0?4:_capacity * 2);//为了避免空间是0,扩容失败
    }
    _str[_size] = ch;
    ++_size;
    _str[_size] = '\0';
  }

append

//尾插字符串
  void append(const char*ch)
  {
    size_t num = strlen(ch);
    if (num + _size > _capacity)
    {
      reserve(num + _size);
    }
    strcpy(_str + _size, ch);
    _size += num;
    //注意这里直接运用strcpy拷贝过来,连同\0一起,之后变为新大小
  }

operator+=

这里重载的+=是利用了尾插的实现,故也作为字符串修饰

也是两种实现,+=字符或字符串

Mystring& operator+=(const char ch)
  {
    push_back(ch);
    return *this;
  }
  Mystring& operator+=(const char* ch)
  {
    append(ch);
    return *this;
  }

insert

也是有两种实现方式

注意注释的提示:若这里想要进行头插,此时end>=0。只有当end--到负数才停止,而size_t 无法去判断正负数,程序崩溃,解决方法,将类型转换为int ,或者修改后移顺序,_str[end] = _str[end-1];去掉条件=。

其次就是对于扩容需要考虑空间为0的情况。

//某个位置插入字符
  void insert(size_t pos,char ch)
  {
    assert(pos < _size);
    if (_size == _capacity)
    {
      reserve(_capacity = 0 ? 4 : _capacity * 2);//为了避免空间是0,扩容失败
    }
    int end = _size;
    //pos位置处后移
    while (end>=(int)pos)
    {
      //后移
      _str[end + 1] = _str[end];
      --end;
    }
    //插入
    _str[pos] = ch;
  }
  //某个位置插入字符串
  void insert(size_t pos,char *ch)
  {
    size_t len = strlen(ch);
    if (_size +len>_capacity)
    {
      reserve(_capacity = 0 ? 4 : _capacity * 2);//为了避免空间是0,扩容失败
    }
    size_t end = _size;
    //pos位置处后移
    while (end >= pos)
    {
      //后移
      _str[end + len] = _str[end];
      --end;
    }
    //插入
    strncpy(_str+pos, ch,len);
    _size += len;
  }

erase

删除pos位置后的npos个字符,这里的npos给的是缺省值,再没参数的情况下为-1,也就是pos后的全部删除。

//删除
  void erase(size_t pos,size_t len= npos)
  {
    assert(pos <= _size);
    if (len == npos || pos + len >= _size)
    {
      _str[pos] = '\0';
      _size = pos;
    }
    else
    {
      size_t begin = pos + len;
      while (begin <= _size)
      {
        _str[begin- len] = _str[begin ];
        ++begin;
      }
      _size -= len;
    }
  }

6.容量

reserve

//扩容
  void reserve(size_t n)
  {
    if (n > _size)
    {
      char* tmp = new char[n + 1];//这里还是留一个\0位置
      strcpy(tmp, _str);
      delete[]_str;
      _str = tmp;
      _capacity = n;
    }
  }

size

//返回大小
size_t size()const
  {
    return _size;
  }

capacity

//返回容量
size_t capacity()const
  {
    return _capacity;
  }

resize

//初始化大小为n,且可以赋值之后的
  void resize(size_t n,char c='\0')
  {
    if (n <= _size)
    {
      //删除
      _str[n] = '\0';
      _size = n;
    }
    else
    {
      //扩容
      reserve(n);
      //初始化值
      while (_size < n)
      {
        _str[_size] = c;
        _size++;
      }
      //末尾给\0
      _str[_size] = '\0';
    }
  }

7.元素访问

重载下标引用操作符

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

8.运算符重载

对于这里的比较运算符,通过实现<与==,再取反实现> ,>=,!=.

重载<

bool operator<(const Mystring &s1)
  {
    return strcmp(this->_str, s1._str) <0;
  }

重载==

bool operator==(const Mystring& s)
  {
    return strcmp(this->_str, s._str) == 0;
  }

重载<=

bool operator<=(const Mystring& s)
  {
    return *this < s || *this == s;//由于<与=已实现,直接用
  }

重载>

bool operator>(const Mystring& s)
  {
    return !( *this <=s) ;//取反
  }

重载>=

bool operator>=(const Mystring& s)
  {
    return !(*this < s);//取反
  }

重载!=

bool operator!=(const Mystring& s)
  {
    return!(*this==s);
  }

重载输入流

//流插入
istream& operator>>(istream& in, Mystring& s)
{
  char ch;
  ch = in.get();//一个个拿字符,包括空格与\n
  while (ch != ' ' && ch != '\n')
  {
    s += ch;
    ch = in.get();
  }
  return in;
}

重载输出流

//流提取
ostream& operator<<(ostream& out, const Mystring& s)
{
  for (size_t i = 0; i < s.size(); i++)
  {
    out << s[i];
  }
  return out;
}


相关文章
|
2月前
|
安全 编译器 C语言
【C++数据结构】string的模拟实现
【C++数据结构】string的模拟实现
|
2月前
|
算法 C语言 C++
C++语言学习指南:从新手到高手,一文带你领略系统编程的巅峰技艺!
【8月更文挑战第22天】C++由Bjarne Stroustrup于1985年创立,凭借卓越性能与灵活性,在系统编程、游戏开发等领域占据重要地位。它继承了C语言的高效性,并引入面向对象编程,使代码更模块化易管理。C++支持基本语法如变量声明与控制结构;通过`iostream`库实现输入输出;利用类与对象实现面向对象编程;提供模板增强代码复用性;具备异常处理机制确保程序健壮性;C++11引入现代化特性简化编程;标准模板库(STL)支持高效编程;多线程支持利用多核优势。虽然学习曲线陡峭,但掌握后可开启高性能编程大门。随着新标准如C++20的发展,C++持续演进,提供更多开发可能性。
49 0
|
2月前
|
编译器 C++ 容器
【C++】String常见函数用法
【C++】String常见函数用法
17 1
|
1月前
|
存储 C++
C++(五)String 字符串类
本文档详细介绍了C++中的`string`类,包括定义、初始化、字符串比较及数值与字符串之间的转换方法。`string`类简化了字符串处理,提供了丰富的功能如字符串查找、比较、拼接和替换等。文档通过示例代码展示了如何使用这些功能,并介绍了如何将数值转换为字符串以及反之亦然的方法。此外,还展示了如何使用`string`数组存储和遍历多个字符串。
|
2月前
|
Go
Go - 学习 grpc.Dial(target string, opts …DialOption) 的写法
Go - 学习 grpc.Dial(target string, opts …DialOption) 的写法
47 12
|
2月前
|
安全 程序员 C++
C++ --> string类的使用(详细介绍)
C++ --> string类的使用(详细介绍)
41 5
|
2月前
|
存储 编译器 C语言
C++ --> string类模拟实现(附源码)
C++ --> string类模拟实现(附源码)
63 4
|
2月前
|
存储 C++
C++ dll 传 string 类 问题
C++ dll 传 string 类 问题
21 0
|
2月前
|
存储 C++
【C/C++学习笔记】string 类型的输入操作符和 getline 函数分别如何处理空白字符
【C/C++学习笔记】string 类型的输入操作符和 getline 函数分别如何处理空白字符
32 0
|
3月前
|
C++
C++ string中的函数和常用用法
C++ 中string中的函数和常用用法
29 4
下一篇
无影云桌面