C++复习之 string的深入理解和仿写

深入理解 C++ 中的 std::string 类型与常用方法

在 C++ 中,字符串处理是编程中最常见的任务之一。虽然 C++ 标准库为我们提供了强大的 std::string 类来处理字符串,但仍然有很多开发者对 std::string 的使用方法不甚了解,尤其是在内存管理、性能优化和特性使用方面。本文将深入解析 std::string 的内部机制,并介绍一些常见的操作方法。


什么是 std::string

std::string 是 C++ 标准库中的一个类,定义在 <string> 头文件中,用来表示动态大小的字符串。它封装了字符数组的动态内存管理,并提供了多种方法来方便地进行字符串的操作。std::string 主要由以下几部分组成:

  • 内存管理std::string 使用动态内存来存储字符串内容,能够自动扩展容量来容纳更多字符。
  • 自动内存释放std::string 会在析构时自动释放内存,避免了手动管理字符数组带来的复杂性。
  • 丰富的成员函数std::string 提供了很多操作字符串的功能,如查找、替换、连接、分割等。

std::string 的构造与初始化

1. 默认构造函数

std::string str;

默认构造一个空字符串,等效于 str = ""

2. 初始化为字符串常量

std::string str = "Hello, World!";

将一个字符串字面量赋值给 std::string 对象。

3. 初始化为指定长度的字符

std::string str(10, 'a');

该构造函数会创建一个包含 10 个字符 'a' 的字符串。

4. char* 构造

const char* cstr = "Hello";
std::string str(cstr);

将一个 C 风格的字符串(const char*)转换为 std::string

5. 拷贝构造函数

std::string str1 = "Hello";
std::string str2 = str1;

通过拷贝构造函数将一个已有的 std::string 对象赋值给另一个。

6. 移动构造函数

std::string str1 = "Hello";
std::string str2 = std::move(str1);  // str1 的资源被转移到 str2

通过移动构造函数,str2 接管了 str1 的资源,避免了不必要的内存复制。


std::string 的常用成员函数

1. 获取字符串的长度

std::string str = "Hello";
size_t len = str.size();  // 或 str.length()

返回字符串的字符数,不包括结束的 \0size()length() 功能等效,都是用于获取字符串长度。

2. 获取 C 风格字符串

std::string str = "Hello";
const char* cstr = str.c_str();

std::string 转换为 C 风格的字符串,返回一个指向字符数组的指针。

3. 检查字符串是否为空

std::string str;
bool isEmpty = str.empty();

empty() 函数用于检查字符串是否为空,如果字符串长度为 0,则返回 true

4. 字符串拼接

std::string str1 = "Hello";
std::string str2 = "World";
std::string result = str1 + ", " + str2;  // 使用 + 运算符连接字符串

使用 + 运算符可以将两个字符串连接起来。

5. 追加字符串

std::string str = "Hello";
str.append(", World!");

append() 用于在字符串的末尾追加另一个字符串。

6. 查找子串

std::string str = "Hello, World!";
size_t pos = str.find("World");
if (pos != std::string::npos) {
    std::cout << "'World' found at position: " << pos << std::endl;
}

使用 find() 函数查找子串的首次出现位置,返回子串的起始位置,如果没有找到则返回 std::string::npos

7. 获取子串

std::string str = "Hello, World!";
std::string sub = str.substr(7, 5);  // 从位置 7 开始,长度为 5 的子串

substr() 用于提取字符串的一个子串。

8. 字符串替换

std::string str = "Hello, World!";
str.replace(7, 5, "C++");  // 将从位置 7 开始的 5 个字符替换为 "C++"

replace() 用于替换指定位置的字符。

9. 删除字符

std::string str = "Hello, World!";
str.erase(5, 7);  // 删除从位置 5 开始的 7 个字符

erase() 用于删除字符串中的一部分。

10. 插入字符或字符串

std::string str = "Hello!";
str.insert(5, ", World");

insert() 用于在指定位置插入字符或字符串。

11. 去除空白字符

std::string str = "   Hello, World!   ";
str.erase(0, str.find_first_not_of(" "));  // 去除开头的空格
str.erase(str.find_last_not_of(" ") + 1);  // 去除末尾的空格

使用 erase()find_first_not_of()find_last_not_of() 可以去除字符串两端的空白字符。

12. 字符串比较

std::string str1 = "Hello";
std::string str2 = "World";
if (str1 == str2) {
    std::cout << "Strings are equal." << std::endl;
} else {
    std::cout << "Strings are not equal." << std::endl;
}

使用 ==!= 运算符可以比较两个字符串是否相等或不等。

13. 字符串转换为数字

std::string str = "123";
int num = std::stoi(str);  // 转换为整数
double dnum = std::stod(str);  // 转换为双精度浮点数

std::stoi()std::stod() 分别用于将字符串转换为整数和浮点数。


std::string 的内存管理与性能优化

std::string 是一个动态大小的容器,它会自动管理内存。在 C++ 中,std::string 会根据需要动态调整其内部存储的容量,以容纳更多的字符。这是通过小缓冲区优化指针缓存机制来实现的。因此,字符串操作时,尤其是进行大量的连接和修改时,可能会导致频繁的内存分配和复制。

