string类的接口
namespace zjw
{class string{public:typedef char* iterator;typedef const char* const_iterator;private:char* _str;int _size;int _capacity;};
这里的迭代器直接使用原生指针来封装。
_str为指向string数组的首地址的指针。
_size为string数组的大小。
_capacity为string数组的容量
基本函数
实现 函数返回string的首地址;
实现 函数返回string的尾地址;
实现 函数返回string的大小;
实现 函数返回string的容量;
iterator begin(){return _str;}iterator end(){return _str+_size;}size_t size(){return _size;}size_t capacity(){return _capacity;}
如果一个string类不能被修改的话,就无法调用非const的成员函数
const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}
默认构造以及析构函数
string(const char* str = ""):_size(strlen(str)){_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}~string(){ delete[]_str;_str = nullptr;_size = _capacity = 0;}
这里有一个问题,就是默认构造传的缺省参数可以是nullptr 吗??
答案是不行的,因为strlen要将str解引用找’\0’,如果传nullptr的话,对空指针解引用就会出错。
这里也不可以传单字符‘\0’,因为这里的str是const char* 类型,而’\0’是char 类型
运算符重载下标访问函数
char& operator[](size_t pos){assert(pos<_size);return _str[pos];}const char& operator[](size_t pos) const{assert(pos < _size);return _str[pos];}
下标为_size的地方存的是’\0’,所以不用访问,pos<_size,为了让const迭代器不修改对于下标的值,修改上面得到下面,对上面进行函数重载
reserve函数
void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[]_str;_str = tmp;_capacity = n;}}
注意:这里多开一个是为了存’\0’;
push_back()函数
void push_back(char ch){if (_capacity == _size){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';}
原来下标是_size的地方是‘\0’,然后之间在_size的地方放入ch,_size++,染后在补一个’\0’;要插入数据的一半都要扩容
append函数
void append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;//_str[_size] = '\0';}
尾插一个字符串,如果string的大小+要插入的字符串大小>_capacity,就扩容到_size + len,刚好,strcpy会从str开始直到遇到‘\0’一个字节一个字节拷贝到_str + _size(原字符串’\0’的地方),这里会把str字符串后面的‘\0’也拷贝过来。
运算符重载+=字符以及+=字符串
string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}
在这里传引用返回是因为_str指向的空间是在堆上开的,出函数不会被销毁,所以可以传引用返回
insert函数(插入字符)
void insert(size_t pos, char ch){assert(pos<=_size);if (_capacity == _size){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size;while (end >=pos){_str[end+1] = _str[end];end--;}_str[pos] = ch;_size++;}
上面这个写法可以吗??
是不可以的
怎么修改呢??
void insert(size_t pos, char ch){assert(pos<=_size);if (_capacity == _size){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size+1;while (end >pos){_str[end] = _str[end-1];end--;}_str[pos] = ch;_size++;}
这样就可以了,我们从’\0’的下一个位置开始end,此时end结束的条件是=0,所以不会陷入循环,这里和顺序表的insert很像,可以参考顺序表那里。
earse函数
void earse(size_t pos, size_t len = npos){assert(pos < _size);if (len == npos || pos > _size-len){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}}
删除从pos位置开始的len个字符长度的字符,如果len大于剩下的字符,就全删了,这里给的缺省值为npos=-1,可以认为是很大的数,绝对超过了pos后面的字符数,我们要在string里面声明一个npos,在类外面定义初始化。
如果len==npos或者len+pos>_size,就始要将pos开始后面的字符串全删除,我们直接在pos位置放’\0’即可,
_size=pos;
如果没有超过字符串
insert(插入字符串)
void insert(size_t pos, const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size + len;while (end > pos + len - 1){_str[end] = _str[end - len];end--;}strncpy(_str + pos, str, len);_size += len;}
void test6(){string str;str.push_back('a');str.push_back('a');str.push_back('b');str.insert(2, "hello");cout << str.c_str();}
swap
void swap(string& str){std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);}
void test12(){ string str1;string str2;str1 += "hello";str2+="nihao";cout << "str1:"<<str1 << endl;cout << "str2:"<<str2 << endl;str1.swap(str2);cout << "str1:" << str1 << endl;cout << "str2:" << str2 << endl;}
这里我们自己实现的swap是类成员函数,但是#include< algorithm >这个头文件库里面也有一个swap,swap全局也有一个非成员函数swap
我们可以发现算法库中的swap需要完成三次的拷贝,以及一次析构(临时变量c的析构),而string里面定义一个全局的swap,就是为了防止调用算法库里面的swap,因为会先在全局找,然后会在展开的库里找,而全局的swap实现只需要调用类里面的swap即可
void swap(string& x, string& y){x.swap(y);cout << "没使用库里的" << endl;}
void test13(){string str1;string str2;str1 += "hello";str2 += "nihao";cout << "str1:" << str1 << endl;cout << "str2:" << str2 << endl;swap(str1, str2);//检测是调用库里的,还是全局的cout << "str1:" << str1 << endl;cout << "str2:" << str2 << endl;}
赋值以及拷贝构造
string(const string& s){string tmp(s._str);swap(tmp);}string& operator=(string tmp){swap(tmp);return *this;}
根据调试发现
老版本赋值
string& operator=(const string& s){char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;return *this;}
find函数(查找字符)
size_t find(char ch, size_t pos = 0){for (int i = pos; i < _size; i++){if (_str[i] == ch)return i;}return npos;}
从pos位置开始查找,如果pos使用缺省,则从第一个位置开始查找,从pos位置开始遍历,如果找到返回下标,如果找不到返回npos
void test7(){string str;str.push_back('a');str.push_back('a');str.push_back('b');str.insert(2, "hello");cout << endl;int ret = str.find('b', 0);cout << ret;}
find函数(查找子串)
size_t find(const char* sub, size_t pos = 0) const{assert(pos < _size);//下标为_size的是‘\0’const char* ptr = strstr(_str + pos, sub);if (ptr != nullptr)//找到了,但是strstr返回的是找到子串的起始地址-字符串起始地址就是子串相对起始位置的长度{return ptr - _str;}else{return npos;}}
void test8(){string str;str += "beijing huanyingni zhangjiawang";int ret = str.find("zhangjiawang");cout << ret;}
substr函数
string substr(size_t pos = 0, size_t len = npos) const{string substr;if (pos > _size - len){for (int i = pos; i <= _size; i++){substr += _str[i];}}else{for (int i = pos; i < pos + len; i++){substr += _str[i];}substr += '\0';}return substr;}
从pos位置开始取,取len个长度,分两种情况,如果从pos开始还没取到len长,就结束,就取到结尾,遍历pos到_size,string substr 保存遍历的值.
第二种,遍历pos到pos+len,string substr 保存遍历的值.最后记得加‘\0’;
运算符重载比较函数
bool operator==(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str())==0;}bool operator>(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str());}bool operator<(const string& s1, const string& s2){return !(s1 > s2 && s1 == s2);}bool operator<=(const string& s1, const string& s2){return !(s1 > s2);}bool operator>=(const string& s1, const string& s2){return !(s1 < s2);}bool operator!=(const string& s1, const string& s2){return !(s1 == s2);}
注意这里为什么要搞成全局的呢??
void test10(){string str1;string str2;str1 += "abc";str2 += "aba";cout << (str1 == str2) << endl;cout << ("aba" == str2) << endl;//2cout << (str2== "aba") << endl;}
因为第二种的话不满足类成员做左操作数,全局的话就可以,而第三个是会搞一个类型转换.
void test10(){string str1;string str2;str1 += "abc";str2 += "aba";cout << (str1 == str2) << endl;cout << ("aba" == str2) << endl;cout << (str2== "aba") << endl;cout << (str1>str2) << endl;cout << (str1 >=str2) << endl;cout << (str1<str2) << endl;cout << (str1 <=str2) << endl;cout << (str1!=str2) << endl;}
运算符重载流插入
ostream& operator<<(ostream& out, const string& s){for (auto e : s)//遍历一个一个输出{out << e;}return out;}
有返回值是为了可以连续流插入
运算符重载流提取
版本一:
istream& operator>>(istream& in, string& s){char ch;in >> ch;//读取字符到chwhile (ch != ' ' && ch != '\n')//读到‘ ’或‘\n’结束输入{s += ch;//将ch加进去in >> ch;//循环读取}return in;}
这样写会有一个问题,就是cin和scanf一样默认‘\n’和‘ ’是分割符不会进行读取,所以ch不会是空格或换行,所以陷入死循环.
版本2:
c语言中可以用getchar来读取,而c++中存在get就可以读空格
istream& operator>>(istream& in, string& s){char ch;in.get(ch);while (ch != ' ' && ch != '\n'){s += ch;in.get(ch);}return in;}
void test11(){string str1;string str2;cin >> str1;//>>str2;//>>str2;cout << str1;//cout << str1.capacity();}
版本3
由于我们在s+=ch,会出现频繁扩容,影响效率,我们应该怎么解决呢??
我们可以提前开好空间,但是不知道应该开多大.假如说我们开128个
istream& operator>>(istream& in, string& s){char ch;in.get(ch);s.reserve(128);while (ch != ' ' && ch != '\n'){s += ch;in.get(ch);}return in;}
void test11(){string str1;string str2;cin >> str1;//>>str2;//>>str2;//cout << str1;cout << str1.capacity();}
两个数据开128就会有极大的浪费,多一点还好
版本4
istream& operator>>(istream& in, string& s){s.clear();//可能我们s里面之前有数据,但是流提取是要覆盖的char ch;ch = in.get();char buff[128];int i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[127] = '\0';s += buff;i = 0;}ch = in.get();}if (i > 0){buff[i] = '\0';s += buff;}return in;}
这里感觉可以类比冯诺依曼体系,buff就相当于内存,先将输入的值放在内存buff里面,等到装满了,在一次性给s,减少搬运次数.避免了一次空间开的很大,而数据只有几个
clear
void clear(){_size = 0;_str[0] = '\0';}
c_str
将const string* 转化为const char*
const char* c_str() const{return _str;}
cout不能直接处理自定义类型string,但是可以使用c_str将string转成常量字符串,内置类型就可以直接打印
源码
.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
using namespace std;
#include<assert.h>
#include<iostream>
#include<algorithm>
namespace zjw
{class string{public: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;}string(const char* str = ""):_size(strlen(str)){_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}~string(){delete[]_str;_str = nullptr;_size = _capacity = 0;}size_t size(){return _size;}size_t capacity(){return _capacity;}char& operator[](size_t pos){assert(pos < _size);return _str[pos];}const char& operator[](size_t pos) const{assert(pos < _size);return _str[pos];}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[]_str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_capacity == _size){reserve(_capacity == 0 ? 4 : _capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';}void append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;//_str[_size] = '\0';}string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}void insert(size_t pos, char ch){assert(pos <= _size);if (_capacity == _size){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];end--;}_str[pos] = ch;_size++;}const char* c_str() const{return _str;}void earse(size_t pos, size_t len = npos){assert(pos < _size);if (len == npos || pos > _size - len){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}}bool empty(){return _size == 0;}void resize(size_t n, char ch = '\0'){if (n <= _size){_str[n] = '\0';_size = n;}else{reserve(n);for (size_t i = _size; i < n; i++){_str[i] = ch;}_size = n;}}void insert(size_t pos, const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size + len;while (end > pos + len - 1){_str[end] = _str[end - len];end--;}strncpy(_str + pos, str, len);_size += len;}size_t find(char ch, size_t pos = 0){for (int i = pos; i < _size; i++){if (_str[i] == ch)return i;}return npos;}size_t find(const char* sub, size_t pos = 0) const{assert(pos < _size);const char* ptr = strstr(_str + pos, sub);if (ptr != nullptr){return ptr - _str;}else{return npos;}}string substr(size_t pos = 0, size_t len = npos) const{string substr;if (pos > _size - len){for (int i = pos; i <= _size; i++){substr += _str[i];}}else{for (int i = pos; i < pos + len; i++){substr += _str[i];}substr += '\0';}return substr;}void swap(string& str){std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);}string(const string& s){string tmp(s._str);swap(tmp);}string& operator=(string tmp){swap(tmp);return *this;}string& operator=(const string& s){char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;return *this;}void clear(){_size = 0;_str[0] = '\0';}friend istream& operator>>(istream& in, string& s);public:static const int npos;private:char* _str;int _size;int _capacity;};const int string::npos = -1;void swap(string& x, string& y){x.swap(y);cout << "没使用库里的" << endl;}istream& operator>>(istream& in, string& s){s.clear();char ch;ch = in.get();char buff[128];int i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[127] = '\0';s += buff;i = 0;}ch = in.get();}if (i > 0){buff[i] = '\0';s += buff;}return in;}//istream& operator>>(istream& in, string& s)//{// // char ch;// // in.get(ch);// // s.reserve(128);// while (ch != ' ' && ch != '\n')// {// // // s += ch;// in.get(ch);// }// // // // // return in;//}bool operator==(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str())==0;}bool operator>(const string& s1, const string& s2){return strcmp(s1.c_str(), s2.c_str());}bool operator<(const string& s1, const string& s2){return !(s1 > s2 && s1 == s2);}bool operator<=(const string& s1, const string& s2){return !(s1 > s2);}bool operator>=(const string& s1, const string& s2){return !(s1 < s2);}bool operator!=(const string& s1, const string& s2){return !(s1 == s2);}ostream& operator<<(ostream& out, const string& s){for (auto e : s){out << e;}return out;}//istream& operator>>(istream& in, string& s)//{// s.clear();// char ch;// // ch = in.get();// //in >> ch;// s.reserve(128);// while (ch != ' ' && ch != '\n')// {// // s += ch;// ch = in.get();// //in >> ch;// }// return in;//////////}void test1(){string str;str += 'a';str += 'b';str += "beijing";string::iterator it = str.begin();while (it != str.end()){cout << *it << " ";it++;}}void test2(){string str;str.push_back('a');str.push_back('a');str.push_back('b');str.push_back('a');str.push_back('a');string::iterator it = str.begin();while (it != str.end()){cout << *it << " ";it++;}cout << str[2];}void test3(){string str;str.append("stringbj");for (auto& e : str){cout << e << " ";}}void print_string(const string it){string::const_iterator res = it.begin();while (res != it.end()){cout << *res << " ";++res;}}void test4(){string str;str += "abc";str.append("stringbj");string::iterator it = str.begin();while (it != str.end()){cout << *it << " ";it++;}//str.insert(0, 'g');for (auto& e : str){cout << e << " ";}cout << endl;// str.earse(1,string::npos);*//* for (auto e : str){cout << e << " ";}*/// print_string(str);}void test5(){string str;str.push_back('a');str.push_back('a');str.push_back('b');str.resize(10);/* string::iterator it = str.begin();while (it != str.end()){cout << *it << " ";it++;}*/cout << str.c_str();}void test6(){string str;str.push_back('a');str.push_back('a');str.push_back('b');str.insert(2, "hello");cout << str.c_str();}void test7(){string str;str.push_back('a');str.push_back('a');str.push_back('b');str.insert(2, "hello");cout << endl;int ret = str.find('b', 0);cout << ret;}void test8(){string str;str += "beijing huanyingni zhangjiawang";int ret = str.find("zhangjiawang");cout << ret;}void test9(){string str;str += "beijing huanyingni zhangjiawang";string op = str.substr(0, 10);cout << op.c_str();}void test10(){string str1;string str2;str1 += "abc";str2 += "aba";cout << (str1 == str2) << endl;cout << ("aba" == str2) << endl;cout << (str2== "aba") << endl;cout << (str1>str2) << endl;cout << (str1 >=str2) << endl;cout << (str1<str2) << endl;cout << (str1 <=str2) << endl;cout << (str1!=str2) << endl;}void test11(){string str1;string str2;cin >> str1;//>>str2;//>>str2;//cout << str1;cout << str1.capacity();}void test12(){ string str1;string str2;str1 += "hello";str2+="nihao";cout << "str1:"<<str1 << endl;cout << "str2:"<<str2 << endl;str1.swap(str2);cout << "str1:" << str1 << endl;cout << "str2:" << str2 << endl;}void test13(){string str1;string str2;str1 += "hello";str2 += "nihao";cout << "str1:" << str1 << endl;cout << "str2:" << str2 << endl;swap(str1, str2);cout << "str1:" << str1 << endl;cout << "str2:" << str2 << endl;}void test14(){string str2;str2 += "nihao";string str1(str2);}}
.cpp
#include"标头.h"int main()
{zjw::test11();
}