std::string是C++标准库中的一个类,用于表示可变长度的字符串。它是由字符类型(通常是char)的数组实现的,并提供了许多字符串操作函数。
std::string的构造函数有多种形式,可以用于初始化空字符串、从字符数组或另一个字符串中复制字符串、从指定长度的字符数组或另一个字符串中截取字符串等。例如:
// 初始化空字符串
std::string str1;
// 从字符数组中复制字符串
char chars[] = "Hello, world!";
std::string str2(chars);
// 从另一个字符串中复制字符串
std::string str3(str2);
// 截取另一个字符串中的一部分
std::string str4(str2, 7, 5);
std::string重载了许多运算符,使得字符串的处理更加方便。例如,可以使用"+“运算符将两个字符串连接起来,也可以使用”==“、”<"等运算符比较两个字符串是否相等或大小关系。
std::string还提供了许多成员函数,用于操作字符串。以下是一些常用的成员函数:
length() 和 size(): 返回字符串的长度。
clear(): 清空字符串。
empty(): 如果字符串为空,则返回true。
append(): 在字符串末尾添加字符串或字符。
substr(): 返回从字符串中的指定位置开始的指定长度的子字符串。
replace(): 用另一个字符串或字符替换字符串中的一段子字符串。
find(): 查找字符串中第一个出现指定子字符串的位置。
rfind(): 查找字符串中最后一个出现指定子字符串的位置。
compare(): 将字符串与另一个字符串或字符进行比较。
除了这些成员函数外,std::string还可以像普通字符数组一样直接操作,例如使用下标访问字符串中的字符。此外,它还支持使用迭代器来遍历字符串中的字符。
需要注意的是,在使用std::string时,通常应该使用std::string::size_type类型来表示字符串的长度,而不是使用int类型。这是因为std::string可能包含大量数据,超出了int类型的表示范围。
总之,std::string提供了方便的字符串操作方法,是C++中常用的字符串类型。
字符串初始化:
std::string str; // 创建一个空字符串
std::string str("hello"); // 创建一个包含 "hello" 的字符串
std::string str = "world"; // 同上
字符串连接:
std::string str1 = "hello";
std::string str2 = "world";
std::string str3 = str1 + ", " + str2; // str3 等于 "hello, world"
字符串比较:
std::string str1 = "apple";
std::string str2 = "banana";
if (str1 == str2) {
// 相等
} else if (str1 < str2) {
// str1 小于 str2
} else {
// str1 大于 str2
}
获取字符串长度:
std::string str = "hello, world";
int len = str.length(); // len 等于 12
获取子字符串:
std::string str = "hello, world";
std::string sub_str = str.substr(0, 5); // sub_str 等于 "hello"
查找子字符串:
std::string str = "hello, world";
size_t pos = str.find("world"); // pos 等于 7
using string = basic_string<char, char_traits<char>, allocator<char>>;
using wstring = basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t>>;
string 其实是basic_string 类型的模板特化。
template <class _Elem, class _Traits = char_traits<_Elem>, class _Alloc = allocator<_Elem>>
class basic_string { // null-terminated transparent array of elements
private:
friend _Tidy_deallocate_guard<basic_string>;
friend basic_stringbuf<_Elem, _Traits, _Alloc>;
using _Alty = _Rebind_alloc_t<_Alloc, _Elem>;
using _Alty_traits = allocator_traits<_Alty>;
using _Scary_val = _String_val<conditional_t<_Is_simple_alloc_v<_Alty>, _Simple_types<_Elem>,
_String_iter_types<_Elem, typename _Alty_traits::size_type, typename _Alty_traits::difference_type,
typename _Alty_traits::pointer, typename _Alty_traits::const_pointer, _Elem&, const _Elem&>>>;
static_assert(!_ENFORCE_MATCHING_ALLOCATORS || is_same_v<_Elem, typename _Alloc::value_type>,
_MISMATCHED_ALLOCATOR_MESSAGE("basic_string" , "T"));
static_assert(is_same_v<_Elem, typename _Traits::char_type>,
"N4910 23.4.3.2 [string.require]/3 requires that the supplied "
"char_traits character type match the string's character type.");
static_assert(!is_array_v<_Elem> && is_trivial_v<_Elem> && is_standard_layout_v<_Elem>,
"The character type of basic_string must be a non-array trivial standard-layout type. See N4910 "
"23.1 [strings.general]/1.");
public:
using traits_type = _Traits;
using allocator_type = _Alloc;
using value_type = _Elem;
using size_type = typename _Alty_traits::size_type;
using difference_type = typename _Alty_traits::difference_type;
using pointer = typename _Alty_traits::pointer;
using const_pointer = typename _Alty_traits::const_pointer;
using reference = value_type&;
using const_reference = const value_type&;
using iterator = _String_iterator<_Scary_val>;
using const_iterator = _String_const_iterator<_Scary_val>;
using reverse_iterator = _STD reverse_iterator<iterator>;
using const_reverse_iterator = _STD reverse_iterator<const_iterator>;
private:
static constexpr auto _BUF_SIZE = _Scary_val::_BUF_SIZE;
static constexpr auto _ALLOC_MASK = _Scary_val::_ALLOC_MASK;
// When doing _String_val operations by memcpy, we are touching:
// _String_val::_Bx::_Buf (type is array of _Elem)
// _String_val::_Bx::_Ptr (type is pointer)
// _String_val::_Mysize (type is size_type)
// _String_val::_Myres (type is size_type)
// N4910 23.1 [strings.general]/1 says _Elem must be trivial standard-layout, so memcpy is safe.
// We need to ask if pointer is safe to memcpy.
// size_type must be an unsigned integral type so memcpy is safe.
// We also need to disable memcpy if the user has supplied _Traits, since
// they can observe traits::assign and similar.
static constexpr bool _Can_memcpy_val = _Is_specialization_v<_Traits, char_traits> && is_trivial_v<pointer>;
// This offset skips over the _Container_base members, if any
static constexpr size_t _Memcpy_val_offset = _Size_after_ebco_v<_Container_base>;
static constexpr size_t _Memcpy_val_size = sizeof(_Scary_val) - _Memcpy_val_offset;
template <class _Iter>
// TRANSITION, /clr:pure is incompatible with templated static constexpr data members
// static constexpr bool _Is_elem_cptr =_Is_any_of_v<_Iter, const _Elem* const, _Elem* const, const _Elem*, _Elem*>;
using _Is_elem_cptr = bool_constant<_Is_any_of_v<_Iter, const _Elem* const, _Elem* const, const _Elem*, _Elem*>>;
#if _HAS_CXX17
template <class _StringViewIsh>
using _Is_string_view_ish =
enable_if_t<conjunction_v<is_convertible<const _StringViewIsh&, basic_string_view<_Elem, _Traits>>,
negation<is_convertible<const _StringViewIsh&, const _Elem*>>>,
int>;
#endif // _HAS_CXX17
#ifdef _INSERT_STRING_ANNOTATION
_CONSTEXPR20 void _Create_annotation() const noexcept {
// Annotates the valid range with shadow memory
auto& _My_data = _Mypair._Myval2;
_Apply_annotation(_My_data._Myptr(), _My_data._Myres, _My_data._Myres, _My_data._Mysize);
}
_CONSTEXPR20 void _Remove_annotation() const noexcept {
// Removes annotation of the range with shadow memory
auto& _My_data = _Mypair._Myval2;
_Apply_annotation(_My_data._Myptr(), _My_data._Myres, _My_data._Mysize, _My_data._Myres);
}
_CONSTEXPR20 void _Modify_annotation(const difference_type _Count) const noexcept {
// Extends/shrinks the annotated range by _Count
if (_Count == 0) {
return;
}
auto& _My_data = _Mypair._Myval2;
_Apply_annotation(
_My_data._Myptr(), _My_data._Myres, _My_data._Mysize, static_cast<size_type>(_My_data._Mysize + _Count));
}
_NODISCARD static const void* _Get_aligned_first(const void* _First, const size_type _Capacity) noexcept {
const char* _CFirst = reinterpret_cast<const char*>(_First);
if (_Capacity >= _Asan_granularity) { // We are guaranteed to have sufficient space to find an aligned address
return reinterpret_cast<const void*>(
(reinterpret_cast<uintptr_t>(_CFirst) + (_Asan_granularity - 1)) & ~(_Asan_granularity - 1));
}
uintptr_t _Alignment_offset = reinterpret_cast<uintptr_t>(_CFirst) & (_Asan_granularity - 1);
if (_Alignment_offset != 0) {
_Alignment_offset = _Asan_granularity - _Alignment_offset;
}
if (_Capacity > _Alignment_offset) {
return _CFirst + _Alignment_offset;
}
return nullptr;
}
static _CONSTEXPR20 void _Apply_annotation(const value_type* _Ptr, const size_type _Capacity,
const size_type _Old_size, const size_type _New_size) noexcept {
#if _HAS_CXX20
if (_STD is_constant_evaluated()) {
return;
}
#endif // _HAS_CXX20
if (!_Asan_string_should_annotate) {
return;
}
// We need to check whether we have a misaligned SSO buffer because of the proxy in `_Container_base` (e.g. x86)
if constexpr (_Memcpy_val_offset % _Asan_granularity != 0) {
const uintptr_t _Alignment_offset = reinterpret_cast<uintptr_t>(_Ptr) & (_Asan_granularity - 1);
if (_Alignment_offset != 0 && _Capacity == _BUF_SIZE - 1) {
return;
}
}
// Needs to consider the null terminator
const char* _First = reinterpret_cast<const char*>(_Ptr);
const char* _End = reinterpret_cast<const char*>(_Ptr + _Capacity + 1);
const char* _Old_last = reinterpret_cast<const char*>(_Ptr + _Old_size + 1);
const char* _New_last = reinterpret_cast<const char*>(_Ptr + _New_size + 1);
if constexpr (_Has_minimum_allocation_alignment_string<basic_string>) {
__sanitizer_annotate_contiguous_container(_First, _End, _Old_last, _New_last);
} else {
const void* _Aligned_first = _Get_aligned_first(_First, _Capacity + 1);
if (!_Aligned_first) {
// There is no aligned address within the underlying buffer. Nothing to do
return;
}
const void* _Aligned_old_last = _Old_last < _Aligned_first ? _Aligned_first : _Old_last;
const void* _Aligned_new_last = _New_last < _Aligned_first ? _Aligned_first : _New_last;
const void* _Aligned_end = _End < _Aligned_first ? _Aligned_first : _End;
__sanitizer_annotate_contiguous_container(
_Aligned_first, _Aligned_end, _Aligned_old_last, _Aligned_new_last);
}
}