SQL之string的使用与模拟实现
- 1.官方库中string类的使用接口
- 1.1 工具网站搜索string类的使用
- 2.2string类的常用接口说明
- 2.模拟实现重要/常用的成员函数接口
- 2.1 准备工作
- 2.1.1. 解决命名冲突
- 2.1.2. 成员变量
- 2.1.3. 默认成员函数——构造函数/拷贝构造函数/析构函数
- 2.1.4赋值运算符重载
- 2.2成员函数模拟模拟实现
- 2.2.1. string类对象的容量操作
- 2.2.2. string类对象的访问及遍历操作
- 2.2.3. string类对象的修改操作
- 2.2.4. string类非成员函数——流插入,流输出
- 2.2.5 swap交换
所属专栏:C“嘎嘎" 系统学习❤️
🚀 >博主首页:初阳785❤️
🚀 >代码托管:chuyang785❤️
🚀 >感谢大家的支持,您的点赞和关注是对我最大的支持!!!❤️
🚀 >博主也会更加的努力,创作出更优质的博文!!❤️
1.官方库中string类的使用接口
1.1 工具网站搜索string类的使用
- 这里提供个大家一个查询官方库中各个函数的使用方法的网站链接: https://cplusplus.com/
打开后是这个界面:
- 然后点击红色框框——切换老版本,因为新版本不支持查询搜索,老版本支持:
- 我们这里可以进行搜索string。
这里就会显示所有的string类中的成员函数,也可以点击进行详细的功能显示。
2.2string类的常用接口说明
- string类对象的常见构造
(constructor)函数名称 | 功能说明 |
---|---|
string() (重点) | 构造空的string类对象,即空字符串 |
string(const char* s) (重点) | 用C-string来构造string类对象 |
string(size_t n, char c) | string类对象中包含n个字符c |
string(const string&s) | (重点) 拷贝构造函数 |
int main()
{string s1;//这种写法就等于string s1()这种写法,但是不能这样写,//因为编译器可能会把不能分辨他是一个函数还是一个对象string s2("hello world");string s3(4, 'a');string s4(s2);cout << s1 << endl;cout << s2 << endl;cout << s3 << endl;cout << s4 << endl;return 0;
}
- string类对象的容量操作
函数名称 | 功能说明 |
---|---|
size(重点) | 返回字符串有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回空间总大小 |
empty (重点) | 检测字符串释放为空串,是返回true,否则返回false |
clear (重点) | 清空有效字符 |
reserve (重点) | 为字符串预留空间** |
resize (重点) | 将有效字符的个数该成n个,多出的空间用字符c填充 |
注意:
- size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一
致,一般情况下基本都是用size()。- clear()只是将string中有效字符清空,不改变底层空间大小。
- resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字
符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的
元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大
小,如果是将元素个数减少,底层空间总大小不变。- reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于
string的底层空间总大小时,reserver不会改变容量大小
int main()
{string s1("hello world");cout << "size:" << s1.size() << endl;//这个等同于lengthcout << "capacity:" << s1.capacity() << endl;s1.resize(13);cout << "size:" << s1.size() << endl;cout << "capacity:" << s1.capacity() << endl;s1.reserve(20);cout << "size:" << s1.size() << endl;//这个等同于lengthcout << "capacity:" << s1.capacity() << endl;s1.clear();if (s1.empty()){cout << "内容清空" << endl;}return 0;
}
- string类对象的访问及遍历操作
函数名称 | 功能说明 |
---|---|
operator[] (重点) | 返回pos位置的字符,const string类对象调用 |
begin+ end | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 |
rbegin + rend | rbegin获取最后一个字符下一个位置的迭代器 + rend获取第一个字符位置的迭代器 |
范围for | C++11支持更简洁的范围for的新遍历方式 |
int main()
{string s1("hello world");for (int i = 0; i < s1.size(); i++){cout << s1[i];}cout << endl;string::iterator it = s1.begin();while (it != s1.end()){cout << *it;it++;}cout << endl;string::reverse_iterator ri = s1.rbegin();while (ri != s1.rend()){cout << *ri;ri++;}cout << endl;return 0;
}
- string类对象的修改操作
函数名称 | 功能说明 |
---|---|
push_back | 在字符串后尾插字符c |
append | 在字符串后追加一个字符串 |
operator+= (重点) | 在字符串后追加字符串str |
c_str(重点) | 返回C格式字符串 |
find + npos(重点) | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
rfind | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
int main()
{string s1("hello world");cout << s1.c_str() << endl;s1.push_back('!');cout << s1 << endl;s1.append(" I am user");cout << s1 << endl;s1 += " !";cout << s1 << endl;int pos = s1.find('!');cout << pos << endl;pos = s1.rfind('!');cout << pos << endl;return 0;
}
- string类非成员函数
函数 | 功能说明 |
---|---|
operator+ | 尽量少用,因为传值返回,导致深拷贝效率低 |
operator>> | (重点) 输入运算符重载 |
operator<< | (重点) 输出运算符重载 |
getline (重点) | 获取一行字符串 |
relational operators (重点) | 大小比较 |
这里说明一下getline,其他的在模拟实现里讲解。
我们正常输入一个字符串的时候大家可能都会想先想到cin输入流,但是cin遇到空格或者换行时停止输入。
所以如果我们要输入一个字符串,包含空格的化就要用到getline。
int main()
{string s1;getline(cin, s1);cout << s1 << endl;return 0;
}
2.模拟实现重要/常用的成员函数接口
2.1 准备工作
2.1.1. 解决命名冲突
首先要模拟string类,我们就要创建一个类,但是如果我们直接创建一个string类的话,会和库里面的string类相互冲突,于是就要用到我们前面的知识点——命名空间。我们可以把我们自己写的string类写在我们自己定义的一个命名空间里面,这样就避免了冲突。
namespace qfw
{class string{//……};
}
2.1.2. 成员变量
要模拟实现一个sting类,首要的是要弄明白string类的内部结构,最重要的就是它的成员变量有哪些。从上面的 使用情况来看,我们可以判断首先有存放字符串的一个指针_str,再就是string的容量_capacity,以及string的字符个数_size。这我们也可以结合之前的数据结构中的单链表来类比。
private:char* _str = nullptr;size_t _capacity = 0;size_t _size = 0;const static size_t npos = -1;//全部变量,用来部分成员函数的默认参数
2.1.3. 默认成员函数——构造函数/拷贝构造函数/析构函数
- 构造函数的设计
上面的使用情况,我们可以知道,构造函数有四种情况1.空构造,2.字符串构造,3.n个字符构造,4.拷贝构造
这里我们可以将其总结成两大类别:1.空构造,2.有参构造。
所以这里要设计成缺省参数。
string(const char* str = ""){_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];//这里多开一个空间给"\0"strcpy(_str, str);}
这里能不能吧默认参数写成const char* str = nullptr,答案是不能,因为如果时空构造的话,str=nullptr这个时候strlen()计算大小的时候就相当于对str进行解引用,也就是对nullptr进行解引用了。
- 拷贝构造
如果这里我们不自己写拷贝构造的话,编译器就会调用默认拷贝构造,就是浅拷贝。但是这里我们成员变量中有指针,而且这个指针是后续用来动态开辟内存的,也就是说涉及到了动态的资源管理。这个问题我们之前的章节也有讲过,如果是用浅拷贝的话就会出现野指针的问题,也就是所同一块空间被释放了两次,所以这里就不能使用浅拷贝,而是要用深拷贝。
string(const string& s){_str = new char[s._capacity + 1];//这里多开一个空间给"\0"_size = s._size;_capacity = s._capacity;strcpy(_str, s._str);}
这里也有第二种写法:
string(const string& s)
{string tmp(s._str);swap(tmp);
}
注:swap函数后面模拟实现中有写到。
这种写法巧妙的服用了默认构造函数。
- 析构函数
析构函数就像对于比较简单,直接释放就行。
~string(){delete[] _str;_str = nullptr;_size = 0;_capacity = 0;}
2.1.4赋值运算符重载
string& operator=(const string& s)
{char* tmp = new char[s._capacity + 1];//先开辟一块新的空间delete[] _str;//释放原来的空间_str = tmp;//指向新的空间strcpy(_str, s._str);//将值拷贝到新的空间_size = s._size;_capacity = s._capacity;return *this;
}
这里也有第二种写法:
string& operator=(string s)//注意这里是传值操作,并且不能别const修饰{swap(s);return *this;}
这里同样巧妙服用了拷贝构造。
2.2成员函数模拟模拟实现
2.2.1. string类对象的容量操作
size_t size(){return _size;}size_t capacity(){return _capacity;}bool empty()const{return _size == 0;}void clear(){_str[0] = '\0';_size = 0;}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];//这里多开一个空间给"\0"strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void resize(size_t n, char c = '\0){if (n > _capacity){reserve(n);}if (n <= _size){_str[n] = '\0';_size = n;}else{int cout = n - _size;for (int i = _size; i < n; i++){_str[i] = c;_size++;}_str[_size] = '\0';}}
2.2.2. string类对象的访问及遍历操作
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& operator[](size_t pos) const//引用返回的两个好处:1.减少拷贝。2.修改返回值 //只读
{assert(pos <= _size);return _str[pos];
}char& operator[](size_t pos)//引用返回的两个好处:1.减少拷贝。2.修改返回值 //可读可写
{assert(pos <= _size);return _str[pos];
}
2.2.3. string类对象的修改操作
void push_back(char ch)
{if (_size == _capacity){size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newCapacity);}_str[_size] = ch;_size++;_str[_size] = '\0';
}void append(const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){reserve(len + _size);}strcpy(_str + _size, str);_size += len;
}string& operator+=(char ch)
{push_back(ch);return *this;
}string& operator+=(const char* str)
{append(str);return *this;
}const char* c_str() const
{return _str;
}size_t find(char ch, size_t pos = 0) const
{assert(pos < _size);for (int i = pos; i < _size; i++){if (_str[i] == ch)return i;}return npos;
}size_t find(const char* str, size_t pos = 0) const
{assert(pos < _size);char* ret = strstr(_str, str);if (ret){return ret - _str;}return npos;
}string substr(size_t pos = 0, size_t len = npos) const
{string tmp;assert(pos < _size);size_t end = pos + len;if (len == npos || pos + len >= _size){end = _size;}tmp.reserve(end - pos);for (int i = pos; i < end; i++){tmp += _str[i];}return tmp;
}
void insert(size_t pos, char ch)
{assert(pos <= _size);if (_size == _capacity){size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newCapacity);}size_t end = _size + 1;while (end != pos){_str[end] = _str[end - 1];--end;}_str[end] = ch;_size++;//return *this;
}void insert(size_t pos, const char* str)
{assert(pos <= _size);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--;}_size += len;strncpy(_str + pos, str, len);//return *this;
}void erase(size_t pos = 0, size_t len = npos)
{assert(pos < _size);if (len == npos || pos + len >= _size){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}//return *this;
}
2.2.4. string类非成员函数——流插入,流输出
ostream& operator<<(ostream& out, const string& s)
{for (int i = 0; i < s.size(); i++){out << s[i];}return out;
}istream& operator>>(istream& in, string& s)
{s.clear();char ch;in.get(ch);while (ch != ' ' && ch != '\n'){s += ch;in.get(ch);}return in;
}
2.2.5 swap交换
void swap(string& s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}