C++ String 类

#include <bits/stdc++.h>

using namespace std;

namespace wjl
{
    class string
    {
    public:
        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;
        }

        string(const char *str = "") // '\0'
        /* :_str(str) */ // 不能直接给 - 权限放大
        :_size(strlen(str))
        ,_capacity(_size)
        {
            _str = new char[_capacity + 1];
            strcpy(_str, str);
        }

        void swap(string &s)
        {
            std::swap(_str, s._str);
            std::swap(_size, s._size);
            std::swap(_capacity, s._capacity);
        }

        string(const string &s)
        :_str(nullptr)
        ,_size(0)
        ,_capacity(0) // 初始化列表,把 this 初始化以防 swap 之后出现问题
        {
            // 古法
            /*
            _str = new char[s._capacity + 1];
            strcpy(_str, s._str);

            _size = s._size;
            _capacity = s._capacity;
            */

            // 现代写法
            string tmp(s._str);
            swap(tmp);// 默认 this 调用
        }

        /*
        string()
        :_str(new char[4])
        ,_size(0)
        ,_capacity(0)
        {
            _str[0] = '\0';
            _str[1] = '\0';
            _str[2] = '\0';
            _str[3] = '\0';
        }
        */

        const char *c_str() const
        {
            return _str;
        }

        size_t size() const
        {
            return _size;
        }

        size_t capacity() const
        {
            return _capacity;
        }

        char &operator [](size_t pos)
        {
            assert(pos < _size);

            return _str[pos];
        }

        const char &operator [](size_t pos) const // 只读版本
        {
            assert(pos < _size);

            return _str[pos];
        }

        string &operator +=(char ch)
        {
            push_back(ch);
            return *this;
        }

        string &operator +=(const char *str)
        {
            append(str);
            return *this;
        }

        void reserve(size_t n) // 扩容
        {
            if (n <= _capacity)
            {
                return;
            }

            // 扩容 - 开新空间,拷贝数据,删除旧空间,改变指针指向
            char *tmp = new char[n + 1];// 多的那个放 '\0'
            strcpy(tmp, _str);// 会拷贝 '\0'
            delete[] _str;
            _str = tmp;

            _capacity = n;
        }

        void push_back(char ch)
        {
            if (_size == _capacity)
            {
                reserve(_capacity == 0 ? 4 : _capacity * 2);// _capacity 是 0 怎么办?
            }

            _str[_size++] = ch;
            _str[_size] = '\0';
        }

        void append(const char *str)
        {
            size_t len = strlen(str);
            if (_size + len > _capacity)
            {
                reserve(_size + len);
            }

            strcpy(_str + _size, str);
            _size += len;
        }

        void insert(size_t pos, char ch)
        {
            assert(pos <= _size);

            if (_size == _capacity)
            {
                reserve(_capacity == 0 ? 4 : _capacity * 2);
            }

            memmove(_str + pos + 1, _str + pos, _size - pos);
            _str[pos] = ch;
            _size++;
        }

        void insert(size_t pos, const char *str)
        {
            assert(pos <= _size);

            size_t len = strlen(str);
            if (_size + len > _capacity)
            {
                reserve(_size + len);
            }

            memmove(_str + pos + len, _str + pos, _size - pos);
            memcpy(_str + pos, str, len);
            _size += len;
        }

        void erase(size_t pos, size_t len = npos)
        {
            assert(pos < _size);

            if (len == npos || pos + len >= _size)
            {
                len = _size - pos;
            }

            memmove(_str + pos, _str + pos + len, _size - pos - len);
            memset(_str + _size - len, 0, len);
            _size -= len;
        }

        void resize(size_t n, char ch = '\0')
        {
            if (n <= _size)
            {
                _str[n] = '\0';
                _size = n;
            }
            else
            {
                reserve(n);// 不管空间是大于还是小于 capacity,只需要 reserve 就可以了
                while (_size < n)
                {
                    _str[_size++] = ch;
                }
                _str[_size] = '\0';
            }
        }

        size_t find(char ch, size_t pos = 0) // 从 pos 位置开始查找
        {
            for (size_t i = pos; i < _size; i++)
            {
                if (_str[i] == ch)
                {
                    return i;
                }
            }

            return npos;
        }

