【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相关的内容,感谢大家的支持。♥

相关文章
|
3天前
|
编译器 程序员 C语言
【C++】string模拟实现
这篇博客探讨了自定义实现C++ `string` 类的关键功能,包括构造、拷贝构造、赋值运算符重载及析构函数。作者强调了理解并实现这些功能对于面试的重要性。博客介绍了`string` 类的头文件`string.h`,其中定义了迭代器、基本成员函数如`swap()`、`size()`、`c_str()`等,并提到了深拷贝概念。此外,还展示了构造函数、析构函数和赋值运算符的实现,以及迭代器的定义与使用。博客还包括对C语言字符串函数的引用,以辅助读者理解实现细节。
|
3天前
|
编译器 C++
【C++】string类的使用④(字符串操作String operations )
这篇博客探讨了C++ STL中`std::string`的几个关键操作,如`c_str()`和`data()`,它们分别返回指向字符串的const char*指针,前者保证以&#39;\0&#39;结尾,后者不保证。`get_allocator()`返回内存分配器,通常不直接使用。`copy()`函数用于将字符串部分复制到字符数组,不添加&#39;\0&#39;。`find()`和`rfind()`用于向前和向后搜索子串或字符。`npos`是string类中的一个常量,表示找不到匹配项时的返回值。博客通过实例展示了这些函数的用法。
|
3天前
|
存储 C++
【C++】string类的使用③(非成员函数重载Non-member function overloads)
这篇文章探讨了C++中`std::string`的`replace`和`swap`函数以及非成员函数重载。`replace`提供了多种方式替换字符串中的部分内容,包括使用字符串、子串、字符、字符数组和填充字符。`swap`函数用于交换两个`string`对象的内容,成员函数版本效率更高。非成员函数重载包括`operator+`实现字符串连接,关系运算符(如`==`, `&lt;`等)用于比较字符串,以及`swap`非成员函数。此外,还介绍了`getline`函数,用于按指定分隔符从输入流中读取字符串。文章强调了非成员函数在特定情况下的作用,并给出了多个示例代码。
|
10天前
|
C++ 容器
C++字符串string容器(构造、赋值、拼接、查找、替换、比较、存取、插入、删除、子串)
C++字符串string容器(构造、赋值、拼接、查找、替换、比较、存取、插入、删除、子串)
18 1
|
3天前
|
C++
【C++】string类的使用④(常量成员Member constants)
C++ `std::string` 的 `find_first_of`, `find_last_of`, `find_first_not_of`, `find_last_not_of` 函数分别用于从不同方向查找目标字符或子串。它们都返回匹配位置,未找到则返回 `npos`。`substr` 用于提取子字符串,`compare` 则提供更灵活的字符串比较。`npos` 是一个表示最大值的常量,用于标记未找到匹配的情况。示例代码展示了这些函数的实际应用,如替换元音、分割路径、查找非字母字符等。
|
3天前
|
C++
C++】string类的使用③(修改器Modifiers)
这篇博客探讨了C++ STL中`string`类的修改器和非成员函数重载。文章介绍了`operator+=`用于在字符串末尾追加内容,并展示了不同重载形式。`append`函数提供了更多追加选项,包括子串、字符数组、单个字符等。`push_back`和`pop_back`分别用于在末尾添加和移除一个字符。`assign`用于替换字符串内容,而`insert`允许在任意位置插入字符串或字符。最后,`erase`函数用于删除字符串中的部分内容。每个函数都配以代码示例和说明。
|
3天前
|
安全 编译器 C++
【C++】string类的使用②(元素获取Element access)
```markdown 探索C++ `string`方法:`clear()`保持容量不变使字符串变空;`empty()`检查长度是否为0;C++11的`shrink_to_fit()`尝试减少容量。`operator[]`和`at()`安全访问元素,越界时`at()`抛异常。`back()`和`front()`分别访问首尾元素。了解这些,轻松操作字符串!💡 ```
|
3天前
|
存储 编译器 Linux
【C++】string类的使用②(容量接口Capacity )
这篇博客探讨了C++ STL中string的容量接口和元素访问方法。`size()`和`length()`函数等价,返回字符串的长度;`capacity()`提供已分配的字节数,可能大于长度;`max_size()`给出理论最大长度;`reserve()`预分配空间,不改变内容;`resize()`改变字符串长度,可指定填充字符。这些接口用于优化内存管理和适应字符串操作需求。
|
3天前
|
C++ 容器
【C++】string类的使用①(迭代器接口begin,end,rbegin和rend)
迭代器接口是获取容器元素指针的成员函数。`begin()`返回首元素的正向迭代器,`end()`返回末元素之后的位置。`rbegin()`和`rend()`提供反向迭代器,分别指向尾元素和首元素之前。C++11增加了const版本以供只读访问。示例代码展示了如何使用这些迭代器遍历字符串。
|
3天前
|
存储 编译器 C语言
【C++】string类的使用①(默认成员函数
本文介绍了C++ STL中的`string`类,它是用于方便地操作和管理字符串的类,替代了C语言中不便的字符数组操作。`string`基于`basic_string`模板,提供类似容器的接口,但针对字符串特性进行了优化。学习资源推荐[cplusplus.com](https://cplusplus.com/)。`string`类提供了多种构造函数,如无参构造、拷贝构造、字符填充构造等,以及析构函数和赋值运算符重载。示例代码展示了不同构造函数和赋值运算符的用法。