您现在的位置是:首页 > 编程 > 

深入理解字符串:从String类手动实现、代码详解到性能优化(万字长文&基础进阶&面试加分)

2025-07-26 09:22:16
深入理解字符串:从String类手动实现、代码详解到性能优化(万字长文&基础进阶&面试加分) 首先对于面试来说,这应该是我们耳熟能详能够手写的基础八股,其次偶尔重复造轮子是为了磨练自己的技术水平!在编程领域,字符串(String)是我们最常见的数据类型之一。尽管大多数编程语言都提供了内置的字符串类型,但是深入理解并手动实现一个简单的字符串类,可以帮助我们更深入地理解字符串的工作原理

深入理解字符串:从String类手动实现、代码详解到性能优化(万字长文&基础进阶&面试加分)

首先对于面试来说,这应该是我们耳熟能详能够手写的基础八股,其次偶尔重复造轮子是为了磨练自己的技术水平!

在编程领域,字符串(String)是我们最常见的数据类型之一。尽管大多数编程语言都提供了内置的字符串类型,但是深入理解并手动实现一个简单的字符串类,可以帮助我们更深入地理解字符串的工作原理,以及内存管理、拷贝和移动语义等重要概念。

1. 手动实现基本的 String 类

首先,我们来看一个简单的 C++ 字符串类的实现:

代码语言:cpp代码运行次数:0运行复制
#include <iostream>
#include <cstring>

class MyString {
private:
    char* data; // 字符串数据
    size_t length; // 字符串长度

public:
    // 构造函数
    MyString(ct char* str = "") {
        length = strlen(str);
        data = new char[length + 1]; // +1 为了存储 '\0'
        strcpy(data, str);
    }

    // 拷贝构造函数
    MyString(ct MyString& other) {
        length = other.length;
        data = new char[length + 1];
        strcpy(data, other.data);
    }

    // 移动构造函数
    MyString(MyString&& other) noexcept {
        length = other.length;
        data = other.data;
        other.data = nullptr; // 避免析构时重复释放
        other.length = 0;
    }

    // 析构函数
    ~MyString() {
        delete[] data; // 释放动态分配的内存
    }

    // 重载赋值运算符
    MyString& operator=(ct MyString& other) {
        if (this != &other) {
            delete[] data; // 释放旧内存
            length = other.length;
            data = new char[length + 1];
            strcpy(data, other.data);
        }
        return *this;
    }

    // 重载移动赋值运算符
    MyString& operator=(MyString&& other) noexcept {
        if (this != &other) {
            delete[] data; // 释放旧内存
            data = other.data;
            length = other.length;
            other.data = nullptr; // 避免析构时重复释放
            other.length = 0;
        }
        return *this;
    }

    // 获取字符串长度
    size_t size() ct {
        return length;
    }

    // 获取字符串内容
    ct char* c_str() ct {
        return data;
    }
};

int main() {
    MyString str1("Hello, World!");
    MyString str2 = str1; // 拷贝构造
    MyString str = std::move(str1); // 移动构造

    std::cout << "str2: " << _str() << ", length: " << str2.size() << std::endl;
    std::cout << "str: " << _str() << ", length: " << str.size() << std::endl;

    return 0;
}

2. 深入理解每个函数的作用

2.1 构造函数

在我们的 MyString 类中,构造函数的作用是初始化一个新的 MyString 对象。我们提供了两种构造函数:默认构造函数和参数化构造函数。

  • 默认构造函数:这个构造函数允许我们创建一个空的 MyString 对象。这是通过默认参数 "" 实现的,这样当我们不提供任何参数时,就会创建一个空字符串。
  • 参数化构造函数:这个构造函数接受一个 C 风格字符串作为参数,并为其分配内存。我们首先通过 strlen 函数获取字符串的长度,然后使用 new 运算符分配足够的内存来存储字符串和结束字符 \0。最后,我们使用 strcpy 函数将输入字符串复制到新分配的内存中。
2.2 拷贝构造函数

拷贝构造函数的作用是创建一个新的 MyString 对象,该对象是现有对象的副本。在我们的实现中,拷贝构造函数接受一个 MyString 对象的引用作为参数,然后创建一个新的 MyString 对象,该对象具有与输入对象相同的长度和内容。这是通过分配新的内存并复制输入对象的数据来实现的。

2. 移动构造函数