        size_t find(const char *sub, size_t pos = 0)
        {
            const char *p = strstr(_str + pos, sub);
            if (p)
            {
                return p - _str;// 指针相减
            }
            else
            {
                return npos;
            }
        }

        string substr(size_t pos, size_t len = npos)
        {
            assert(pos < _size);

            string s;
            size_t end = pos + len;
            if (len == npos || pos + len >= _size)
            {
                len = _size - pos;
                end = _size;
            }

            s.reserve(len);
            for (size_t i = pos; i < end; i++)
            {
                s += _str[i];
            }

            return s;// 注意浅拷贝:_str 地址不变,返回时被析构
        }

        bool operator <(const string &s) const
        {
            return strcmp(_str, s._str) < 0;
        }

        bool operator ==(const string &s) const
        {
            return strcmp(_str, s._str) == 0;
        }

        bool operator <=(const string &s) const
        {
            return *this < s || *this == s;
        }

        bool operator >(const string &s) const
        {
            return !(*this <= s);
        }

        bool operator >=(const string &s) const
        {
            return !(*this < s);
        }

        bool operator !=(const string &s) const
        {
            return !(*this == s);
        }

//        string &operator =(const string &s)
//        {
//            if (this != &s)
//            {
//                // 古法
//                /*
//                char *tmp = new char[s._capacity + 1];
//                strcpy(tmp, s._str);
//                delete[] _str;
//                _str = tmp;
//                _size = s._size;
//                _capacity = s._capacity;
//                */
//
//                // 现代方法
//                string temp(s);
//                swap(temp);
//                // 注意原本的 this 被交换给了 temp,自动析构
//            }
//
//            return *this;
//        }

        // 极致现代写法
        string &operator =(string s)
        {
            swap(s);

            return *this;
        }

        void clear()
        {
            _str[0] = '\0';
            _size = _capacity = 0;
        }

        ~string()
        {
            delete[] _str;
            _str = nullptr;
            _size = _capacity = 0;
        }

    private:
        char *_str;
        size_t _size;
        size_t _capacity;

    public:
        const static size_t npos; /* = -1; */ // 特例:const static 整形 可以在类里面初始化
    };

    ostream &operator <<(ostream &out, const string &s);
    istream &operator >>(istream &in, string &s);

    ostream &operator <<(ostream &out, const string &s) // 流插入
    {
        for (const char &i : s)
        {
            out << i;
        }

        return out;
    }

    istream &operator >>(istream &in, string &s) // 流提取
    {
        s.clear();

        char buffer[129];// 缓冲空间
        size_t i = 0;
        char ch;
        /* in >> ch; */ // 流插入拿不到空格和换行

        begin:
        ch = in.get();
        if (ch == ' ' || ch == '\n')
        {
            goto begin;
        }

        while (ch != ' ' && ch != '\n') // 空格自动结束
        {
            buffer[i++] = ch;
            if (i == 128)
            {
                buffer[i] = '\0';
                s += buffer;
                i = 0;
            }

            ch = in.get();
        }

        if (i != 0)
        {
            buffer[i] = '\0';
            s += buffer;// 减少扩容次数
        }

        return in;
    }
}
const size_t wjl::string::npos = -1;// static 成员要在类外初始化

void func(const string& s) // 传值传参会进行深拷贝,所以一般用 const & 传参
{
    auto it = s.begin();// cbegin 也行

    auto rit = s.rbegin();// crbegin 也行
    while (it != s.end())
    {
        /* *it = 'a'; */ // err - 只读
        cout << *it;
        it++;
    }
    cout << endl;

    while (rit != s.rend())
    {
        cout << *rit;
        rit++;
    }
    cout << endl;
}

void test_string1()
{
    wjl::string s1("hello world");
    cout << s1.c_str() << endl;

    wjl::string s2;
    cout << s2.c_str() << endl;

    for (size_t i = 0; i < s1.size(); i++)
    {
        cout << s1[i];
    }
    cout << endl;

    auto it = s1.begin();
    while (it != s1.end())
    {
        cout << *it;
        ++it;
    }
    cout << endl;

    for (auto &ch : s1) // 底层是迭代器,begin 换成大写都调用不到
    {
        cout << ch;
    }
    cout << endl;
}

void test_string2()
{
    wjl::string s1("hello world");
    cout << s1.c_str() << endl;

    s1.push_back(' ');
    s1.append("hello bit hello bit");
    cout << s1.c_str() << endl;

    s1 += '#';
    s1 += "******************";

    cout << s1.c_str() << endl;
}

