当前位置: 代码网 > it编程>编程语言>C/C++ > 『C++成长记』string模拟实现

『C++成长记』string模拟实现

2024年07月28日 C/C++ 我要评论
本篇我们将为大家介绍string常用接口的模拟实现。

🔥博客主页:

📚系列专栏:c++

🌟人之为学,不日近则日退

❤️感谢大家点赞👍收藏⭐评论✍️

目录

一、存储结构

二、默认成员函数

📒2.1构造函数

📒2.2析构函数

📒2.3拷贝构造

📒2.4赋值重载

三、容量操作

📒3.1获取有效字符长度

📒3.2获取对象空间大小

📒3.3使用reserve扩容

四、字符串的遍历

📒4.1下标访问

📒4.2迭代器访问

五、修改操作

📒5.1尾插字符

📒5.2尾插字符串

📒5.3任意位置插入字符

📒5.4任意位置插入字符串

📒5.5+=重载

六、其他操作

📒6.1删除操作

📒6.2查找操作

📒6.3交换操作

📒6.4获取字符串

📒6.5运算符重载

📒6.6清理字符串

📒6.7流操作


🗒️前言:

一、存储结构

        string本质上是一个char类型的顺序表,所以结构上和顺序表类似。

namespace bit
{
    class string
    {
    public:

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

        const static size_t npos;
    };
}

结构上使用命名空间 bit 进行封装,防止与库冲突,其中:

  • _str :指向存放字符串存空间的指针
  • _size :表示当前所存储的有效字符个数
  • _capacity :表示当前可存储的最大容量
  • nops:此值设置为 -1,无符号整型转换就是42亿,且此值为const和静态参数具有全局效应,这个值常常被用来当作循环结束判断条件和查找时未找到的标志,某些函数设置其为缺省参数。

二、默认成员函数

📒2.1构造函数

string.h
string(const char* str = "");  //给缺省值 构造空串

string.cpp
string::string(const char* str)
    :_size(strlen(str))
{
    _str = new char[_size + 1];
    _capacity = _size;
    strcpy(_str, str);
}

📒2.2析构函数

        我们开辟内存是使用 new[ ] 申请的,所以对应使用 delete[ ]释放

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

📒2.3拷贝构造

        拷贝构造函数,如果我们不写,编译器会默认生成一个,但是默认生成的拷贝构造函数只支持浅拷贝,新构造的对象只是拷贝了_str的指针地址,两个对象都指向同一块空间,最终两个对象析构时释放同一片空间的资源势必会导致程序崩溃

我们需要新构造的对象通过拷贝构造开辟一片新的空间将数据复制过去,也就是深拷贝,需要我们自己写一个拷贝构造。

📒2.4赋值重载

        赋值重载需要注意自己给自己赋值这种冗余的行为,同时也要控制空间大小

三、容量操作

📒3.1获取有效字符长度

size_t string::size() const
{
    return _size;
}

小tips

  • 这个函数比较小,可以写在类中形成内联函数。 
  • 对于不涉及对字符串的增删查改的函数,使用const修饰this增强安全性。

📒3.2获取对象空间大小

        与size函数规则一致。

size_t string::capacity() const
{
    return _capacity;
}

📒3.3使用reserve扩容

void string::reserve(size_t n)
{
    char* tmp = new char[n + 1];
    strcpy(tmp, _str);    //将原字符串的数据拷贝到新空间上
    delete[] _str;        //释放原字符串的空间
    _str = tmp;
    _capacity = n;
}

四、字符串的遍历

📒4.1下标访问

        下标访问是通过重载 [ ] 运算符实现的,在下标pos正确的情况下,返回当前下标字符的引用,否则assert报错。

char& string::operator[](size_t pos)
{
    assert(pos < _size);
    return _str[pos];
}

📒4.2迭代器访问

        现在我们可以简单认为迭代器是指针对象,就是对指针的封装。

//迭代器的声明
typedef char* iterator;
typedef const char* const_iterator;  //对数据无法修改

迭代器的begin返回字符串的地址,end返回字符串末端的下一个即‘\0’

string::iterator string::begin()
{
    return _str;
}

string::iterator string::end()
{
    return _str + _size;
}

string::const_iterator string::begin()const
{
    return _str;
}

string::const_iterator string::end()const
{
    return _str + _size;
}

五、修改操作

📒5.1尾插字符

        在插入字符前,先要判断是否需要扩容。