移动构造函数的作用是创建一个新的 MyString 对象,该对象接管现有对象的资源。在我们的实现中,移动构造函数接受一个 MyString 对象的右值引用作为参数,然后创建一个新的 MyString 对象,该对象接管输入对象的数据和长度。这是通过直接将输入对象的数据和长度赋值给新对象,然后将输入对象的数据指针设置为 nullptr 和长度设置为 0 来实现的。

2.4 析构函数

析构函数的作用是清理 MyString 对象。在我们的实现中,析构函数释放了 MyString 对象的数据所占用的内存。这是通过使用 delete[] 运算符来释放 data 指针指向的内存来实现的。

2.5 赋值运算符重载

赋值运算符的作用是将一个 MyString 对象的内容赋值给另一个 MyString 对象。在我们的实现中,赋值运算符首先检查自我赋值的情况,然后释放接收对象的旧数据,分配新的内存,并复制输入对象的数据。

2.6 移动赋值运算符重载

移动赋值运算符的作用是将一个 MyString 对象的资源转移给另一个 MyString 对象。在我们的实现中,移动赋值运算符首先检查自我赋值的情况,然后释放接收对象的旧数据,接管输入对象的数据和长度,并将输入对象的数据指针设置为 nullptr 和长度设置为 0

. 实现自定义字符串类时的关键点

在实现自定义字符串类时,有几个关键的注意事项:

.1 内存管理
  • 动态内存分配:在我们的 MyString 类中,我们使用 new 运算符动态分配内存来存储字符串数据。因此,我们必须确保在析构函数中使用 delete[] 运算符释放这些内存,以防止内存泄漏。
  • 深拷贝与浅拷贝:在拷贝构造函数和赋值运算符中,我们需要确保实现深拷贝,即创建数据的新副本,而不是简单地复制数据指针。这样可以避免多个 MyString 对象指向同一内存区域,从而防止数据损坏和内存泄漏。
.2 移动语义
  • 在 C++11 及以后版本中,我们可以使用移动构造函数和移动赋值运算符来提高性能。这是通过转移资源,而不是复制资源来实现的,从而避免了不必要的内存分配和数据复制。
. 字符串结束符
  • 在我们的 MyString 类中,我们需要确保每个字符串以 \0 结尾,以便与 C 风格字符串兼容。这是通过在分配内存时多分配一个字符,并在复制字符串时包括结束字符来实现的。
.4 异常安全
  • 在内存分配和数据复制过程中,我们需要考虑异常安全性。这是通过在分配新内存或复制数据之前释放旧内存来实现的,从而确保在发生异常时不会导致内存泄漏。

4. 理解深拷贝和数据独立性

假设我们创建了一个 MyString 对象 str1,并将其拷贝到 str2。在这个过程中,str2 将获得 str1 的数据副本,而不是指向同一内存区域。这意味着对 str1 的修改不会影响 str2,从而确保了数据的独立性。

代码语言:cpp代码运行次数:0运行复制
MyString str1("Hello");
MyString str2 = str1; // str2 拷贝了 str1 的内容

5. 添加更多功能的MyString

下面是一个扩展了更多功能的 MyString 类的实现,包括字符串连接、子字符串查和字符替换等功能:

代码语言:cpp代码运行次数:0运行复制
#include <iostream>
#include <cstring>

class MyString {
private:
    char* data; // 字符串数据
    size_t length; // 字符串长度

public:
    // 构造函数
    MyString(ct char* str = "") {
        length = strlen(str);
        data = new char[length + 1]; // +1 为了存储 '\0'
        strcpy(data, str);
    }

    // 拷贝构造函数
    MyString(ct MyString& other) {
        length = other.length;
        data = new char[length + 1];
        strcpy(data, other.data);
    }

    // 移动构造函数
    MyString(MyString&& other) noexcept {
        length = other.length;
        data = other.data;
        other.data = nullptr; // 避免析构时重复释放
        other.length = 0;
    }

    // 析构函数
    ~MyString() {
        delete[] data; // 释放动态分配的内存
    }

    // 重载赋值运算符
    MyString& operator=(ct MyString& other) {
        if (this != &other) {
            delete[] data; // 释放旧内存
            length = other.length;
            data = new char[length + 1];
            strcpy(data, other.data);
        }
        return *this;
    }

    // 重载移动赋值运算符
    MyString& operator=(MyString&& other) noexcept {
        if (this != &other) {
            delete[] data; // 释放旧内存
            data = other.data;
            length = other.length;
            other.data = nullptr; // 避免析构时重复释放
            other.length = 0;
        }
        return *this;
    }

