模拟实现 C++ 标准库中的 std::string
类是一个很好的练习,可以帮助深入理解 C++ 的内存管理和面向对象编程。
1 浅拷贝和深拷贝解析
浅拷贝和深拷贝是对象复制中的两种不同机制,理解它们的区别对于编写高效、无误的代码至关重要。
浅拷贝 (Shallow Copy)
浅拷贝是指创建一个新对象,这个新对象有着原始对象属性值的一份精确拷贝。如果属性是基本数据类型,拷贝的就是基本数据类型的值;如果属性是引用类型,拷贝的就是内存地址。因此,浅拷贝后的新对象和原对象共享同一块内存,修改一个对象可能会影响另一个对象。
示例:
#include <iostream>
#include <cstring>
class ShallowCopy {
public:
ShallowCopy(const char* data) {
_data = new char[strlen(data) + 1];
strcpy(_data, data);
}
ShallowCopy(const ShallowCopy& other) {
_data = other._data; // 仅复制指针
}
~ShallowCopy() {
delete[] _data;
}
void print() const {
std::cout << _data << std::endl;
}
private:
char* _data;
};
int main() {
ShallowCopy obj1("Hello");
ShallowCopy obj2 = obj1; // 浅拷贝
obj1.print(); // 输出: Hello
obj2.print(); // 输出: Hello
return 0;
}
深拷贝 (Deep Copy)
深拷贝是指创建一个新对象,并递归地复制所有对象的属性。新的对象与原始对象没有任何关联,修改一个对象不会影响另一个对象。
示例:
#include <iostream>
#include <cstring>
class DeepCopy {
public:
DeepCopy(const char* data) {
_data = new char[strlen(data) + 1];
strcpy(_data, data);
}
DeepCopy(const DeepCopy& other) {
_data = new char[strlen(other._data) + 1];
strcpy(_data, other._data); // 复制内容
}
~DeepCopy() {
delete[] _data;
}
void print() const {
std::cout << _data << std::endl;
}
private:
char* _data;
};
int main() {
DeepCopy obj1("Hello");
DeepCopy obj2 = obj1; // 深拷贝
obj1.print(); // 输出: Hello
obj2.print(); // 输出: Hello
return 0;
}
区别总结
- 浅拷贝:复制对象的引用,两个对象共享同一块内存,修改一个对象会影响另一个对象。
- 深拷贝:复制对象的内容,两个对象独立存在,修改一个对象不会影响另一个对象。
2 代码实现
1. 引入必要的头文件
#include <cstring> // 用于strlen, strcpy, strcmp, memmove, memcpy, memset等函数
#include <cassert> // 用于assert断言检查
#include <algorithm> // 用于std::swap交换函数
2. 类定义及类型别名
class string {
public:
typedef char* iterator; // 定义迭代器类型,方便遍历字符串
3. 构造函数
// 默认构造函数,初始化为空字符串或给定的C风格字符串
string(const char* str = "") {
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
4. 复制构造函数与赋值操作符
// 复制构造函数,使用copy-and-swap技巧
string(const string& s) : _str(nullptr), _size(0), _capacity(0) {
string tmp(s._str); // 创建一个临时对象
this->swap(tmp); // 与临时对象交换数据
}
// 复制赋值操作符,同样使用copy-and-swap技巧
string& operator=(string s) {
this->swap(s); // 与传入的对象交换数据
return *this;
}
5. 析构函数
// 析构函数,释放分配的内存
~string() {
if (_str) {
delete[] _str;
_str = nullptr;
}
}
6. 迭代器支持
// 迭代器支持,用于遍历字符串
iterator begin() { return _str; }
iterator end() { return _str + _size; }
7. 修改操作
// 在字符串末尾添加一个字符
void push_back(char c) {
if (_size == _capacity)
reserve(_capacity * 2); // 如果容量不足,增加容量
_str[_size++] = c;
_str[_size] = '\0'; // 空终止符
}
// 重载+=操作符,用于添加单个字符
string& operator+=(char c) {
push_back(c);
return *this;
}
8. 字符串附加操作
// 附加一个C风格字符串
void append(const char* str) {
size_t len = strlen(str);
if (_size + len > _capacity)
reserve(_size + len); // 确保有足够的容量
strcat(_str, str); // 追加字符串
_size += len;
}
// 重载+=操作符,用于附加C风格字符串
string& operator+=(const char* str) {
append(str);
return *this;
}
9. 清空字符串
// 清空字符串
void clear() {
_size = 0;
_str[_size] = '\0'; // 空终止符
}
10. 交换操作
// 交换两个字符串的数据
void swap(string& s) {
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
11. 获取C风格字符串
// 获取C风格字符串
const char* c_str() const {
return _str;
}
12. 容量相关操作
// 获取字符串的大小、容量和是否为空
size_t size() const { return _size; }
size_t capacity() const { return _capacity; }
bool empty() const { return _size == 0; }
// 调整字符串的大小
void resize(size_t newSize, char c = '\0') {
if (newSize > _size) {
if (newSize > _capacity)
reserve(newSize); // 确保有足够的容量
memset(_str + _size, c, newSize - _size);
}
_size = newSize;
_str[newSize] = '\0'; // 空终止符
}
// 预留内存空间
void reserve(size_t newCapacity) {
if (newCapacity > _capacity) {
char* str = new char[newCapacity + 1];
strcpy(str, _str); // 复制现有字符串
delete[] _str; // 释放旧的内存
_str = str;
_capacity = newCapacity;
}
}
13. 访问操作符
// 方括号访问操作符
char& operator[](size_t index) {
assert(index < _size);
return _str[index];
}
const char& operator[](size_t index) const {
assert(index < _size);
return _str[index];
}
14. 字符串比较操作符
// 字符串比较操作符
bool operator<(const string& s) {
return strcmp(_str, s._str) < 0;
}
bool operator<=(const string& s) {
return strcmp(_str, s._str) <= 0;
}
bool operator>(const string& s) {
return strcmp(_str, s._str) > 0;
}
bool operator>=(const string& s) {
return strcmp(_str, s._str) >= 0;
}
bool operator==(const string& s) {
return strcmp(_str, s._str) == 0;
}
bool operator!=(const string& s) {
return strcmp(_str, s._str) != 0;
}
15. 字符串查找操作
// 查找单个字符或子字符串
size_t find(char c, size_t pos = 0) const {
for (size_t i = pos; i < _size; ++i) {
if (_str[i] == c) {
return i;
}
}
return string::npos; // 未找到
}
size_t find(const char* s, size_t pos = 0) const {
size_t len = strlen(s);
for (size_t i = pos; i <= _size - len; ++i) {
if (strncmp(_str + i, s, len) == 0) {
return i;
}
}
return string::npos; // 未找到
}
16. 插入操作
// 在指定位置插入单个字符或子字符串
string& insert(size_t pos, char c) {
if (pos > _size) {
throw std::out_of_range("Position out of range");
}
if (_size + 1 > _capacity) {
reserve(_size + 1); // 确保有足够的容量
}
memmove(_str + pos + 1, _str + pos, _size - pos + 1);
_str[pos] = c;
++_size;
return *this;
}
string& insert(size_t pos, const char* str) {
size_t len = strlen(str);
if (pos > _size) {
throw std::out_of_range("Position out of range");
}
if (_size + len > _capacity) {
reserve(_size + len); // 确保有足够的容量
}
memmove(_str + pos + len, _str + pos, _size - pos + 1);
memcpy(_str + pos, str, len);
_size += len;
return *this;
}
17. 删除操作
// 删除指定位置的字符或子字符串
string& erase(size_t pos, size_t len) {
if (pos > _size || pos + len > _size) {
throw std::out_of_range("Position out of range");
}
memmove(_str + pos, _str + pos + len, _size - pos - len + 1);
_size -= len;
return *this;
}
18. 友元函数:输入输出流操作
private:
friend ostream& operator<<(ostream& _cout, const string& s); // 输出流操作符
friend istream& operator>>(istream& _cin, string& s); // 输入流操作符
char* _str;
size_t _capacity;
size_t _size;
};
19. 输出流操作符
// 输出流操作符,用于输出字符串
ostream& operator<<(ostream& _cout, const string& s) {
for (size_t i = 0; i < s.size(); ++i) {
_cout << s[i];
}
return _cout;
}
以上是按功能块拆分的代码,每一块都有对应的中文注释解释其功能和作用。