【C++】深入理解String类(一)

今天我们来讲解一下c++中常用的string类,了解其使用,并且对它进行模拟实现。

标准库中的string类

在讲解c++的string之前,我们先回顾一下C语言中的字符串.

C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。

什么是String类(了解即可)

那么在c++中的string 类是什么?

  • 字符串是表示字符序列的类
  • 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
  • string在底层实际是:basic_string模板类的别名

string类的常用接口

string类对象的常见构造

函数名称功能说明
string()构造空的string类对象,即空字符串
string(const char* s)用C-string来构造string类对象
string(size_t n, char c)string类对象中包含n个字符c
string(const string&s)拷贝构造函数

string类对象常用容器操作

字符串的遍历
1. 使用 size()
for (size_t i=0;i< s2.size();i++)
	{
		s2[i] += i;
	}
	cout << endl;
2. 使用auto ch
for (auto ch : s2)
	{
		cout << ch << " ";
	}
	cout << endl;
3. 使用迭代器

迭代器是一个像指针的东西,有可能是指针,也可能不是。其好处,可以用统一类似的方式去访问修改容器。

begin()函数返回第一个有效数据位置的迭代器
end()函数返回最后一个有效数据的下一个位置的迭代器

1.所有的容器都支持迭代器(容器就是数据结构)
2.vector/string 这种结构支持下标+[]去访问,像list,map就不支持了

string::iterator it = s2.begin();
	while (it != s2.end())
	{
		cout << *it <<" ";
		++it;
	}
	cout << endl;

除了 begin()和end()之外,iterator 还提供了其他的函数:
在这里插入图片描述

  1. rebegin() 和 rend()
    简单来说,使用这一组函数就是反向遍历字符串
