C++ String部分成员模拟实现流程详解
目录
- string类的成员设计
- 普通构造函数的模拟
- 拷贝构造函数的模拟
- 赋值重载函数的模拟
- String的析构函数模拟
- 补全上述的成员函数
- 迭代器的简单模拟
- 其他成员函数的模拟
string类的成员设计
class string { private: char* _str; int _size; int _capacity; };
说明:以下的五个成员函数的模拟实现,均去除了_size
和_capacity
成员变量,目的是为了更方便解释重点。在五个成员函数模拟后,会对string类的设计进行补全。
普通构造函数的模拟
我们是否可以使用默认构造函数来初始化对象?在这种情况下是万万不能的!要记住默认的构造函数对自定义类型会去调用它自己的构造函数进行初始化,而对于内置类型是不做处理的,此时我们的成员变量_str
的类型是内置类型,不会被初始化,所以一定要自己写构造函数。
//这种构造函数是否可行? string(const char* str) { _str = str; }
这种写法做不到用字符串构造一个对象。
原因:这样会使得str
和_str
指向的都是同一块空间。str会影响到_str.
所以正确的做法是,给_str
分配一块属于自己的空间,再把str
的值拷贝给_str.
string(const char* str) { _str = new char[strlen(str) + 1]; //要多给一个'\0'的空间 strcpy(_str, str); }
修一下小细节:
1.实例化对象的时候是支持无参构造的,所以可以给参数一个缺省值""
,里面自己隐藏的有一个\0
.如果没有传参数,则使用缺省值。
string(const char* str = "") { _str = new char[strlen(str) + 1]; //要多给一个'\0'的空间 strcpy(_str, str); }
拷贝构造函数的模拟
看了普通构造函数的模拟实现以后,最不应该犯的错就是把一个string对象的数据直接给了另一个string对象
所以直接甩代码
string(const string& s) { _str = new char[strlen(s._str) + 1]; strcpy(_str, s._str); }
当然,如果有前面所写普通构造函数,还可以利用普通构造函数来拷贝构造一个对象。
//还可以借助普通构造函数 string(const string& s) :_str(nullptr) { string tmp(s._str); swap(_str, tmp._str); }
赋值重载函数的模拟
这里重载赋值,是为了把一个已经存在的string对象的数据给另一个已经存在的string对象。
也就意味着,两个对象均有自己的空间。不要把string对象的_str直接赋值,否则析构的时候会析构两次,并且这两个string对象由于_str使用的是同一块空间,会相互之间影响。
string& operator=(const string& s) { delete[] _str; //把原来的空间释放掉 _str = new char[strlen(s._str) + 1]; //给一块新的空间 strcpy(_str, s._str);; }
上面这种方法是不行的。
1.不排除自己给自己赋值的情况,自己都给释放了,拿什么来赋值?
2.使用delete先释放,只要地址正确无论如何都会释放成功,但是new
一块空间不一定会成功,如果一开始就给释放了,而我去申请空间却申请不到,那就是不仅没有赋值成功,还把我自己原本有的给丢了。
//正确的写法 string& operator=(const string& s) { if (this != &s) { char* tmp = new char[strlen(s._str) + 1]; strcpy(tmp, s._str); delete[] _str; _str = tmp; } return *this; //如果自己给自己赋值,那就返回自己 }
还可以使用传值的方法
string& operator=(string s) { swap(_str, s._str); return *this; }
String的析构函数模拟
~string() { if (_str) { delete[] _str; _str = nullptr; } }
补全上述的成员函数
//因为std库里原本有一个string,所以这里加上一个命名空间,防止命名污染 namespace YDY { class string { public: string(const char* str = "") :_size(strlen(str)) ,_capacity(_size) { _str = new char[_capacity + 1]; //要多给一个'\0'的空间 strcpy(_str, str); } string(const string& s) :_str(nullptr) ,_size(s._size) ,_capacity(s._capacity) { string tmp(s._str); swap(_str, tmp._str); } string& operator=(const string& s) { if (this != &s) { char* tmp = new char[strlen(s._str) + 1]; strcpy(tmp, s._str); delete[] _str; _str = tmp; _size = s._size; _capacity = s._capacity; } return *this; } ~string() { if (_str) { delete[] _str; _str = nullptr; } _size = _capacity = 0; } private: char* _str; int _size; int _capacity; }; void test() { string s1; string s2(s1); string s3 = s1; } }
迭代器的简单模拟
typedef char* iterator; typedef const char* const_iterator; iterator begin() { return _str; } iterator end() { return _str + _size; } const_iterator begin() const { return _str; } const_iterator end() const { return _str + _size; }
其他成员函数的模拟
const char* c_str() { return _str; } size_t size() { return _size; } char& operator[](size_t pos) { assert(pos < _size); return _str[pos]; } const char& operator[](size_t pos) const { assert(pos < _size); return _str[pos]; } //reserve void reserve(size_t n) { if (n > _capacity) { //扩容到n+1 //tmp是内置类型, char* tmp = new char[n + 1]; strcpy(tmp, _str); delete[] _str; _str = tmp; _capacity = n; } } // void push_back(char c) { //空间不够,扩容 if (_size == _capacity) { //扩容 reserve(_size + 1); } _str[_size] = c; _size++; _str[_size] = '\0'; } void append(const char* str) { int len = strlen(str); if (_size + len > _capacity) { //增容 reserve(_size + len); } strcpy(_str + _size, str); _size += len; } string& operator+=(char ch) { push_back(ch); return *this; } string& operator+=(const char* str) { //复用追加函数append() append(str); return *this; } //任意位置的插入 string& insert(size_t pos, char ch) { if (_size == _capacity) { reserve(_size + 1); } //开始插入 int end = _size + 1; //找到pos的位置,并留出pos的位置以便插入 while (end > pos) { _str[end] = _str[end - 1]; end--; } _str[pos] = ch; _size++; return *this; } string& insert(size_t pos, const char* str) { assert(pos < _size); size_t len = strlen(str); if (_size + len > _capacity) { //增容 reserve(_size + len); } //找到pos的位置,并且留出要插入的位置 size_t end = _size + len; while (end > pos) { _str[end] = _str[end - len]; end--; } //开始插入 strncpy(_str + pos, str, len); return *this; } //从pos的位置开始删除len的长度 string& erase(size_t pos = 0, size_t len = std::string::npos) { assert(pos < _size); if (len == std::string::npos || pos + len > _size) { _str[pos] = '\0'; _size = pos; } else { strcpy(_str + pos, _str + pos + len); _size -= len; } return *this; }
到此这篇关于C++ String部分成员模拟实现流程详解的文章就介绍到这了,更多相关C++ String成员模拟实现内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!