#include <iostream>
#include <cstring>
class MyString {
private:
char* data;
size_t length;
public:
// 构造函数
MyString(const char* str = nullptr) {
if (str == nullptr) {
length = 0;
data = new char[1];
data[0] = '\0';
} else {
length = std::strlen(str);
data = new char[length + 1];
std::strcpy(data, str);
}
}
// 析构函数
~MyString() {
delete[] data;
}
// 拷贝构造函数
MyString(const MyString& other) {
length = other.length;
data = new char[length + 1];
std::strcpy(data, other.data);
}
// 赋值运算符重载
MyString& operator=(const MyString& other) {
if (this != &other) {
delete[] data;
length = other.length;
data = new char[length + 1];
std::strcpy(data, other.data);
}
return *this;
}
// 拼接函数
MyString& append(const MyString& other) {
size_t newLength = length + other.length;
char* newData = new char[newLength + 1];
std::strcpy(newData, data);
std::strcat(newData, other.data);
delete[] data;
data = newData;
length = newLength;
return *this;
}
// 比较函数
int compare(const MyString& other) const {
return std::strcmp(data, other.data);
}
// 获取字符串长度
size_t size() const {
return length;
}
// 获取C风格字符串
const char* c_str() const {
return data;
}
};
内存管理
- 构造函数:根据传入的字符串分配足够的内存空间,并复制字符串内容。如果传入
nullptr
,分配一个长度为1的空间存储空字符。
- 析构函数:释放构造函数中分配的内存,避免内存泄漏。
- 拷贝构造函数和赋值运算符重载:先释放自身已有的内存,再根据传入对象的内容分配新的内存并复制。
异常安全问题
- 资源分配即初始化(RAII):使用
new
分配内存时,如果内存分配失败会抛出 std::bad_alloc
异常。由于对象在构造函数中分配内存后立即初始化成员变量,在析构函数中释放内存,所以即使在构造函数或其他成员函数中抛出异常,也能保证已分配的内存被正确释放,避免内存泄漏。
- 异常安全的赋值运算符重载:在赋值运算符重载中,先检查是否是自赋值(
this != &other
),然后释放旧的内存,再分配新的内存并复制数据。如果在分配新内存时抛出异常,原对象的状态不会改变,仍然保持有效,符合异常安全的基本保证。如果需要提供强异常安全保证,可以先创建一个临时对象,进行所有可能抛出异常的操作,然后再交换临时对象和当前对象的内容。