void string::push_back(char ch)
{
    if (_size == _capacity)
    {
        size_t newcapacity = _capacity == 0 ? 4: _capacity * 2;
        reserve(newcapacity);
    }
    _str[_size] = ch;
    _str[_size + 1] = '\0';
    _size++;
}

📒5.2尾插字符串

void string::append(char* s)
{
    size_t len = strlen(s);
    if (_size +len > _capacity)
    {
        reserve(_size + len);
    }
    strcpy(_str + _size, s);
    _size += len;
}

📒5.3任意位置插入字符

        我们要考虑头插的位置,endpos的类型都是size_t,代码会陷入死循环,这里我们提供两种解决方法。

我们可以通过复用来实现尾插

void string::push_back(char ch)
{
    insert(_size, ch);
}

📒5.4任意位置插入字符串

void string::insert(size_t pos, const char* s)
{
    assert(pos <= _size);
    size_t len = strlen(s);
    if (_size + len > _capacity)
    {
        reserve(_size + len);
    }
    //方法一:
    //int end = _size;
    //while (end >= (int)pos )
    //{
    //    _str[end + len] = _str[end];
    //    --end;
    //}
    
    //方法二:
    size_t end = _size+len;
    while (end > pos+len-1)
    {
        _str[end] = _str[end - len];
        --end;
    }		
    memcpy(_str + pos, s, len);
    _size += len;
}

我们也可以通过复用来实现尾插字符串

void string::append(const char* str)
{
    insert(_size, str);
}

📒5.5+=重载

        +=运算符可以在当前字符串尾部追加字符或字符串,我们可以通过复用push_backappend函数来实现。

//追加字符
string& string::operator+=(char ch)
{
    push_back(ch);
    return *this;
}

//追加字符串
string& string::operator+=(const char* str)
{
    append(str);
    return *this;
}

六、其他操作

📒6.1删除操作

        erasepos下标开始删除len个字符,其中len是一个缺省参数,默认是npos如果没有传值或len超过从pos位置开始到字符串尾部的字符个数则默认从pos位置开始删除后面的所有字符,且不允许在空串的情况下进行删除!

void string::erase(size_t pos, size_t len)
{
    assert(pos < _size);
    if (len >= _size - pos)
    {
        _str[pos] = '\0';
        _size = pos;
    }
    else
    {
        strcpy(_str + pos, _str + pos + len);
        _size -= len;
    }
}

📒6.2查找操作

        查找函数是find,是从pos位置开始查找,可以查找一个字符或一个子串,查找到后字符返回下标,字符串返回首字符的地址,如果有多个重复的字符或字符串,返回查找到的第一个字符的下标或字符串首的下标;如果没找到则返回npos

📒6.3交换操作

void string::swap(string& s)
{
    std::swap(_str, s._str);
    std::swap(_size, s._size);
    std::swap(_capaicty, s._capaicty);
}

📒6.4获取字符串

string string::substr(size_t pos, size_t len)
{
    assert(pos < _size);
    if (len > _size - pos)
    {
        string sub(_str + pos);
        return sub;
    }
    else
    {
        string sub;
        sub.reserve(len);
        for (size_t i = 0; i < len; i++)
        {
            sub += _str[pos + i];
        }
        return sub;
    }
}

📒6.5运算符重载

        逻辑判断运算符只需要实现两个,其余的通过复用就可以全部实现。 

bool string::operator<(const string& s) const
{
    return strcmp(_str, s._str) < 0;
}

bool string::operator>(const string& s) const
{
    return !(*this <= s);
}

bool string::operator<=(const string& s) const
{
    return *this < s || *this == s;
}

bool string::operator>=(const string& s) const
{
    return !(*this < s);
}

bool string::operator==(const string& s) const
{
    return strcmp(_str, s._str) == 0;
}

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

📒6.6清理字符串

        clear函数支持清空一个字符串,但不是释放对象,区别于析构函数。clear函数清理字符串并不会引起缩容,只是在下标0位置置为 \0 _size置为0即可。

void string::clear()
{
    _str[0] = '\0';
    _size = 0;
}

📒6.7流操作

        流操作属于iostream中的对象,所以不需要定义在类中作为成员函数,也不需要声明为友元,因为使用流体去和流插入需要ostreamistream对象作为左操作参数。


🎁结语: 

     本次的内容到这里就结束啦。希望大家阅读完可以有所收获,同时也感谢各位读者三连支持。文章有问题可以在评论区留言,博主一定认真认真修改,以后写出更好的文章。你们的支持就是博主最大的动力。

 

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com