1. 预分配内存

如果你知道字符串的最终长度,可以通过 reserve() 函数提前分配足够的内存,避免多次扩容:

std::string str;
str.reserve(1000);  // 提前为字符串分配 1000 个字符的内存

2. 减少不必要的拷贝

在进行字符串的传递或返回时,避免不必要的拷贝可以提高性能。使用移动语义(C++11 引入)可以避免数据的拷贝。std::move() 可以将一个字符串的资源转移到另一个字符串:

std::string str1 = "Hello";
std::string str2 = std::move(str1);  // 转移资源

3. 避免多次 + 连接

多个字符串连接操作可能会导致性能问题,因为每次连接都会进行内存重新分配。使用 std::ostringstream 可以高效地连接多个字符串:

#include <sstream>

std::ostringstream oss;
oss << "Hello, " << "World!";
std::string result = oss.str();

总结

std::string 是 C++ 中处理字符串的核心类,它封装了内存管理,并提供了丰富的成员函数来方便地操作字符串。通过合理使用 std::string,可以大大简化代码并提高开发效率。在实际开发中,除了了解常用的方法外,性能优化和内存管理也是非常重要的,尤其是在进行大量字符串操作时。

下面是我对string的仿写

String.h

#pragma once
#include <iostream>
#include <cstring>
#include <stdexcept>  // 为了异常处理

class String {
public:
    // 构造函数、拷贝构造函数和析构函数
    String(const char* str = nullptr);              // 默认构造函数
    String(const String& other);                    // 拷贝构造函数
    String(String&& other) noexcept;                // 移动构造函数
    ~String();                                      // 析构函数

    // 赋值运算符和移动赋值运算符
    String& operator=(const String& other);         // 赋值运算符
    String& operator=(String&& rhs) noexcept;       // 移动赋值运算符

    // 重载 + 运算符(字符串连接)
    String operator+(const String& other) const;

    // 常用成员函数
    size_t size() const;                            // 获取字符串长度
    const char* c_str() const;                      // 获取 C 风格字符串

    // 字符串比较
    bool operator==(const String& other) const;     // 比较相等
    bool operator!=(const String& other) const;     // 比较不等

    // 重载输出流操作符
    friend std::ostream& operator<<(std::ostream& os, const String& c);

private:
    char* m_data;                                   // 存储字符串

    // 私有方法:用于复制字符串数据
    void copyData(const char* str);
};

String.cpp
#include "String.h"
#include <iostream>
#include <cstring>
#include <stdexcept>  // 引入异常处理库

// 默认构造函数
String::String(const char* str) {
    if (str) {
        size_t len = strlen(str);
        m_data = new char[len + 1];
        strcpy(m_data, str);
    }
    else {
        m_data = new char[1]{ '\0' };  // 使用列表初始化
    }
}

// 拷贝构造函数
String::String(const String& other) {
    copyData(other.m_data);
}

// 移动构造函数
String::String(String&& other) noexcept : m_data(other.m_data) {
    other.m_data = nullptr;
}

// 析构函数
String::~String() {
    delete[] m_data;
}

// 赋值运算符
String& String::operator=(const String& other) {
    if (this != &other) {
        delete[] m_data;  // 先删除原有内存
        copyData(other.m_data);  // 然后复制新数据
    }
    return *this;
}

// 移动赋值运算符
String& String::operator=(String&& rhs) noexcept {
    if (this != &rhs) {
        delete[] m_data;   // 先释放现有内存
        m_data = rhs.m_data; // 直接接管 rhs 的资源
        rhs.m_data = nullptr; // 防止 rhs 的析构时释放资源
    }
    return *this;
}

// 字符串连接运算符
String String::operator+(const String& other) const {
    size_t len1 = strlen(m_data);
    size_t len2 = strlen(other.m_data);

    char* result = new char[len1 + len2 + 1]; // +1 需要为 '\0' 结束符分配空间

    strcpy(result, m_data);  // 复制当前字符串
    strcat(result, other.m_data);  // 连接另一个字符串

    String newString(result); // 使用 String 的构造函数来返回
    delete[] result;  // 删除临时分配的内存
    return newString;
}

// 获取字符串长度
size_t String::size() const {
    return strlen(m_data);
}

// 获取 C 风格字符串
const char* String::c_str() const {
    return m_data;
}

// 字符串相等比较
bool String::operator==(const String& other) const {
    return strcmp(m_data, other.m_data) == 0;
}

// 字符串不等比较
bool String::operator!=(const String& other) const {
    return !(*this == other);
}

// 重载输出流操作符
std::ostream& operator<<(std::ostream& os, const String& c) {
    os << (c.m_data ? c.m_data : "");
    return os;
}

// 私有方法:用于复制字符串数据
void String::copyData(const char* str) {
    if (!str) {
        m_data = new char[1]{ '\0' };  // 防止传入 nullptr
    }
    else {
        size_t len = strlen(str);
        m_data = new char[len + 1];  // +1 为 '\0' 终止符
        strcpy(m_data, str);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋到亦天凉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值