    // 获取字符串长度
    size_t size() ct {
        return length;
    }

    // 获取字符串内容
    ct char* c_str() ct {
        return data;
    }

    // 字符串连接
    MyString operator+(ct MyString& other) {
        size_t new_length = length + other.length;
        char* new_data = new char[new_length + 1];
        strcpy(new_data, data);
        strcat(new_data, other.data);
        return MyString(new_data);
    }

    // 子字符串查
    size_t find(ct MyString& sub) ct {
        char* pos = strstr(data, sub.data);
        if (pos) {
            return pos - data;
        } else {
            return npos;
        }
    }

    // 字符替换
    void replace(char old_char, char new_char) {
        for (size_t i = 0; i < length; ++i) {
            if (data[i] == old_char) {
                data[i] = new_char;
            }
        }
    }

    static ct size_t npos = -1;
};

int main() {
    MyString str1("Hello, World!");
    MyString str2 = str1; // 拷贝构造
    MyString str = std::move(str1); // 移动构造

    std::cout << "str2: " << _str() << ", length: " << str2.size() << std::endl;
    std::cout << "str: " << _str() << ", length: " << str.size() << std::endl;

    MyString str4 = str2 + str; // 字符串连接
    std::cout << "str4: " << _str() << ", length: " << str4.size() << std::endl;

    size_t pos = str4.find("World"); // 子字符串查
    std::cout << "pos: " << pos << std::endl;

    str4.replace('o', '0'); // 字符替换
    std::cout << "str4: " << _str() << ", length: " << str4.size() << std::endl;

    return 0;
}

这个版本的 MyString 类增加了以下功能:

  • 字符串连接:通过重载 + 运算符实现。新的字符串长度是两个输入字符串的长度之和,内容是两个输入字符串的内容连接。
  • 子字符串查:通过 find 函数实现。如果到子字符串,返回其在字符串中的位置;否则,返回 npos
  • 字符替换:通过 replace 函数实现。将字符串中的所有 old_char 替换为 new_char

6. 进一步性能优化

在实际使用中,性能是非常重要的考虑因素。可以讨论如何优化 MyString 类的性能,例如通过使用小字符串优化(Small String Optimization)来避免小字符串的内存分配。

小字符串优化是一种常见的优化手段,它可以避免小字符串的内存分配。具体来说,如果字符串的长度小于某个阈值(例如 15),则可以直接在对象内部存储字符串,而不是在堆上分配内存。这样可以避免小字符串的内存分配和释放开销,提高性能。

代码语言:cpp代码运行次数:0运行复制
#include <iostream>
#include <cstring>

class MyString {
private:
    union {
        char local_buffer[16]; // 本地缓冲区,用于存储小字符串
        char* heap_buffer; // 堆缓冲区,用于存储大字符串
    };
    size_t length; // 字符串长度

public:
    // 构造函数
    MyString(ct char* str = "") {
        length = strlen(str);
        if (length < 16) {
            strcpy(local_buffer, str);
            local_buffer[length] = '\0';
        } else {
            heap_buffer = new char[length + 1];
            strcpy(heap_buffer, str);
        }
    }

    // 拷贝构造函数
    MyString(ct MyString& other) {
        length = other.length;
        if (length < 16) {
            strcpy(local_buffer, other.local_buffer);
        } else {
            heap_buffer = new char[length + 1];
            strcpy(heap_buffer, other.heap_buffer);
        }
    }

    // 移动构造函数
    MyString(MyString&& other) noexcept {
        length = other.length;
        if (length < 16) {
            strcpy(local_buffer, other.local_buffer);
        } else {
            heap_buffer = other.heap_buffer;
            other.heap_buffer = nullptr;
        }
    }

    // 析构函数
    ~MyString() {
        if (length >= 16) {
            delete[] heap_buffer;
        }
    }

    // 重载赋值运算符
    MyString& operator=(ct MyString& other) {
        if (this != &other) {
            if (length >= 16) {
                delete[] heap_buffer;
            }
            length = other.length;
            if (length < 16) {
                strcpy(local_buffer, other.local_buffer);
            } else {
                heap_buffer = new char[length + 1];
                strcpy(heap_buffer, other.heap_buffer);
            }
        }
        return *this;
    }