auto rit = s1.rbegin();
	while (rit != s1.rend())
	{
		*rit += 1;
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
  1. cbegin() 和 cend()

当我们对 字符串 只能读 不能写的时候,为了防止字符串被误改,我们可以使用这组函数替代 begin()和end()。这组函数在我们要对字符串进行输出打印时十分有用。

同时,这里要注意。我们对const对象使用迭代器是要是用 const迭代器。

void Print(const string& s)
{
	//const 对象要使用const迭代器,只读,不能写
	string::const_iterator it = s.cbegin();
	while (it != s.cend())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}
  1. crbegin() 和 crend()
    看了上面两组函数,这个函数的意义就很简单了:倒过来读const 对象
计算空间相关

在这里插入图片描述

1. size() 和 length()

两者都是计算字符串的实际长度(不包括‘\0’).
在这里,相比于size()来说,length()有些老旧了,所以我比较推荐大家使用size()
2. capacity()
我们调用s.capacity()可以知道我们给string对象分配的存储空间大小。

c++在默认初始化capacity的时候给的并不是0:
在这里插入图片描述
我们可以发现capacity直接被初始化为15(实际上是16)。

我们再看一下capacity是如何扩容的:
我们将size改为20:
在这里插入图片描述
我们可以发现capacity扩容成了31。

那么,capacity到底是怎么增长的呢?
我们使用一段程序测试一下:
在这里插入图片描述
由此可以看出,在vs编译器中,capacity每次以1.5倍去增容。

但不是所有的环境下都是按这样的规律去增容,比如在Linux中,capacity被初始化为1,每次以2倍去增容。

3. resize() 和 reserve()
  • reserve()函数
    在这里插入图片描述
    根据文档,reserve函数是用来改变capacity的,如果我们知道需要空间的大小,就可以使用reserve直接一次性开好,避免增容,提高效率

我们可以试着使用一下:
在这里插入图片描述
我们发现capacity不是100,而是111,这与vs内的自带对齐模式有关,暂时不用深入了解,这种机制并不会影响我们的使用,有兴趣的同学可以去查看其源码。

如果我们使用reseve函数开辟的空间小于原有空间的话,vs中是不会做出回应的,也就是内存不会变小。
在这里插入图片描述

这里我附上部分源码(vs2019):

 void reserve(_CRT_GUARDOVERFLOW const size_type _Newcap = 0) { // determine new minimum length of allocated storage
        if (_Mypair._Myval2._Mysize > _Newcap) { // requested capacity is not large enough for current size, ignore
            return; // nothing to do
        }

        if (_Mypair._Myval2._Myres == _Newcap) { // we're already at the requested capacity
            return; // nothing to do
        }

        if (_Mypair._Myval2._Myres < _Newcap) { // reallocate to grow
            const size_type _Old_size = _Mypair._Myval2._Mysize;
            _Reallocate_grow_by(
                _Newcap - _Old_size, [](_Elem* const _New_ptr, const _Elem* const _Old_ptr, const size_type _Old_size) {
                    _Traits::copy(_New_ptr, _Old_ptr, _Old_size + 1);
                });

            _Mypair._Myval2._Mysize = _Old_size;
            return;
        }

        if (_BUF_SIZE > _Newcap && _Mypair._Myval2._Large_string_engaged()) {
            // deallocate everything; switch back to "small" mode
            _Become_small();
            return;
        }

        // ignore requests to reserve to [_BUF_SIZE, _Myres)
    }

  • resize()函数

在这里插入图片描述
resize,顾名思义,就是调整字符串的长度

  1. 如果 n 比当前字符串长度小,则当前值将缩短为其第一个 n 字符,删除 n th 以外的字符。

在这里插入图片描述
2. 如果 n 大于当前字符串长度,则当前内容通过在末端插入尽可能多的字符来扩展,以达到 n 的大小。
如果我们不指定增加的字符,那么编译器吧自动给我添加’\0’
在这里插入图片描述

当然,如果size>capacity,capacity会判断并且增容。
所以,如果我们既要开好空间,还要对这些空间初始化,就可以用resize

增删查改相关

在这里插入图片描述
这一部分的内容较多,我只挑选比较常用的讲解:

1. operator+=

这是一个十分使用的运算符重载,可以实现字符串的连接。

在这里插入图片描述
而且在c++11中,这个函数重载除了几个版本:
在这里插入图片描述

2. append

append是一个对字符串进行尾插的函数
在这里插入图片描述
其实之前我们讲的+=在底层是用append和push_back实现的:
在这里插入图片描述
那么apend和+=有什么区别呢?

观看下列一段程序即可:

// appending to string
#include <iostream>
#include <string>

int main ()
{
  std::string str;
  std::string str2="Writing ";
  std::string str3="print 10 and then 5 more";

  // used in the same order as described above:
  str.append(str2);                       // "Writing "
  str.append(str3,6,3);                   // "10 "
  str.append("dots are cool",5);          // "dots "
  str.append("here: ");                   // "here: "
  str.append(10u,'.');                    // ".........."
  str.append(str3.begin()+8,str3.end());  // " and then 5 more"
  str.append<int>(5,0x2E);                // "....."

  std::cout << str << '\n';
  return 0;
}
 Edit & 
3. push_back

push_back是对字符串尾插一个字符的函数,由于比较简单,这里不做过多解释

4. insert

insert 插入函数,我们可以选择任意位置插入字符或者字符串。

我们这里也是通过一段程序来学习其用法:

#include <iostream>
#include <string>

int main ()
{
  std::string str="to be question";
  std::string str2="the ";
  std::string str3="or not to be";
  std::string::iterator it;

  // used in the same order as described above:
  str.insert(6,str2);                 // to be (the )question
  str.insert(6,str3,3,4);             // to be (not )the question
  str.insert(10,"that is cool",8);    // to be not (that is )the question
  str.insert(10,"to be ");            // to be not (to be )that is the question
  str.insert(15,1,':');               // to be not to be(:) that is the question
  it = str.insert(str.begin()+5,','); // to be(,) not to be: that is the question
  str.insert (str.end(),3,'.');       // to be, not to be: that is the question(...)
  str.insert (it+2,str3.begin(),str3.begin()+3); // (or )

  std::cout << str << '\n';
  return 0;
}
5. erase

erase 删除函数

我们这里依然通过一段程序来学习其用法:

#include <iostream>
#include <string>

int main ()
{
  std::string str ("This is an example sentence.");
  std::cout << str << '\n';
                                           // "This is an example sentence."
  str.erase (10,8);                        //            ^^^^^^^^
  std::cout << str << '\n';
                                           // "This is an sentence."
  str.erase (str.begin()+9);               //           ^
  std::cout << str << '\n';
                                           // "This is a sentence."
  str.erase (str.begin()+5, str.end()-9);  //       ^^^^^
  std::cout << str << '\n';
                                           // "This sentence."
  return 0;
}

其实String类的功能还有很多,这里不一一讲解,当我们见到不会的函数,我们可以通过查看文档来自行学习。

在 【C++】深入理解String类(二),我将尝试去模拟实现String类,使我们的理解更加深刻。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ornamrr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值