最好的响应式网站,律师做网站,建设部门户网站,人才招聘网官网目录
①String类的主体
②String类的具体实现
1.构造函数、拷贝构造函数、赋值运算符、析构函数
⑴构造函数
⑵拷贝构造函数
⑶赋值运算符
⑷析构函数
2.迭代器#xff08;范围for的实现原理#xff09;
3.修改:push_back, apppend, , clear, swap, c_str
⑴push_b…目录
①String类的主体
②String类的具体实现
1.构造函数、拷贝构造函数、赋值运算符、析构函数
⑴构造函数
⑵拷贝构造函数
⑶赋值运算符
⑷析构函数
2.迭代器范围for的实现原理
3.修改:push_back, apppend, , clear, swap, c_str
⑴push_back
⑵apppend
⑶
⑷clear
⑸swap
⑹c_str
4.容量:size, capacity, empty, resize, reverse
⑴size
⑵capacity
⑶empty
⑷resize
⑸reverse
5.下标[]重载
6.逻辑运算符:, , , , , !
7.查找:find
8.固定位置插入元素:insert
9.清除固定位置元素:erase
10.流插入、流提取运算符重载:,
⑴流插入
⑵流提取 ①String类的主体
为了与库函数里面的string分开我们将模拟实现的string类存放在自己定义的命名空间中同时尽可能的将对应功能函数的名字与库函数一一对应即
namespace my_string
{class string{friend ostream operator(ostream _cout, const my_string::string s);friend istream operator(istream _cin, my_string::string s);public:typedef char* iterator;typedef const char* const_iterator;public:// 构造函数string(const char* str );// 拷贝构造函数string(const string s);// 赋值运算符重载string operator(const string s);// 析构函数~string();// 迭代器iterator begin();iterator end();const_iterator begin() const;const_iterator end() const;// 修改void push_back(char c);string operator(char c);void append(const char* str);string operator(const char* str);void clear();void swap(string s);const char* c_str()const;// 容量size_t size()const;size_t capacity()const;bool empty()const;void resize(size_t n, char c \0);void reserve(size_t n);// 下标char operator[](size_t index);const char operator[](size_t index)const;// 逻辑运算符bool operator(const string s);bool operator(const string s);bool operator(const string s);bool operator(const string s);bool operator(const string s);bool operator!(const string s);// 返回c在string中第一次出现的位置size_t find(char c, size_t pos 0) const;// 返回子串s在string中第一次出现的位置size_t find(const char* s, size_t pos 0) const;// 在pos位置上插入字符c/字符串str并返回该字符的位置string insert(size_t pos, char c);string insert(size_t pos, const char* str);// 删除pos位置上的元素并返回该元素的下一个位置string erase(size_t pos, size_t len);private:char* _str;size_t _capacity;size_t _size;};};
②String类的具体实现
1.构造函数、拷贝构造函数、赋值运算符、析构函数
⑴构造函数
// 构造函数
string(const char* str )
{_size strlen(str);_capacity _size;_str new char[_capacity 1];// 字符串后有\0因此要1memcpy(_str, str, sizeof(char) * (_size 1));// 与上述同理
}
⑵拷贝构造函数
// 拷贝构造函数
string(const string s)
{_size s._size;_capacity s._capacity;// 这里不能直接让_strs._str即浅拷贝// 不然会导致两个string最后析构时对它们指向的空间析构两次// 此外如果其中一个对象对这片空间的元素进行修改会对另一个对象造成影响// 因此此处只能使用深拷贝_str new char[s._capacity 1];memcpy(_str, s._str, sizeof(char) * (s._size 1));
}
⑶赋值运算符
在模拟实现赋值运算符时有两种写法一种是与拷贝构造逻辑类似的写法即
// 赋值运算符重载
string operator(const string s)
{if (this ! s)// 与拷贝构造逻辑相同{_size s._size;_capacity s._capacity;_str new char[s._capacity 1];memcpy(_str, s._str, sizeof(char) * (s._size 1));}return *this;
}
另一种则是通过拷贝构造一个临时对象tmp将this与tmp所指向的空间交换在结束时tmp还能顺便释放自己所指向的空间即
void swap(string s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}// 赋值运算符重载
string operator(const string s)
{if (this ! s){string tmp(s);swap(tmp);}return *this;
}
图示如下 从图中我们可以明显看出在此函数调用完后tmp也会顺带将s1先前所指向的空间释放
⑷析构函数
// 析构函数
~string()
{_size _capacity 0;delete[] _str;_str nullptr;
}
2.迭代器范围for的实现原理
从string类的主体中我们不难看出string类的迭代器其实就是指针即 这是因为string类的对象本身处在一个连续的空间内可以直接通过指针来操控因此我们便可以得到
// 迭代器
iterator begin()
{return _str;
}iterator end()
{return _str _size;
}const_iterator begin() const
{return _str;
}const_iterator end() const
{return _str _size;
}
在这里我们可以看看范围for是如何做到的首先我们直接使用有
而当我们将任意一个迭代器(begin或end注释掉后可以发现)
因此我们可以发现其实所谓的范围for不过就是将这段代码固定的替换成如下这段代码
my_string::string::iterator it s.begin();
while (it ! s.end())
{cout *it;it;
}
而且范围for只会固定的寻找end和begin名字的迭代器略微修改名字也会报错
3.修改:push_back, apppend, , clear, swap, c_str
⑴push_back
void push_back(char c)
{// 当size与capacity相等时需要扩容if (_size _capacity){// 扩容逻辑与reserve类似char* tmp new char[_capacity * 2];memcpy(tmp, _str, sizeof(char) * _size);delete[] _str;_str tmp;_capacity * 2;}_str[_size] c;
}
⑵apppend
void append(const char* str)
{// 与push_back的插入逻辑类似// 但是扩容大小需要作出变化size_t len strlen(str);if (len _size _capacity){reserve(2 * (len _capacity));}for (size_t i 0; i len; i){push_back(str[i]);}
}
⑶
// 直接复用即可
string operator(char c)
{push_back(c);return *this;
}string operator(const char* str)
{append(str);return *this;
}
⑷clear
void clear()
{// 容量一般不缩小以防重复开辟空间降低效率_str[0] \0;_size 0;
}
⑸swap
void swap(string s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}
⑹c_str
const char* c_str()const
{return _str;
}
4.容量:size, capacity, empty, resize, reverse
⑴size
size_t size()const
{return _size;
}
⑵capacity
size_t capacity()const
{return _capacity;
}
⑶empty
bool empty()const
{return _size 0;
}
⑷resize
在这里分为多种情况如图 实现如下
void resize(size_t n, char c \0)
{if (n _size){_str[_size] \0;// 直接将截止位置提前到size的位置即可}else{// 如果是情况三先进行扩容然后统一插入数据if (n _capacity){reserve(n);}for (int i _size; i n; i){_str[i] c;}}_size n;
}
⑸reverse 在这里我们一般不选择将capacity缩小因为缩小也是需要代价的可能会开辟一片新的空间因此实现如下
void reserve(size_t n)
{if (n _capacity){// 扩容可能会开辟新空间char* tmp new char[n 1];memcpy(tmp, _str, sizeof(char) * (_size 1));delete[] _str;_str tmp;_capacity n;}
}
5.下标[]重载
// 下标
char operator[](size_t index)
{assert(index _size);//此处因为传入参数为size_t因此不需要判断index是否小于0return *(_str index);
}const char operator[](size_t index)const
{assert(index _size);return *(_str index);
}
6.逻辑运算符:, , , , , !
字符串比较大小的规则是逐字符比较ASCII码值相等的话就比较下一个在这里两个字符串相比较也会有多种情况如下图所示 我们可以实现任一比较大小和相等之后不断复用来做到简化即
// 逻辑运算符
bool operator(const string s)
{int i 0;// 一直迭代到两字符不相等或下标i越界while (i _size i s.size() s[i] _str[i]){i;}// 如果下标i超过了string1的size需要进行特殊判断if (i _size){//如果两个字符串的大小相等说明此时它们均相同if (_size s.size()){return false;}else//反之表示string1截止在i这个下标处string1string2{return true;}}// 如果下标i超过了string2的size此时string1始终是或string2的// 因此返回falseif (i s.size()){return false;}if (_str[i] s[i]){return true;}else{return false;}
}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 || *this s);
}bool operator(const string s)
{int i 0;while (i _size i s.size() s[i] _str[i]){i;}// 此时下标i要么越界要么就在两个字符串中分别指向不同的字符// 因此只有当i越界且两个字符串大小相等时才能相等其余情况都不行if ((i _size || i s.size()) _size s.size()){return true;}else{return false;}
}bool operator!(const string s)
{return !(*this s);
}
7.查找:find
// 返回c在string中第一次出现的位置
size_t find(char c, size_t pos 0) const
{assert(pos _size);int i 0;// 寻找到与c相同的字符或者越界为止while (i _size _str[i] ! c){i;}if (_str[i] c){return i;}else{return -1;}
}// 返回子串s在string中第一次出现的位置
size_t find(const char* s, size_t pos 0) const
{assert(pos _size);const char* tmp strstr(_str pos, s);if (tmp ! nullptr){return tmp - s;}else{return -1;}
}
8.固定位置插入元素:insert
这里先实现插入单个字符
// 在pos位置上插入字符c并返回该字符
string insert(size_t pos, char c)
{assert(pos _size);// 检查是否需要扩容if (_size _capacity){reserve(2 * _capacity);}// 从最后一个数据开始将每个数据向后挪动int i 0;for (i _size; i pos; i--){_str[i] _str[i - 1];}_str[i] c;_size;return *this;
}
然后是实现插入一串子串举例如下 即
// 在pos位置上插入字符串str并返回该字符的位置
string insert(size_t pos, const char* str)
{assert(pos _size);int len strlen(str);if (_size len _capacity){reserve(2 * (_size len));}// 向后挪位置腾出空间int i 0;// 在这里i与pos比较时会发生隐式类型转换因此还需要添加一个条件for (i _size - 1; i ! -1 i pos; i--){_str[i len] _str[i];}// 从pos位置开始将str的内容拷贝到string中memcpy(_str pos, str, sizeof(char) * len);_size len;return *this;
}
9.清除固定位置元素:erase
// 删除pos位置上的元素并返回字符
string erase(size_t pos, size_t len)
{assert(pos _size);// 如果poslen超过了_size则表示要将pos及其之后的字符全部删除if (pos len _size){_str[pos] \0;_size pos;}else{int i 0;// 从第poslen的位置开始将数据分别向前挪动覆盖for (i poslen; i _size; i){_str[i - len] _str[i];}_size - len;}return *this;
}
10.流插入、流提取运算符重载:,
⑴流插入
friend ostream operator(ostream _cout, const my_string::string s)
{for (char c : s){cout c;}cout endl;return _cout;
}
⑵流提取
friend istream operator(istream _cin, my_string::string s)
{// 在每次输入前需要对缓冲区作出处理s.clear();// 删除存在于最前方的空格与换行符char ch _cin.get();while (ch || ch \n){ch _cin.get();}// 在这里我们创建一个临时的缓冲器来存放s// 这样的话每次插入字符数只有超过了128时将其尾插到s中// 之后归零i从而避免扩容,以此提高插入效率char buff[128] { 0 };int i 0;while (ch ! ch ! \n){buff[i] ch;// 当i到达127时将此位置置为\0然后将i重置为0if (i 127){buff[i] \0;s buff;i 0;}ch _cin.get();}if (i ! 0){buff[i] \0;s buff;}return _cin;
}