模拟实现 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(); // 输出: Helloobj2.print(); // 输出: Helloreturn 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(); // 输出: Helloobj2.print(); // 输出: Helloreturn 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;
}
以上是按功能块拆分的代码,每一块都有对应的中文注释解释其功能和作用。