C++ 基础: std::string

一、常见的使用

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);
        }
    }

你可能感兴趣的:(c++,开发语言)