一 参考C++ STL的string类构造一个自定义的字符串类
构造一个自定义的字符串类类似于 C++ 标准库中的 std::string
,需要支持字符串的动态分配、拷贝、拼接、访问字符等基本操作。我们可以从构造函数、析构函数、运算符重载、以及常见的字符串操作函数等方面来设计这个类。
1. 基本思路
- 存储方式:字符串本质是一个字符数组,我们可以用一个指针来存储字符串数据,并通过动态分配内存来管理字符数组。
- 长度管理:使用一个变量来存储字符串的长度。
- 内存管理:实现拷贝构造函数、赋值运算符和析构函数以避免内存泄漏和重复分配。
- 功能实现:添加一些常见的功能,比如字符串拼接、访问单个字符、获取字符串长度等。
2. 代码实现
#include <iostream>
#include <cstring> // 用于 std::strlen 和 std::strcpy
class MyString {
private:
char* data; // 存储字符串数据
size_t length; // 存储字符串的长度
public:
// 默认构造函数
MyString() : data(nullptr), length(0) {}
// 带参数的构造函数
MyString(const char* str) {
if (str) {
length = std::strlen(str); // 获取传入字符串的长度
data = new char[length + 1]; // 为字符串分配内存(+1 为了放终止符 '\0')
std::strcpy(data, str); // 复制字符串数据
} else {
data = nullptr;
length = 0;
}
}
// 拷贝构造函数
MyString(const MyString& other) {
length = other.length;
data = new char[length + 1]; // 分配新的内存
std::strcpy(data, other.data); // 复制字符串数据
}
// 移动构造函数 (C++11)
MyString(MyString&& other) noexcept : data(other.data), length(other.length) {
other.data = nullptr; // 转移所有权,防止双重释放
other.length = 0;
}
// 析构函数
~MyString() {
delete[] data; // 释放内存
}
// 赋值运算符重载 (拷贝赋值)
MyString& operator=(const MyString& other) {
if (this == &other) {
return *this; // 防止自我赋值
}
delete[] data; // 释放当前对象的内存
length = other.length;
data = new char[length + 1]; // 分配新内存
std::strcpy(data, other.data); // 复制数据
return *this;
}
// 赋值运算符重载 (移动赋值) (C++11)
MyString& operator=(MyString&& other) noexcept {
if (this == &other) {
return *this;
}
delete[] data; // 释放当前对象的内存
data = other.data; // 转移所有权
length = other.length;
other.data = nullptr;
other.length = 0;
return *this;
}
// 重载加号运算符,用于字符串拼接
MyString operator+(const MyString& other) const {
size_t newLength = length + other.length;
char* newData = new char[newLength + 1];
std::strcpy(newData, data); // 复制第一个字符串
std::strcat(newData, other.data); // 拼接第二个字符串
MyString newString(newData); // 构造新字符串对象
delete[] newData; // 释放临时的内存
return newString;
}
// 获取字符串长度
size_t size() const {
return length;
}
// 获取字符串数据
const char* c_str() const {
return data;
}
// 重载 [] 操作符,访问字符
char& operator[](size_t index) {
if (index >= length) {
throw std::out_of_range("Index out of range");
}
return data[index];
}
const char& operator[](size_t index) const {
if (index >= length) {
throw std::out_of_range("Index out of range");
}
return data[index];
}
// 输出流重载,用于打印字符串
friend std::ostream& operator<<(std::ostream& os, const MyString& str) {
os << str.data;
return os;
}
};
int main() {
MyString str1("Hello");
MyString str2(" World!");
MyString str3 = str1 + str2; // 拼接字符串
std::cout << "Concatenated string: " << str3 << std::endl; // 输出 Hello World!
str1[0] = 'h'; // 修改字符
std::cout << "Modified str1: " << str1 << std::endl; // 输出 hello
return 0;
}
3. 代码解释
(1) 构造函数与析构函数
- 默认构造函数:构造一个空字符串(即
data
指向nullptr
)。 - 带参数的构造函数:根据传入的 C 风格字符串初始化
MyString
对象。 - 拷贝构造函数:深拷贝,确保两个对象拥有独立的字符串内存。
- 移动构造函数:将另一个对象的资源“转移”给当前对象,避免不必要的内存分配和释放。
- 析构函数:释放动态分配的内存。
(2) 赋值运算符重载
- 拷贝赋值:确保在赋值时进行深拷贝,并处理自我赋值的情况。
- 移动赋值:通过移动语义实现高效的资源转移,避免重复分配内存。
(3) +
运算符重载
实现字符串拼接,将两个字符串连接成一个新字符串,并返回新的 MyString
对象。
(4) []
运算符重载
允许通过索引访问或修改字符串中的单个字符,同时处理索引越界的情况。
(5) size()
和 c_str()
方法
size()
:返回字符串的长度。c_str()
:返回 C 风格字符串(即char*
),方便与 C 库函数交互。
(6) 输出流运算符重载
重载 <<
运算符,使得 MyString
对象可以通过标准输出流 std::cout
输出。
4. 总结
这个自定义的 MyString
类提供了基本的字符串操作功能,包括动态内存管理、字符串拼接、字符访问、以及支持移动语义。它与 C++ STL 中的 std::string
类有类似的功能,但实际应用中 std::string
已经过高度优化ÿ