所属专栏:C“嘎嘎" 系统学习❤️
>博主首页:初阳785❤️
>代码托管:chuyang785❤️
>感谢大家的支持,您的点赞和关注是对我最大的支持!!!❤️
>博主也会更加的努力,创作出更优质的博文!!❤️
(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;
}
函数名称 | 功能说明 |
---|---|
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;//这个等同于length
cout << "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;//这个等同于length
cout << "capacity:" << s1.capacity() << endl;
s1.clear();
if (s1.empty())
{
cout << "内容清空" << endl;
}
return 0;
}
函数名称 | 功能说明 |
---|---|
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;
}
函数名称 | 功能说明 |
---|---|
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;
}
函数 | 功能说明 |
---|---|
operator+ | 尽量少用,因为传值返回,导致深拷贝效率低 |
operator>> | (重点) 输入运算符重载 |
operator<< | (重点) 输出运算符重载 |
getline (重点) | 获取一行字符串 |
relational operators (重点) | 大小比较 |
这里说明一下getline,其他的在模拟实现里讲解。
我们正常输入一个字符串的时候大家可能都会想先想到cin输入流,但是cin遇到空格或者换行时停止输入。
所以如果我们要输入一个字符串,包含空格的化就要用到getline。
int main()
{
string s1;
getline(cin, s1);
cout << s1 << endl;
return 0;
}
首先要模拟string类,我们就要创建一个类,但是如果我们直接创建一个string类的话,会和库里面的string类相互冲突,于是就要用到我们前面的知识点——命名空间。我们可以把我们自己写的string类写在我们自己定义的一个命名空间里面,这样就避免了冲突。
namespace qfw
{
class string
{
//……
};
}
要模拟实现一个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;//全部变量,用来部分成员函数的默认参数
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;
}
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;
}
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';
}
}
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];
}
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;
}
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;
}
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}