void test_string3()
{
    wjl::string s1("hello world");
    cout << s1.c_str() << endl;

    s1.insert(5, '%');
    cout << s1.c_str() << endl;

    s1.insert(s1.size(), '%');
    cout << s1.c_str() << endl;

    s1.insert(0, '%');// 注意类型提升:在多操作数的操作符两端,类型会自动提升至高等级
    cout << s1.c_str() << endl;

    s1.insert(0, "awa");
    cout << s1.c_str() << endl;

    s1.insert(5, "awa");
    cout << s1.c_str() << endl;

    s1.insert(s1.size(), "qwq");
    cout << s1.c_str() << endl;

    s1.erase(0, 3);
    cout << s1.c_str() << endl;

    s1.erase(2);
    cout << s1.c_str() << endl;

    wjl::string s2("awa");
    cin >> s1;
    cin >> s2;
    cout << s1 << endl;
    cout << s2 << endl;
}

void test_string4()
{
    wjl::string s1("hello world");
    cout << s1 << endl;

    s1.resize(5);
    cout << s1 << endl;

    s1.resize(10, 'x');
    cout << s1 << endl;
}

void test_string5()
{
    wjl::string s5("https://legacy.cplusplus.com/reference/string/string/rfind/");
    // 协议,域名,资源名

    wjl::string sub1, sub2, sub3;
    size_t i1 = s5.find(':');
    if (i1 != wjl::string::npos)
    {
        sub1 = s5.substr(0, i1);
    }
    else
    {
        cout << "没有找到 i1" << endl;
    }

    size_t i2 = s5.find('/', i1 + 3);
    if (i2 != wjl::string::npos)
    {
        sub2 = s5.substr(i1 + 3, i2 - (i1 + 3));// 构造 + 赋值,不能优化
        // 左闭右开区间 - i2 - i1 就是字符个数
        // 闭区间 - i2 - i1 + 1

        sub3 = s5.substr(i2 + 1);
    }
    else
    {
        cout << "没有找到 i2" << endl;
    }

    cout << sub1 << endl;
    cout << sub2 << endl;
    cout << sub3 << endl;
}

void test_string6()
{
    wjl::string s1("hello world");
    wjl::string s2 = s1;// 拷贝构造

    cout << s1 << endl;
    cout << s2 << endl;

    wjl::string s3("xxxxxxxxxxx");
    s2 = s3;
    cout << s2 << endl;

    string s4;
    cout << sizeof(s4) << endl;// 8

    string s5("hello world");
    cout << sizeof(s5) << endl;// 8
    // 里面存储指向堆空间的指针,前面的空间包含三个变量 _size _capacity _refcount(引用计数)
    // 后面存储字符串

    string s6(s5);
    cout << (void *)s5.c_str() << endl;// 0xa00022908
    cout << (void *)s6.c_str() << endl;// 0xa00022908

    s6[0]++;
    cout << (void *)s6.c_str() << endl;// 0xa00022938

    // 浅拷贝的问题:
    // 1.析构两次
    // 2.更改一个变量同时会更改其他变量

    // 解决浅拷贝,但是不用深拷贝:引用计数 -> 智能指针
    // 记录有几个对象指向这块空间
    // 拷贝的时候 ++ 引用计数,析构的时候 -- 引用计数
    // 引用计数减到 0 时,说明是最后一个对象,再析构

    // 一个修改数据会影响另一个 - 写时拷贝(延迟拷贝)
    // 在写入的时候再拷贝
    // 检测引用计数,如果不为 1,就要深拷贝,并且更新原来的引用计数,创建新的引用计数
}

