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


相关文章
|
4天前
|
算法 C++ Python
从入门到精通:C++ 学习路线指南,附详细学习计划
C++是一种高级编程语言,广泛用于开发操作系统、应用程序、游戏和各种工具。如果你想学习这门语言,以下是一个适合初学者的学习路线:
18 0
|
6天前
|
安全 C++
【C++】从认识using namespace std开始进入C++的学习
【C++】从认识using namespace std开始进入C++的学习
13 0
|
6天前
|
存储 安全 Java
【C++】引用之带你“消除”C语言版数据结构教材的一些困惑(虽然是C++的内容,但是强烈建议正在学习数据结构的同学点进来看看)
【C++】引用之带你“消除”C语言版数据结构教材的一些困惑(虽然是C++的内容,但是强烈建议正在学习数据结构的同学点进来看看)
22 0
|
6天前
|
人工智能 分布式计算 Java
【C++初阶】前言——C++的发展简述及学习方法分享
之前我们一直分享C语言和使用C语言完成数据结构的初阶的文章,今天我们正式进入C++的学习,这篇文章主要是给大家带来的是C++的由来、以及和C语言的区别、更主要的是和大家分享自己的学习方法,给一些我的建议。
|
16天前
|
C语言 C++
C++入门学习(3)函数重载
C++入门学习(3)函数重载
|
16天前
|
存储 安全 编译器
C++入门学习(4)引用 (讲解拿指针比较)
C++入门学习(4)引用 (讲解拿指针比较)
|
16天前
|
C语言 C++
C++入门学习(2)缺省参数
C++入门学习(2)缺省参数
|
16天前
|
编译器 C语言 C++
C++入门学习(1)命名空间和输入输出
C++入门学习(1)命名空间和输入输出
|
22天前
|
人工智能 物联网 编译器
【C++初阶(一)】学习前言 命名空间与IO流
【C++初阶(一)】学习前言 命名空间与IO流
26 0
|
1月前
|
存储 搜索推荐 Linux
5个值得学习的C++完整项目实战
5个值得学习的C++完整项目实战