string(2),咕咕咕!
1.string类的模拟实现
class string {friend std::ostream& operator<<(std::ostream& _cout, const bit::string& s);friend std::istream& operator>>(std::istream& _cin, bit::string& s);public:typedef char* iterator;public:// 构造函数string(const char* str = "") {_size = std::strlen(str);_capacity = _size;_str = new char[_capacity + 1]; // 额外空间存储'\0'std::strcpy(_str, str);}// 拷贝构造函数string(const string& s) : _str(nullptr), _capacity(0), _size(0) {string temp(s._str);swap(temp);}// 赋值运算符重载string& operator=(const string& s) {if (this != &s) {string temp(s);swap(temp);}return *this;}// 析构函数~string() {delete[] _str;_str = nullptr;_size = 0;_capacity = 0;}// 迭代器iterator begin() {return _str;}iterator end() {return _str + _size;}// modifyvoid push_back(char c) {if (_size == _capacity) {reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size++] = c;_str[_size] = '\0';}string& operator+=(char c) {push_back(c);return *this;}void append(const char* str) {size_t len = std::strlen(str);if (_size + len > _capacity) {reserve(_size + len);}std::strcpy(_str + _size, str);_size += len;}string& operator+=(const char* str) {append(str);return *this;}void clear() {_size = 0;_str[_size] = '\0';}void swap(string& s) {std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}const char* c_str() const {return _str;}// capacitysize_t size() const {return _size;}size_t capacity() const {return _capacity;}bool empty() const {return _size == 0;}void resize(size_t n, char c = '\0') {if (n > _size) {// 需要扩容if (n > _capacity) {reserve(n);}std::fill(_str + _size, _str + n, c);}_size = n;_str[_size] = '\0';}void reserve(size_t n) {if (n > _capacity) {char* new_str = new char[n + 1];std::strcpy(new_str, _str);delete[] _str;_str = new_str;_capacity = n;}}// accesschar& operator[](size_t index) {return _str[index];}const char& operator[](size_t index) const {return _str[index];}// relational operatorsbool operator<(const string& s) {return std::strcmp(_str, s._str) < 0;}bool operator<=(const string& s) {return *this < s || *this == s;}bool operator>(const string& s) {return !(*this <= s);}bool operator>=(const string& s) {return !(*this < s);}bool operator==(const string& s) {return std::strcmp(_str, s._str) == 0;}bool operator!=(const string& s) {return !(*this == s);}// 查找字符第一次出现的位置size_t find(char c, size_t pos = 0) const {if (pos >= _size) {return npos;}for (size_t i = pos; i < _size; ++i) {if (_str[i] == c) {return i;}}return npos;}// 查找子串第一次出现的位置size_t find(const char* s, size_t pos = 0) const {if (pos >= _size) {return npos;}const char* p = std::strstr(_str + pos, s);if (p == nullptr) {return npos;}return p - _str;}// 插入字符string& insert(size_t pos, char c) {if (pos > _size) {return *this;}if (_size == _capacity) {reserve(_capacity == 0 ? 4 : _capacity * 2);}// 移动数据for (size_t i = _size; i > pos; --i) {_str[i] = _str[i - 1];}_str[pos] = c;_size++;_str[_size] = '\0';return *this;}// 插入字符串string& insert(size_t pos, const char* str) {if (pos > _size || str == nullptr) {return *this;}size_t len = std::strlen(str);if (len == 0) {return *this;}// 检查容量if (_size + len > _capacity) {reserve(_size + len);}// 移动数据for (size_t i = _size; i >= pos && i != (size_t)-1; --i) {_str[i + len] = _str[i];}// 复制字符串std::strncpy(_str + pos, str, len);_size += len;_str[_size] = '\0';return *this;}// 删除字符string& erase(size_t pos, size_t len = npos) {if (pos >= _size) {return *this;}size_t left = _size - pos;len = len < left ? len : left;// 移动数据覆盖要删除的部分std::strcpy(_str + pos, _str + pos + len);_size -= len;return *this;}// 静态成员,表示未找到static const size_t npos;private:char* _str;size_t _capacity;size_t _size;};// 初始化静态成员const size_t string::npos = -1;// 流运算符重载std::ostream& operator<<(std::ostream& _cout, const bit::string& s) {_cout << s._str;return _cout;}std::istream& operator>>(std::istream& _cin, bit::string& s) {s.clear();char ch;_cin >> ch;while (ch != ' ' && ch != '\n') {s.push_back(ch);_cin.get(ch);}return _cin;}
错误案例:
String(const char* pStr="") 这是正确深度构造
:_pStr(new char[strlen(pStr)+1])
{
strcpy(_pStr,pStr);
}void Test()
{
String s1("hello world!");
String s2(s1); 默认构造了
}~String()
{
if(_pStr) delete []_pStr;
}s2需要调用String类拷贝函数来创建,该类没有显式定义,就用系统默认拷贝构造函数(浅拷贝)
导致s1与s2共用同一块空间,在释放时同一块空间多次释放,会引起程序崩溃
浅拷贝:位拷贝,编译器只是把对象中的值拷贝过来,如果对象中管理资源,会导致多个对象共享同一份资源,当一个对象销毁时就会把该资源释放掉,而此时另一些对象继续对资源操作时会发生访问违规。
深拷贝:每个对象都有一份独立的资源,不要和其他对象共享。
class string
{
public:// 构造函数string(const char* str = ""){// 计算字符串长度int len = strlen(str);// 分配内存(+1是为了存放结束符'\0')_str = new char[len + 1];// 复制字符串内容strcpy(_str, str);}// 拷贝构造函数string(const string &s){// 分配与原字符串相同大小的内存int len = strlen(s._str);_str = new char[len + 1];// 复制内容strcpy(_str, s._str);}// 赋值运算符重载string& operator=(const string &s){// 检查自我赋值if (this != &s){// 释放原有内存delete[] _str;// 分配新内存并复制内容int len = strlen(s._str);_str = new char[len + 1];strcpy(_str, s._str);}return *this;}// 析构函数~string(){// 使用delete[]释放动态数组delete[] _str;_str = nullptr;}private:char *_str;
};
2.拓展
string在实现中其实用了buffer数组,当字符串长度小于一定值(比如16),就存在固定长度的buffer里,如果长于16的话就1.5倍/2倍变大
写时拷贝:在浅拷贝基础上增加了引用计数
引用计数:记录资源使用者的个数。在构造时,资源的计数为1,每增加一个对象使用该资源,就给计数加1,某个对象销毁时,该计数减1,计数为1,说明该对象是最后一个使用者,将资源释放,否则不释放。