int main()
{
    {
        // string 基本用法
        // string 是模板类 basic_string 的 char 类型的显式实例化

        string s1;// 无参构造
        string s2("hello");// 带参构造

        // 重载了流插入和流提取 - 按需分配空间(自动扩容),缓解了 C 的不足
        cin >> s1;
        cout << s1 << endl;
        cout << s2 << endl;

        // cin >> s2;
        // cout << s2 << endl;

        // 字符串拼接
        string ret1 = s1 + s2;
        cout << ret1 << endl;

        string ret2 = s1 + "我来了";
        cout << ret2 << endl;

        cout << s1 + s1 << endl;

        // string 构造
        string s3("hello world");
        string s4 = "hello world";// 构造 + 拷贝构造 + 优化

        // 体验:遍历 string
        // 方法 1
        for (size_t i = 0; i < s3.size(); i++)
        {
            // 读
            cout << s3[i];
        }
        cout << endl;

        for (size_t i = 0; i < s3.size(); i++)
        {
            // 写
            s3[i]++;
            cout << s3[i];
            s3[i]--;
        }
        cout << endl;

        // 优化版本(范围 for) - 自动判断结束,自动 ++
        for (const auto &item: s3) // 底层是迭代器
        {
            cout << item;
        }
        cout << endl;

        // 方法 2:迭代器
        // 迭代器:像指针一样的东西
        string::iterator it = s3.begin();// begin - 开始位置的指针
        // string 类里的 _size 不包括 '\0' - 有效字符
        // end 指向最后一个有效字符的下一个位置 - '\0'
        while (it != s3.end()) // 不要用 '<' -
        {
            // 读
            cout << *it;
            ++it;
        }
        cout << endl;

        it = s3.begin();
        while (it != s3.end())
        {
            // 写
            *it = 'a';
            ++it;
        }
        cout << s3 << endl;

        // 迭代器的优势:通用性 - 可以用于任何结构,并且代码结构很相似(访问方式相同)
        // 原因 - 运算符重载
        // list 不支持 [] 访问
        list<int> lt;
        lt.push_back(1);
        lt.push_back(2);
        lt.push_back(3);
        lt.push_back(4);
        auto lit = lt.begin();
        while (lit != lt.end()) // 不能使用 '<' - 空间不连续,后面空间的地址不一定比前面大
        {
            cout << *lit << " ";
            ++lit;
        }
        cout << endl;

        // 反向迭代器
        string s5 = "hello world";
        auto rit = s5.rbegin();
        while (rit != s5.rend())
        {
            cout << *rit;
            ++rit;// 还是 ++
        }
        cout << endl;

        // 常量迭代器
        func(s5);

        // 子串的拷贝构造
        string s6(s5, 6, 5);
        cout << s6 << endl;

        string s7(s5, 6);// 缺省参数,如果不给值,为 -1
        // npos 是 size_t 类型,所以是 4G 字节(2 ^ 32)
        // 如果字符串太短,就会取到字符串结束 - 有多少取多少
        cout << s7 << endl;

        string s8(10, 'a');// 10 个 a 初始化
        string s9(++s5.begin(), --s5.end());// 迭代器区间初始化

        // 赋值运算符重载
        s8 = s7;
        s8 = "xxx";
        s8 = 'y';

        // size 和 length
        string s10 = "hello world";
        cout << s10.size() << s10.length() << endl;// 不包括 '\0'
        // size 具有通用性,但是 length 没有,因为 string 是早期 C++ 设计的产物,所以先用 length,之后加上了 size

        // clear - 清除数据,但是不释放空间
        cout << s10.capacity() << endl;
        s10.clear();
        cout << s10.capacity() << endl;
        // capacity - 容量

        // empty - 判空

        // max_size() - 最大可开辟空间,没什么用,只有理论意义

        // c_str - 将字符串转换成字符数组,返回常量字符指针
    }

    {
        // string 的扩容
        string s1;
        size_t old = s1.capacity();
        cout << "开始容量:" << old << endl;
        for (size_t i = 0; i < 100; i++) {
            s1.push_back('x');
            if (s1.capacity() != old) {
                cout << "扩容:" << s1.capacity() << endl;
                old = s1.capacity();
            }
        }
        // 0 - 1 - 2 - 4 - 8 - 16 - 32 - 64 - 128

        // reserve - 重新分配空间
        string s2;
        s2.reserve(100);
        cout << "开始容量:" << s2.capacity() << endl;
        // 有可能只给 100,也有可能给一个大于 100 的数
        // 价值:体现在已知大概需要多少空间,提前开好空间 - 减少扩容次数,提高效率

        s2.reserve(10);
        cout << "更改容量:" << s2.capacity() << endl;// 没有缩小,还是 100
        // 标准没有规定不能缩小空间,当写出收缩的指令的时候,编译器会把它当做一个不具有约束力的请求

        // resize - 改变长度
        // void resize(size_t n)
        // void resize(size_t n, char c)
        // 如果不给第二个参数,那么多出来的 size 用 '\0' 填充

        // 假设 size = 7,capacity = 10
        // 有三种情况:
        // 1.更改后的 size > 10
        // 2.更改后的 size 在 7 - 10 之间
        // 3.更改后的 size < 7

        string s3 = "hello world";
        cout << s3.size() << endl;// 11
        cout << s3.capacity() << endl;// 11 - 编译器优化:知道你要开多少字节空间,直接开好

        s3.resize(13);
        cout << s3.size() << endl;// 13
        cout << s3.capacity() << endl;// 22 - 两倍,自动扩容

        s3.resize(15, 'x');// 会把加上的空间赋值成 'x'
        cout << s3 << endl;
        cout << s3.size() << endl;
        cout << s3.capacity() << endl;

        s3.resize(5);// 删除 - 保留前 n 个字符
        cout << s3 << endl;
        cout << s3.size() << endl;// 5
        cout << s3.capacity() << endl;// 22 - 不变

        // 用途:初始化(?)但是好像构造函数也能直接初始化
    }

    {
        // at - []
        string s = "hello world";
        cout << s.at(1) << endl;// 越界 - 抛出异常
    }

    {
        // push_back() - 尾插
        string s;
        s.push_back('#');

        // append() - 尾插字符串/字符
        s.append("hello");
        string ss(" world");
        s.append(ss);
        cout << s << endl;

        // += - 运算符重载
        s += '#';
        s += "hello";
        s += ss;
        cout << s << endl;

        // + (全局函数,非成员函数)
        string ret = ss + "hello";
        cout << ret << endl;
        // 少用 +,因为代价很大
    }

    {
        // assign - 赋值
        string str = "xxx";
        string base = "hello world";

        str.assign(base);
        cout << str << endl;// 会把原来的内容清除

        str.assign(base, 5, 5);
        cout << str << endl;
    }

    {
        // insert - 在 pos 位置之前插入

        string str = "hello world";
        str.insert(0, 1, 'x');// n 是字符个数
        str.insert(str.begin(), 'x');
        cout << str << endl;
    }

    {
        // erase
        // string &erase(size_t pos = 0, size_t len = npos);
        // 把第 pos 个位置之后的 len 个字符删除
        string s = "hello world";
        s.erase(5, 6);
        cout << s << endl;
    }

    {
        // replace - 替换
        // string &replace(size_t pos, size_t len, const string &str);
        // pos 位置开始的 len 个字符替换成 str

        string s = "hello world";
        s.replace(5, 1, "%%20");// 也可以替换成字符
        cout << s << endl;
    }
    // insert,erase,replace 能不用就尽量不用,因为需要挪动数据,效率不高
    // 接口设计复杂,需要时查一下文档

    {
        // 空格替换为 %20
        string s = "The quick brown fox jumps over a lazy dog";

        // 1.find + replace
        // 2.swap
        // 3.以空间换时间 - 再开一个
        string s1;
        for (const auto &item: s)
        {
            if (item != ' ')
            {
                s1 += item;
            }
            else
            {
                s1 += "20%";
            }
        }
        // s = s1;
        // s.assign(s1);

        /* swap(s, s1); */ // 虽然可以,但是效率低 - 一次拷贝构造,两次深拷贝
        // 但是库里提供了一个 string 类型的参数 swap,避免调用到模板类

        printf("s:%p\n", s.c_str());// s:0xa00012828
        printf("s1:%p\n", s1.c_str());// s1:0xa00022958

        s.swap(s1);// 只是交换成员函数,效率高很多

        printf("s:%p\n", s.c_str());// s:0xa00022958
        printf("s1:%p\n", s1.c_str());// s1:0xa00012828

        cout << s << endl;

        // 尾删 pop_back
    }

    {
        // shrink_to_fit - 缩容
        // capacity 减少到 size
    }

    {
        // c_str 和 data - 把 string 类里面的 _str 拿出来
        // 类似于 size 和 length
    }

    {
        // copy - 复制
        // 用处不大
    }

    {
        // find 系列
        // size_t find(const string &str, size_t pos = 0) const;
        //             const char *s, size_t pos = 0
        //             const char *s, size_t pos, size_t n
        //             char c, size_t pos = 0

        // 读取文件的后缀
        string s1("test.c");
        string s2("test.cpp.tar.zip");

        size_t i = s1.find('.');// 4

        // substr - 取子串
        // string substr(size_t pos = 0, size_t len = npos) const;
        // 从 pos 位置(包括 pos)开始取 len 个字符

        string s3 = s1.substr(i);
        string s4 = s2.substr(s2.rfind('.'));// 从后面开始找
        cout << s3 << endl;
        cout << s4 << endl;

        string s5("https://legacy.cplusplus.com/reference/string/string/rfind/");
        // 协议,域名,资源名

        string sub1, sub2, sub3;
        size_t i1 = s5.find(':');
        if (i1 != string::npos)
        {
            sub1 = s5.substr(0, i1);
        }
        else
        {
            cout << "没有找到 i1" << endl;
        }

        size_t i2 = s5.find('/', i1 + 3);
        if (i2 != string::npos)
        {
            sub2 = s5.substr(i1 + 3, i2 - (i1 + 3));
            // 左闭右开区间 - i2 - i1 就是字符个数
            // 闭区间 - i2 - i1 + 1

            sub3 = s5.substr(i2 + 1);
        }
        else
        {
            cout << "没有找到 i2" << endl;
        }

        cout << sub1 << endl;
        cout << sub2 << endl;
        cout << sub3 << endl;

        // find_first_of
        // size_t find_first_of(const string &str, size_t pos = 0)
        // 查找主串中的字符是否匹配 str 里面的每个字符,如果有一个匹配,就返回位置下标

        string str ("Please, replace the vowels in this sentence by asterisks.");
        size_t found = str.find_first_of("aeiou");
        while (found != string::npos)
        {
            str[found] = '*';
            found = str.find_first_of("aeiou", found + 1);
        }
        cout << str << '\n';

        // find_last_of 从后往前找
        // find_first_not_of - 找主串中第一个不匹配 str 里的任意字符,返回其下标

        string st("Please, replace the vowels in this sentence by asterisks.");
        size_t fond = st.find_first_not_of("aeiou");
        while (fond != string::npos)
        {
            st[fond] = '*';
            fond = st.find_first_not_of("aeiou", fond + 1);
        }
        cout << st << '\n';

    }

    {
        // getline - 获取一行
        // 输入带有空格的文本时,cin 不能解决问题

        string s;
        getline(cin, s);
        cout << s << endl;

        getline(cin, s, '!');// 自己定义结束符,直到 delim 字符才会结束
        cout << s << endl;
    }

    {
        // 字符编码介绍

        // ASCII 码不足以表示汉字 - Unicode 万国码
        // Unicode 编码分为 UTF-8  UTF-16  UTF-32
        // UTF-8 是变长的 - 不同字符使用的空间可能不同 - 1 ~ 4 个字节
        // UTF-16 和 UTF-32 把所有字符统一用 16 / 32 比特位存储

        char str1[] = "hello world";
        char str2[] = "比特 hello";

        cout << sizeof(str1) << endl;// 12
        cout << sizeof(str2) << endl;// 13 - 每个汉字占三个字节

        wchar_t ch1;// wide char - 两个字节的字符
        cout << sizeof(ch1) << endl;

        char16_t ch2;// 2 字节 - 对应 UTF-16
        char32_t ch3;// 4 字节 - 对应 UTF-32
        cout << sizeof(ch2) << endl;
        cout << sizeof(ch3) << endl;

        string s;// UTF-8
        u16string us1;// UTF-16
        u32string us2;// UTF-32
        // 上面三个 class 的区别就是模板参数不同 - char,char16_t,char32_t

        // 乱码 - 写入字符的编码和读取字符的编码不一样

        // GBK - 国标 扩展,国家规定标准编码表
        str2[2]++;
        cout << str2 << endl;
        str2[2]++;
        cout << str2 << endl;
        str2[2]++;
        cout << str2 << endl;
        str2[2]++;
        cout << str2 << endl;

        str2[5]--;
        cout << str2 << endl;
        str2[5]--;
        cout << str2 << endl;
        str2[5]--;
        cout << str2 << endl;
        str2[5]--;
        cout << str2 << endl;
    }

    {
        // string 模拟实现
        test_string1();
        test_string2();
        test_string3();
        test_string4();
        test_string5();
        test_string6();
    }

    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值