【C++】string模拟实现(下)

简介: 本文档介绍了自定义`string`类的一些关键功能实现,包括`reserve()`用于内存管理,`push_back()`和`append()`添加字符或字符串,运算符`+=`的重载,以及`insert()`, `erase()`进行插入和删除操作。此外,还涵盖了`find()`查找函数,字符串的比较运算符重载,`substr()`获取子串,`clear()`清除内容,以及流插入和提取操作。常量`npos`用于表示未找到的标记。文档以代码示例和运行结果展示各功能的使用。

==reserve预留空间==

在开始字符串增删查改之前,有必要介绍这个函数,使用它可以更好的控制我们string对象的内存管理。

void string::reserve(size_t n)
{
   
   
    if (_capacity < n) {
   
   
        char* tmp = new char[n + 1];
        strcpy(tmp, _str);
        delete[] _str;
        _str = tmp;
        _capacity = n;
    }
}

当n大于当前容量_capacity的时候,进行空间的开辟,将_str中的内容拷贝到新空间中去,同时delete释放旧空间。

==尾插字符和字符串==

接口函数,一个是push_back,一个是append。

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

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

这两个成员函数都使用了reserve来预留空间。
同时还需要有 运算符重载+= 来实现尾插,用重载的运算符执行这样的操作才是最爽的。

string& string::operator+=(char ch)
{
   
   
    push_back(ch);
    return *this;
}

string& string::operator+=(const char* str)
{
   
   
    append(str);
    return *this;
}

其实不用再实现一遍了,直接复用就行。
可以简单看一下使用的效果:

string str("hello world!");
char ch = 'T';
const char* s = "hhhhhh";
cout << str << endl;

str += ch;
cout << str << endl;

str += s;
cout << str << endl;

在这里插入图片描述

==字符或字符串的插入和删除==

这里的逻辑稍微有些复杂,而且还有几个比较容易掉的坑。

// 字符的插入
void string::insert(size_t pos, char ch)
{
   
   
    assert(pos <= _size);
    if (_size >= _capacity) {
   
   
        reserve(_capacity == 0 ? 4 : _capacity * 2);
    }
    // 注意这里为什么要+1,size_t是无符号整型,没有负值
    //当有符号和无符号比较时,统一会被转成无符号
    size_t end = _size + 1;
    while (end > pos) {
   
   
        _str[end] = _str[end - 1];
        --end;
    }
    _str[pos] = ch;
    ++_size;
}

// 字符串的插入
void string::insert(size_t pos, const char* str)
{
   
   
    assert(pos <= _size);
    size_t len = strlen(str);
    if (len == 0)return;
    if (_size + len > _capacity) {
   
   
        reserve(_size + len);
    }
    // 注意这里为什么要+len,size_t是无符号整型,没有负值
    //当有符号和无符号比较时,统一会被转成无符号
    size_t end = _size + len;
    while (end > pos + len - 1) {
   
   
        _str[end] = _str[end - len];
        --end;
    }
    memcpy(_str + pos, str, len);
    _size += len;
}

// 字符串的删除
void string::erase(size_t pos, size_t len) 
{
   
   
    assert(pos < _size);
    // 当pos+len过大,超过_size,则取到末尾
    if (len >= _size - pos) {
   
   
        _str[pos] = '\0';
        _size = pos;
    }
    else {
   
   
        strcpy(_str + pos, _str + pos + len);
        _size -= len;
    }
}

使用案例:

string str("hello world!");
char ch = 'T';
const char* s = "hhhhhh";
cout << str << endl;

str.insert(3, ch);
cout << str << endl;

str.insert(3, s);
cout << str << endl;

str.erase(3, strlen(s));
cout << str << endl;

在这里插入图片描述

==find查找==

查找C语言查找字符串的库也有提供,可以直接使用。

size_t string::find(char ch, size_t pos)
{
   
   
    for (size_t i = pos; i < _size; i++) {
   
   
        if (_str[i] == ch)
            return i;
    }
    return npos;
}

size_t string::find(const char* sub, size_t pos)
{
   
   
    const char* ptr = strstr(_str, sub);
    return ptr - _str;
}

使用案例:

string str("hello world!");
cout << str.find('w') << endl;
cout << str.find("wor") << endl;

在这里插入图片描述

缺省参数在函数声明那里。

==比大小运算符重载==

C语言中,有一个按能按字典序将字符串比大小的函数——strcmp

bool string::operator>(const string& str) const
{
   
   
    return strcmp(_str, str._str) > 0;
}
bool string::operator==(const string& str) const
{
   
   
    return strcmp(_str, str._str) == 0;
}
bool string::operator>=(const string& str) const
{
   
   
    return *this > str || *this == str;
}
bool string::operator<(const string& str) const
{
   
   
    return !(*this >= str);
}
bool string::operator<=(const string& str) const
{
   
   
    return !(*this > str);
}
bool string::operator!=(const string& str) const
{
   
   
    return !(*this == str);
}

只需要实现前两个,后面的复用就行。

==获取子串==

使用substr可以获取所需对象字串。

string string::substr(size_t pos, size_t len)
{
   
   
    if (pos + len >= _size) {
   
   
        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;
    }
}

==清除clear==

可以将对象内容都删除,但_capacity保持不变。

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

==流插入和流提取==

这两个函数之前在Date类部分实现的时候,定义为了Date类的友元。但是,流插入和流提取其实可以不定义为string类的友元。

// 流插入
std::ostream& operator<<(std::ostream& os, const string& str)
{
   
   
    for (int i = 0; i < str.size(); i++)
        os << str[i];
    return os;
}

// 流提取
std::istream& operator>>(std::istream& is, string& str)
{
   
   
    str.clear();
    char ch = is.get();
    while (ch != ' ' && ch != '\n') {
   
   
        str += ch;
        ch = is.get();
    }
    return is;
}

string对象读取的截断是空格换行,但是cin对象默认是读不到这两个字符的,这就需要使用另一种读取方式:cin.get()
使用案例:

string str;
cin >> str;
cout << str << endl;

在这里插入图片描述

==常量成员npos==

常量成员的定义需要实现在类的外部,像这样:

const size_t string::npos = -1;

常量的内容是整型(浮点型和字符类型等都不行)时,也可以作为缺省参数进行定义。

结语

本篇博客主要介绍了string类常用接口的实现,包括默认成员函数,迭代器,字符和字符串的插入删除等等内容。
后续博主还会继续分享与STL相关的内容,感谢大家的支持。♥

相关文章
|
1月前
|
C++ 容器
|
30天前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
21 1
|
1月前
|
C++ 容器
|
1月前
|
C++ 容器
|
1月前
|
存储 C++ 容器
|
1月前
|
安全 C语言 C++
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
34 4
|
1月前
|
存储 编译器 程序员
【C++篇】手撕 C++ string 类:从零实现到深入剖析的模拟之路
【C++篇】手撕 C++ string 类:从零实现到深入剖析的模拟之路
63 2
|
1月前
|
编译器 C语言 C++
【C++】C++ STL 探索:String的使用与理解(三)
【C++】C++ STL 探索:String的使用与理解
|
1月前
|
存储 编译器 C++
【C++】C++ STL 探索:String的使用与理解(二)
【C++】C++ STL 探索:String的使用与理解
|
1月前
|
编译器 C语言 C++
【C++】C++ STL 探索:String的使用与理解(一)
【C++】C++ STL 探索:String的使用与理解