Update:2020-3-2。
之前总结的,初学编程,较为青涩,有很多表达不是很清楚,表述不完善,有幸得到一个博友的指正,于是再次更新。
但由于水平有限,如果发现有问题和需要补充的,请务必留言评论。
文章分为两部分:
第一部分为简单的方法使用探究,目的是为了快速入门学回使用。
第二部分为简单的源码分析,目的是为了简单的了解一下运行的方式。
目录
1.阅读前提:
- 知道迭代器(iterator)是什么。
- string中返回值为迭代器的方法:
iterator begin();
interator end();
iterator erase(iterator _position);
iterator erase(iterator _first,iterator _last);
interator insert(iterator __p,_CharT __c);
const_iterator begin();
const_iterator cbegin();
const_iterator cend();
const_iterator end();
const_reverse_iterator crbegin();
const_reverse_iterator crend();
可以看到,string内置的迭代器的返回值只有begin(),end()。
- begin()返回string中第一个元素所对应的位置。
- end()返回string中最后一个元素的后一个位置(可以用字符串末尾的"\0"来理解:我们不写"\0",但还是存在。
注意,这里是可以理解,真正的解释可能不是这个...)。 - 可以看出来了,这个英文解释很清楚:指向最后一个元素之后的元素。
erase是我们这次探究的。insert是不用考虑的,所以,很多操作都是基于begin()和end()来实现的。
const的不考虑。
2.使用环境:
编译器:Dev-C++ 5.11的所有默认设置。
3.方法探究:
首先获得一个string对象,然后看其erase都有重载。
感觉这前两个个就比较常用了,现在探究一下这两个。(第三个返回一个basic_string类,感觉应该是构造的时候使用的)
3.1删除一段字符串
看参数应该差不多理解了:删除一段的字符串。
iterator erase(iterator _first,iterator _last)
参数:传入删除首尾的迭代器,返回一个迭代器。
3.1.1测试一:
由图可得:删除了[23456],即[第2位,倒数第3位)。返回了7,即删除字符串的后一位
3.1.2测试二:
删除了[2],即[第2位,第3位)。返回了3.
3.1.3结论:
iterator erase(iterator _first,iterator _last)
方法删除区间为前闭后开区间。返回删除字符串的后一位。
3.2删除一个字符串
看参数应该就清楚了,删除指定位置的字符。
iterator erase(iterator _position)
参数:传入指定位置的迭代器。
3.2.1测试一:
删除了第二个字符,即第二位,返回3,即删除元素的后一位。
3.2.2测试二:
删除了最后倒数第二位,返回结果为9。
3.2.3结论:
删除指定位置的字符,返回删除位置的后一位。
4.方法分析
4.1整体结构的把握:
首先点开erase的方法,进入了basic_string.h文件。可以看到里面简单的介绍:
进入basic_string.tcc文件,可以看到其中的源码:基本一样。
到了这里,应该就清楚参数和返回值了。
4.2方法深入的探究:
可以看出erase方法的核心就是其中的_M_mutate()和_M_rep()了。下面我们来尝试探索一下。
4.2.1_M_mutate()方法探索。
也就是说_M_mutate()就是获得对应空间的方法。
4.2.2_M_Rep探究
这个是一个结构体,basic_string.h中的[147,170]行里面。
作用大概也是内存计数和状态。
代码在下面:其中的Alloc就在Rep结构体的下面。有兴趣的自行了解吧。
struct _Rep_base
{
size_type _M_length;
size_type _M_capacity;
_Atomic_word _M_refcount;
};
struct _Rep : _Rep_base
{
// Types:
typedef typename _Alloc::template rebind<char>::other _Raw_bytes_alloc;
// (Public) Data members:
// The maximum number of individual char_type elements of an
// individual string is determined by _S_max_size. This is the
// value that will be returned by max_size(). (Whereas npos
// is the maximum number of bytes the allocator can allocate.)
// If one was to divvy up the theoretical largest size string,
// with a terminating character and m _CharT elements, it'd
// look like this:
// npos = sizeof(_Rep) + (m * sizeof(_CharT)) + sizeof(_CharT)
// Solving for m:
// m = ((npos - sizeof(_Rep))/sizeof(CharT)) - 1
// In addition, this implementation quarters this amount.
static const size_type _S_max_size;
static const _CharT _S_terminal;
// The following storage is init'd to 0 by the linker, resulting
// (carefully) in an empty string with one reference.
static size_type _S_empty_rep_storage[];
static _Rep&
_S_empty_rep() _GLIBCXX_NOEXCEPT
{
// NB: Mild hack to avoid strict-aliasing warnings. Note that
// _S_empty_rep_storage is never modified and the punning should
// be reasonably safe in this case.
void* __p = reinterpret_cast<void*>(&_S_empty_rep_storage);
return *reinterpret_cast<_Rep*>(__p);
}
bool
_M_is_leaked() const _GLIBCXX_NOEXCEPT
{ return this->_M_refcount < 0; }
bool
_M_is_shared() const _GLIBCXX_NOEXCEPT
{ return this->_M_refcount > 0; }
void
_M_set_leaked() _GLIBCXX_NOEXCEPT
{ this->_M_refcount = -1; }
void
_M_set_sharable() _GLIBCXX_NOEXCEPT
{ this->_M_refcount = 0; }
void
_M_set_length_and_sharable(size_type __n) _GLIBCXX_NOEXCEPT
{
#if _GLIBCXX_FULLY_DYNAMIC_STRING == 0
if (__builtin_expect(this != &_S_empty_rep(), false))
#endif
{
this->_M_set_sharable(); // One reference.
this->_M_length = __n;
traits_type::assign(this->_M_refdata()[__n], _S_terminal);
// grrr. (per 21.3.4)
// You cannot leave those LWG people alone for a second.
}
}
_CharT*
_M_refdata() throw()
{ return reinterpret_cast<_CharT*>(this + 1); }
_CharT*
_M_grab(const _Alloc& __alloc1, const _Alloc& __alloc2)
{
return (!_M_is_leaked() && __alloc1 == __alloc2)
? _M_refcopy() : _M_clone(__alloc1);
}
// Create & Destroy
static _Rep*
_S_create(size_type, size_type, const _Alloc&);
void
_M_dispose(const _Alloc& __a) _GLIBCXX_NOEXCEPT
{
#if _GLIBCXX_FULLY_DYNAMIC_STRING == 0
if (__builtin_expect(this != &_S_empty_rep(), false))
#endif
{
// Be race-detector-friendly. For more info see bits/c++config.
_GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&this->_M_refcount);
if (__gnu_cxx::__exchange_and_add_dispatch(&this->_M_refcount,
-1) <= 0)
{
_GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&this->_M_refcount);
_M_destroy(__a);
}
}
} // XXX MT
void
_M_destroy(const _Alloc&) throw();
_CharT*
_M_refcopy() throw()
{
#if _GLIBCXX_FULLY_DYNAMIC_STRING == 0
if (__builtin_expect(this != &_S_empty_rep(), false))
#endif
__gnu_cxx::__atomic_add_dispatch(&this->_M_refcount, 1);
return _M_refdata();
} // XXX MT
_CharT*
_M_clone(const _Alloc&, size_type __res = 0);
};
4.2.3对元素的修改
basic_string.h中从1428行开始就是修改了,一直到1734行,由于有点多,而且自己就能找到,所以就不再贴到这里了,有兴趣的自行查看。里面就有各种修改操作。这里就不局限于erase了。包括insert,pop等操作。
5.参考博客:
https://blog.csdn.net/liuyuan185442111/article/details/45870441?locationnum=3&fps=1
更加具体的解读参考csdn博主专栏STL中string中源码解读:https://blog.csdn.net/abortexit/category_296884.html