欢迎来到本期节目- - -
string类
以下列举的是string类的常用接口,详情查看cplusplus
构造函数的使用:
以下是string类对象最常见的构造方式
#include<string>
using namespace std;void test1()
{string str1; //调用默认构造函数,构造一个空字符串string str2("I am string"); //调用带参构造,参数是c格式字符串string str3(str2); //调用拷贝构造
}
容器方法的使用:
以下是常用的方法
#include<iostream>
#include<string>
using namespace std;void test2()
{string str("I am string");cout << str.size() << endl; //返回字符串的有效长度cout << str.capacity() << endl; //返回字符串当前的空间总大小(>=字符串的总长度)cout << str.clear() << endl; //清空有效字符, 不影响字符串的容量空间cout << str.empty() << endl; //判断字符串是否为空字符串cout << str.resize(20,'x') << endl; //把有效字符个数改为20个,多出的空间用'x'填充cout << str.reserve(30) << endl; //让字符串的空间容量至少为30个有效字符.
}
注意:
void resize (size_t n);void resize (size_t n, char c);
n是用户需要的有效字符长度; |
c是用户需要的指定字符; |
当n小于实际的有效字符长度时,该函数只会保留前n个字符,删除其余字符. |
当n大于实际的有效字符长度时,该函数会将多出来的空间用c填充,如果未指定,则填充空字符.(可能会扩容) |
void reserve(size_t n = 0);
n是用户期望的容量大小; |
当n小于字符串实际的容量大小,没有明确的规定要不要缩容,但是绝不会影响到字符串的有效长度. |
当n大于字符串实际的容量大小,字符串的容量至少扩容到能存储n个有效字符。(实际开了n+1个,为了存空字符) |
查找方法的使用:
以下是常用的方法
#include<iostream>
#include<string>
using namespace std;void test3()
{string str("test");//打印字符的方式for(int i = 0; i < str.size(); i++){cout << str[i] << " "; //str.operator[](i)}cout << endl;//打印字符串的方式cout << str.c_str() << endl; //该方法返回的是c格式字符串的首地址str += ".cpp"; //添加字符串size_t pos = str.find('.'); //在字符串中正向查找'.' 找到后返回第一个出现该字符的位置if(pos == string::npos) //没找到返回npos实际上npos=-1{cout << "未找到\n" << endl;return ;}cout << "后缀名是:" << endl;cout << str.c_str()+pos << endl; //找到了,打印后缀}
推荐
string& operator+= (const string& str);
string& operator+= (const char* s);
string& operator+= (char c);
operator+=是最常用的接口,因为不仅方便,而且可读性强,代码简洁。 |
非类成员使用
#include<iostream>
#include<string>
using namespace std;void test()
{string str;cin >> str; //流插入,从标准流提取单词输入到字符串中,因为空格为分隔符cout << str << endl; //流提取,把字符串输出到屏幕上//如果想要输入一行文本getline(cin,str); //遇到换行符提取并丢弃,然后停止读取cout << str << endl;
}
注意:
cin和scanf在读取单词之后遇到分隔符会停止读取,最后的分割符还在缓冲区. |
istream& getline (istream& is, string& str, char delim);
istream& getline (istream& is, string& str);
delim是终止符; |
getline遇到指定的delim,就提取并丢弃然后停止读取. |
否则遇到换行符,提取并丢弃然后停止读取. |
string类的结构
尽管在不同的环境下,string类的功能都是一样的,但是底层的结构可能不一样:
以下以32位平台为例:
在vs2019中
在这个环境下,共用体占16个字节;
如果字符串的长度小于16,使用内部固定的字符数组存放;(使用_Buff) |
如果字符串的长度大于等于16,则从堆上开辟空间存放;( 使用_Ptr) |
所以vs中,string类大小占16+4+4+4=28字节.
在g++中
string类只有一个指针
在32位平台下的,string对象的大小共占4字节,引用计数_M_refcount是为写时拷贝服务的.
string类的模拟实现
看了上面的结构,是不是想动刀子的心情都有了,
但是不用担心,模拟实现没有必要完全一模一样,
我们只需要把核心的结构以及功能实现就可以了.
以下是鄙人的代码
首先我们已经了解了string类的核心成员变量:
- 指向字符数组的指针(在堆上申请空间存储c格式字符串)
- size_t字段(存储字符串的长度)
- size_t字段(存储字符串的容量)
class string
{
public://方法
private:char* _arr;size_t _size;size_t _capacity;
}
这里string类方法也可以分为默认成员和迭代器和增删差改
string.h
namespace my_room
{class string{//友元函数friend ostream& operator<<(ostream& _cout, const string& s);friend istream& operator>>(istream& _cin, string& s);public://迭代器typedef char* iterator;typedef const char* const_iterator;iterator begin();const_iterator begin()const;iterator end();const_iterator end()const;public://默认成员函数string(const char* str = "");string(const string& s);string& operator=(const string& s);~string();////增删查改size_t insert(size_t pos, char c);size_t insert(size_t pos, const char* str);void push_back(char c);string& operator+=(char c);void append(const char* str);string& operator+=(const char* str);string& erase(size_t pos, size_t len);size_t find(char c, size_t pos = 0) const;size_t find(const char* s, size_t pos = 0) const;///扩容以及与属性相关的方法void resize(size_t n, char c = '\0');void reserve(size_t n);size_t size()const;size_t capacity()const;bool empty()const;char& operator[](size_t index);const char& operator[](size_t index)const;void clear();void swap(string& s);const char* c_str()const;///relational operators比较字符串大小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);private:char* _str = nullptr;size_t _capacity = 0;size_t _size = 0;};
}
以下是string.cpp
首先我们把默认成员函数搞定:
namespace my_room
{string::string(const char* str) :_str(new char[1]{0}) //如果是空字符串,需要放一个'\0'{int size = strlen(str);reserve(size);for (int i = 0; i < size; i++){_str[i] = str[i];}_size = size;}string::string(const string& s):_str(new char[1]{ 0 }){string tmp(s.c_str()); //这里拷贝构造的tmp就是我们的需要的字符串swap(tmp); //把不需要的this指向的_str释放掉}string& string::operator=(const string& s){if (this != &s){string tmp(s.c_str()); //与拷贝构造的原理相同swap(tmp);}return *this;}string::~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}
}
注意:
用c格式空串构造字符串时,_str如果为nullptr,会有空指针解引用问题,所以应该在初始化列表中初始化一个'\0'. |
接下来让我们把复用的函数给它解决掉:
namespace my_room
{void string::reserve(size_t n){if (n > capacity()){//异地扩容:开新空间,拷贝数据,释放旧空间char* tmp = new char[n + 1]{0}; //这个n和_capacity都表示有效字符串容量,所以实际多开一个for (int i = 0; i < size(); i++) {tmp[i] = _str[i];}delete[] _str;_str = tmp;_capacity = n;}}size_t string::insert(size_t pos, char c){assert(pos <= size()); //取等于,因为可以在尾部插入if (_size + 1 > capacity()){int n = 0;n = capacity()==0 ? 4 : capacity()*2;reserve(n);}int end = size();while (end>pos) //从后往前移动数据{_str[end] = _str[end - 1];end--;}_str[pos] = c;_size++;_str[_size] = 0; //别忘了给字符串结束标志return pos;}size_t string::insert(size_t pos, const char* str){assert(pos <= size());int len = strlen(str);if (_size + len > capacity()){int n = 0;n = _size + len > capacity() * 2 ? _size + len : capacity() * 2;reserve(n);}int end = _size + len;while (end > pos+len-1) //当len为1时,和上面一样的{_str[end] = _str[end - len];end--;}for (int i = 0; i < len; i++){_str[pos + i] = str[i];}_size += len;_str[_size] = 0;return pos;}// 删除pos位置后的len个元素,并返回该元素的下一个位置size_t string::erase(size_t pos, size_t len){assert(pos < size());if (pos + len >= size()) //删除pos位置以及之后的数据{len = size() - pos;}else{int begin = pos+len;while (begin < size()){_str[begin - len] = _str[begin];begin++;}}_size -= len;_str[_size] = 0; //字符串结束标志符return pos;}
}
注意:
在insert函数中的pos的比较问题尽量不要使用等号,因为在边界0的情况下,比较无符号整形容易出现意料之外的问题. |
还有一些简单的函数:
namespace my_room
{size_t string::size()const{return _size;}size_t string::capacity()const{return _capacity;}bool string::empty()const{return _size == 0;}const char* string::c_str()const{return _str;}//------------------------------------------------------------------------char& string::operator[](size_t index){return _str[index];}const char& string::operator[](size_t index)const{return _str[index];}//-------------------------------------------------------------bool string::operator<(const string& s){return strcmp(_str, s._str) < 0;}bool string::operator<=(const string& s){return !(*this > s);}bool string::operator>(const string& s){return strcmp(_str, s._str)>0;}bool string::operator>=(const string& s){return !(*this < s);}bool string::operator==(const string& s){return strcmp(_str, s._str) == 0;}bool string::operator!=(const string& s){return !(*this == s);}
//---------------------------------------------------------------------- void string::clear(){_str[0] = 0;_size = 0;}void string::swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}
}
剩下的几乎都是代码复用:
namespace my_room
{void string::resize(size_t n, char c){reserve(n);if (n > size()){while (n > size()){push_back(c);}}else{_str[n] = '\0';_size = n;}}void string::push_back(char c){insert(size(), c);}string& string::operator+=(char c){insert(size(), c);return *this;}void string::append(const char* str){insert(size(), str);}string& string::operator+=(const char* str){insert(size(), str);return *this;}//----------------------------------------------------------------size_t string::find(char c, size_t pos) const{assert(pos < size());for (size_t i = pos; i < size(); i++){if (_str[i] == c){return i;}}return -1;}size_t string::find(const char* s, size_t pos) const //相当于模拟strstr{assert(pos < size());size_t len = strlen(s);if (pos + len <= size()){const char* cur = _str + pos;while (*cur){const char* dest = cur;const char* src = s;while ((*dest == *src) && *src){dest++;src++;}if (*src == 0){return cur - _str;}cur++;}}return -1;}
}
还剩下个迭代器:
namespace my_room
{string::iterator string::begin(){return _str;}string::const_iterator string::begin()const{return _str;}string::iterator string::end(){return _str + _size;}string::const_iterator string::end()const{return _str + _size;}
}
小伙伴们,动起手来吧,只要稍微一动手,就发现真的是soeasy!🥰
你看鄙人都写出来了勒(写的时候不知道出了多少bug!🙂,有错请指出然后给我一句建议:还得练啊)
希望该片文章对您有帮助,请点赞支持一下吧😘💕