    // 重载移动赋值运算符
    MyString& operator=(MyString&& other) noexcept {
        if (this != &other) {
            if (length >= 16) {
                delete[] heap_buffer;
            }
            length = other.length;
            if (length < 16) {
                strcpy(local_buffer, other.local_buffer);
            } else {
                heap_buffer = other.heap_buffer;
                other.heap_buffer = nullptr;
            }
        }
        return *this;
    }

    // 获取字符串长度
    size_t size() ct {
        return length;
    }

    // 获取字符串内容
    ct char* c_str() ct {
        if (length < 16) {
            return local_buffer;
        } else {
            return heap_buffer;
        }
    }

    // 字符串连接
    MyString operator+(ct MyString& other) {
        size_t new_length = length + other.length;
        char* new_data = new char[new_length + 1];
        strcpy(new_data, c_str());
        strcat(new_data, _str());
        return MyString(new_data);
    }

    // 子字符串查
    size_t find(ct MyString& sub) ct {
        char* pos = strstr(c_str(), _str());
        if (pos) {
            return pos - c_str();
        } else {
            return npos;
        }
    }

    // 字符替换
    void replace(char old_char, char new_char) {
        for (size_t i = 0; i < length; ++i) {
            if (c_str()[i] == old_char) {
                if (length < 16) {
                    local_buffer[i] = new_char;
                } else {
                    heap_buffer[i] = new_char;
                }
            }
        }
    }

    static ct size_t npos = -1;
};

int main() {
    MyString str1("Hello, World!");
    MyString str2 = str1; // 拷贝构造
    MyString str = std::move(str1); // 移动构造

    std::cout << "str2: " << _str() << ", length: " << str2.size() << std::endl;
    std::cout << "str: " << _str() << ", length: " << str.size() << std::endl;

    MyString str4 = str2 + str; // 字符串连接
    std::cout << "str4: " << _str() << ", length: " << str4.size() << std::endl;

    size_t pos = str4.find("World"); // 子字符串查
    std::cout << "pos: " << pos << std::endl;

    str4.replace('o', '0'); // 字符替换
    std::cout << "str4: " << _str() << ", length: " << str4.size() << std::endl;

    return 0;
}

7. 总结:理解字符串的底层实现

这篇文章已经非常详细地解释了如何手动实现一个字符串类,包括构造函数、拷贝构造函数、移动构造函数、析构函数以及赋值运算符的重载等关键部分。同时,也解释了内存管理、深拷贝与浅拷贝、移动语义、字符串结束符、异常安全、更多操作以及代码性能优化等关键概念。

如果要进一步完善,还可以考虑以下两个方面:

  1. 错误处理:当前的 MyString 类没有处理可能的错误,例如内存分配失败。可以添加错误处理代码,以增强其健壮性。
  2. 测试和验证:为了确保 MyString 类的正确性,可以编写一些测试用例来验证其功能。这也可以帮助读者更好地理解如何使用这个类。

手动实现一个字符串类不仅能帮助我们理解字符串的底层实现,还能让我们掌握内存管理、拷贝和移动语义等重要概念。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2024-12-25,如有侵权请联系 cloudcommunity@tencent 删除string对象内存字符串腾讯技术创作特训营S11#重启人生

#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格

本文地址:http://www.dnpztj.cn/biancheng/1218820.html

相关标签:无
上传时间: 2025-07-25 11:26:00
留言与评论(共有 11 条评论)
本站网友 嘀咕的意思
14分钟前 发表
还能让我们掌握内存管理
本站网友 万科光明城市
23分钟前 发表
other.local_buffer); } else { heap_buffer = new char[length + 1]; strcpy(heap_buffer
本站网友 质量受权人
3分钟前 发表
2.4 析构函数析构函数的作用是清理 MyString 对象
本站网友 国足出线
23分钟前 发表
2.2 拷贝构造函数拷贝构造函数的作用是创建一个新的 MyString 对象
本站网友 ipo上市
26分钟前 发表
测试和验证:为了确保 MyString 类的正确性
本站网友 白云二手房
1分钟前 发表
endl; MyString str4 = str2 + str; // 字符串连接 std
本站网友 成都精神病医院
17分钟前 发表
本站网友 山海关吧
22分钟前 发表
" << _str() << "
本站网友 浙江临安
16分钟前 发表
" << _str() << "
本站网友 北京歌华开元大酒店
27分钟前 发表
以及内存管理