STL容器之string类

news2024/9/25 23:18:46

文章目录

  • STL容器之string类
    • 1、 什么是STL
    • 2、STL的六大组件
    • 3、string类
      • 3.1、string类介绍
      • 3.2、string类的常用接口说明
        • 3.2.1、string类对象的常见构造
        • 3.2.2、string类对象的容量操作
        • 3.2.3、string类对象的访问及遍历操作
        • 3.2.4、 string类对象的修改操作
        • 3.2.5、 string类非成员函数
      • 3.3、 vs和g++下string结构的说明
    • 4、string类的模拟实现

img

STL容器之string类

1、 什么是STL

C++标准模板库(Standard Template Library,STL)是C++标准库的一部分,提供了一组通用的模板类和函数,以实现常见的数据结构和算法


2、STL的六大组件

STL包含了多个模块,其中一些主要的包括:

  1. 容器(Containers)

    • string:它被设计为一个动态数组,提供了一系列成员函数来操作字符串,同时自动处理内存的分配和释放。

    • vector: 动态数组,可动态调整大小。

    • list: 双向链表。

    • deque: 双端队列,支持在两端高效地插入和删除元素。

    • queue: 队列。

    • stack: 栈。

    • set: 集合,不允许重复元素。

    • map: 映射,键-值对的关联容器。

    • unordered_set 和 unordered_map: 使用哈希表实现的集合和映射。

  2. 算法(Algorithms)

  • 提供了许多常见的算法,如排序、搜索、遍历等。
  • 包括了泛型算法,例如 sortfindbinary_search 等。
  1. 迭代器(Iterators)

    • 提供了多种迭代器类型,用于遍历不同类型的数据结构。
  2. 函数对象(Function Objects)也称仿函数(functor)

  • 包括了一些预定义的函数对象,如比较器等。
  1. 适配器(Adapters)

    • 提供了用于修改容器接口的适配器,如 stackqueue priority_queue(优先队列,基于堆实现)。
  2. 空间配置器(allocator)

  • std::allocator: 是STL中默认的空间配置器。它使用 newdelete 运算符来进行内存分配和释放。

3、string类

3.1、string类介绍

string类的文档介绍

  1. 字符串是表示字符序列的类
  2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符/字符串的设计特性。
  3. string类是使用char(即作为它的字符类型,使用它的默认char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
  4. string类是basic_string模板类的一个实例,它使用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默认参数(根于更多的模板信息请参考basic_string)。
  5. 注意:这个类独立于所使用的编码来处理字节:如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

总结

  1. string是表示字符串的字符串类
  2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作
  3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string
  4. 不能操作多字节或者变长字符的序列

注意在使用string类时,必须包含#include头文件(#include <string>)以及using namespace std;

3.2、string类的常用接口说明

string类对象其实就是字符串,但是字符串不一定是string类对象

3.2.1、string类对象的常见构造
(constructor)函数名称功能说明
string()(重点)构造空的string类对象,即空字符串
string (const string& str)(重点)拷贝构造函数
string (const string& str, size_t pos, size_t len = npos)构造从string类对象str的pos位置向后len长度的string类对象
string (const char s)(重点)*用C-string(字符串,不是字符串对象)来构造string类对象
string (const char s, size_t n)*构造字符串s从开始位置到n位置的string类对象
string (size_t n, char c)构造n个连续字符c的string类对象
int main() {
    //构造函数/拷贝构造函数
    string s1;
    cout << s1 << endl;

    string s2("hello");
    cout << s2 << endl;

    string s3(s2);
    cout << s3 << endl;

    string s4(s3, 2, 10);
    cout << s4 << endl;

    string s5("world", 2);
    cout << s5 << endl;

    return 0;
}
3.2.2、string类对象的容量操作
函数名称功能说明
size(重点)返回string类对象有效字符长度
length返回string类对象有效字符长度
max_size返回string类对象可以达到的最大字符长度
resize(重点)将有效字符的个数改成n个,多出的空间用字符c填充(如果有参数字符c)
capacity返回当前已经分配给string类对象的存储空间大小,单位是字节
reserve(重点)要求string类对象的容量调整为计划好的容量(参数为n),如果n小于之前的容量,则不调整,大于则调整为n(看编译器,有些编译器可能大于n)
clear(重点)清空string类对象的内容,使其变成一个空字符串
empty(重点)判断string类对象是不是空
int main() {
    string s1("hello world a");

    cout << s1.capacity() << endl;//返回当前已经分配给字符串的存储空间大小,单位是字节

    cout << s1.size() << endl;//返回字符串有效字符长度
    cout << s1.length() << endl;//返回字符串有效字符长度
    cout << s1.max_size() << endl;//返回字符串可以达到的最大字符长度

    s1.resize(25, 'x');//将有效字符的个数该成n个,多出的空间用字符c填充(如果有参数字符c)
    cout << s1.size() << endl;
    cout << s1.capacity() << endl;

    s1.reserve(33);//要求字符串的容量调整为计划好的容量(参数为n),如果n小于之前的容量,则不调整,大于则调整为n(看编译器,有些编译器可能大于n)
  	cout << s1 << endl;
    cout << s1.capacity() << endl;

    s1.clear();//清空字符串的内容,使其变成一个空字符串
    cout << s1.empty() << endl;//判断字符串是不是空

    return 0;
}

注意

  1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()
  2. clear()只是将string中有效字符清空,不改变底层空间大小
  3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变既影响容量,也影响数据
  4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小只影响容量,不影响数据
3.2.3、string类对象的访问及遍历操作
函数名称功能说明
operator[](重点)返回string类对象在pos位置的字符引用,分const和非const,const则字符串内容(字符)不能被修改
at和opreator功能一样
back取string类对象象的末尾字符
front取string类对象的最前面字符
begin返回一个迭代器指向string类对象最前面的字符,分const和非const,const是使得this指向的内容不能改变
end返回一个迭代器指向string类对象最后面的字符的后面一个位置,和begin一样,分const和非const
rbegin反向迭代器,和begin差不多,就是这是指向string类对象最末尾字符位置
rend反向迭代器,和begin差不多,就是这是指向string类对象最前面字符的前一个位置
cbegin和begin差不多,但是这是专门用于const迭代器的
crbegin和cbegin差不多,就是这是指向string类对象最末尾字符位置
范围forC++11支持更简洁的范围for的新遍历方式
int main() {
   const string str1("hello ynu");
   string str2("hello ynu");

//    str1[1] = '1';//常量string对象,这里调用的是const char& operator[] (size_t pos) const;
   str2[1] = 'n';
   str2.at(2) = 'y';

   char ch1 = str2.back();
   char ch2 = str2.front();

   string::iterator it2 = str2.begin();
   while (it2 != str2.end()) {
       cout << *it2;
       fflush(stdout);//将缓冲区数据刷新到终端
       it2++;
   }
   cout << endl;

   //const迭代器
   string::const_iterator it1 = str1.begin();
   while (it1 != str1.end()) {
       cout << *it1;
       it1++;
   }
   cout << endl;

   //反向迭代器
   string::reverse_iterator it3 = str2.rbegin();
   while (it3 != str2.rend()) {
       cout << *it3;
       it3++;
   }
   cout << endl;

   //专属/const迭代器
   string::const_iterator it = str1.cbegin();
   while (it != str1.end()) {
       cout << *it;
       it++;
   }
   cout << endl;

   //范围for
   for (auto e: str2) {
       cout << e;
   }
   cout << endl;
   return 0;
}

3.2.4、 string类对象的修改操作
函数名称功能说明
operator+=(重点)在string类对象末尾追加字符、字符串或者string类对象
append在string类对象末尾追加字符(参数需要加上添加字符的个数n)、字符串或者string类对象
push_back在string类对象末尾增加一个字符
npos(重点)大小为无符号数的最大值,用int表示即-1
insert在string类对象pos位置之前插入字符(参数需要加上添加字符的个数n)、字符串或者string类对象
erase从string类对象pos位置开始删除长度为len的字符串,若不指定len,则len为npos(最大无符号int值)
replace从string类对象pos位置开始向后长度为len的字符串替换为新字符(参数需要加上添加字符的个数n)、新字符串或者新string类对象
swap用一个新string类对象交换本string类对象的内容。注意这里是引用,可以减少对象拷贝
c_str(重点)返回C格式字符串,这个比data常用
data和c_str一样
copy将string类对象从pos位置开始向后的n个字符复制到新字符数组里,并返回这个复制的字符串长度
findstring类对象从pos位置开始向后查找字符、字符串或者string类对象,返回第一次出现的起始字符位置。pos不指定,则从string类对象的第一个字符开始查找。没找到则返回npos
rfind和find差不多,这是pos位置从后往前找,找到返回返回第一次出现的起始字符位置。没找到返回npos
find_first_of在string类对象中找到字符、字符串和string类对象里的匹配的任意字符的第一个位置
substr返回一个string类对象,这个string类对象是原string类对象从pos位置开始向后len长度的截取的字符串副本
int main() {

    string s1("hello world");
    string s2("no no no");

    //operator+=
    s2 += "hh";
    s2 += 'x';
    s2 += s1;

    //append
    s1.append(1, 'x');
    s1.append("hh");
    s1.append(s2);

    //push_back
    s2.push_back('x');

    //insert
    s1.insert(0, "xx");
    s1.insert(0, 2, 'x');


    //erase
    s1.erase(0, 4);

    //replace
    s1.replace(0, 4, "x");

    //swap
    s1.swap(s2);

    //c_str
    const char *str = s1.c_str();

    //data
    const char *str1 = s1.data();

    string s3("hello world");

    //copy
    char buffer[20] = {0};
    size_t length = s3.copy(buffer, 8, 0);
    buffer[length] = '\0';
    cout << buffer << endl;

    //find
    size_t found1 = s3.find('d', 3);
    cout << found1 << endl;

    size_t found2 = s3.find('f');
    //测试是不是用int表示的-1
    int test = found2;
    cout << found2 << endl;
    cout << test << endl;

    //rfind
    size_t found3 = s3.rfind("ll", 5);
    cout << found3 << endl;

    string s4("hello gun qnd world haha no --skas wcl cb ");

    //find_first_of
    size_t found4 = s4.find_first_of("gunwcqnd", 0);

    while (found4 != std::string::npos) {
        s4[found4] = '*';
        found4 = s4.find_first_of("gunwcqnd", found4 + 1);
    }
    cout << s4 << endl;

    //substr
    string sub_str = s4.substr(0, 9);
    cout << sub_str << endl;
    
    return 0;
}

注意

  1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。

  2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

3.2.5、 string类非成员函数
函数名称功能说明
operator+返回一个string类对象,这是string类对象是(字符、字符串、string类对象)的其中两个的串联
relational operators(重点)包含操作符==、!=、<、<=、>、>=,返回值都是bool类型
swap交换两个string类对象的值。这是非成员函数,还有一个成员函数swap
operator>>(重点)从输入流中提取字符串,并存储到string类对象str中,str中之前到数据将会被覆盖。即输入运算符重载
operator<<(重点)将string类对象str的字符序列插入到输出流中。即即输出运算符重载
getline(重点)获取一行字符串。弥补了operator>>的缺陷(遇到第一个空格就只读到第一个空格之前的字符串)
int main() {

    //operator+
    string s1("hello");
    string s2("world");

    string s3 = s1 + s2;
    cout << s3 << endl;
    string s4 = s1 + 'x';
    cout << s4 << endl;

    //operator==
    bool b = s1 == s2;
    cout << b << endl;

    //swap
    cout << s1 << endl;
    cout << s2 << endl;
    swap(s1, s2);
    cout << s1 << endl;
    cout << s2 << endl;

    //operator>>
    cin >> s1;
    cout << s1 << endl;

    //operator<<
    cout << s2 << endl;

    string s5("hello ynu");
    //getline
    getline(cin, s5);
    cout << s5 << endl;

    return 0;
}

3.3、 vs和g++下string结构的说明

注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节。

  • vs下string的结构
    string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字符串的存储空间:

    • 当字符串长度小于16时,使用内部固定的字符数组来存放

    • 当字符串长度大于等于16时,从堆上开辟空间

      union _Bxty
      { // storage for small buffer or pointer to larger one
       value_type _Buf[_BUF_SIZE];
       pointer _Ptr;
       char _Alias[_BUF_SIZE]; // to permit aliasing
      } _Bx;
      

      这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。

      其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量。

      最后:还有一个指针做一些其他事情。

      故总共占16+4+4+4=28个字节。

  • g++下string的结构
    g++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指针将来指向一块堆空间,内部包含了如下字段:

    • 空间总大小

    • 字符串有效长度

    • 引用计数

      struct _Rep_base
      {
         size_type _M_length;
         size_type _M_capacity;
         _Atomic_word _M_refcount;
      };
      

      指向堆空间的指针,用来存储字符串。

  • CLion下string的结构

    CLion下string对象总共占12个字节,是在_rep里存储了三个指针,分别是__l、__s、__r。因此总共12+4+4+4 = 24字节。


4、string类的模拟实现

  • main.cpp文件

    #include "string.h"
    
    
    int main() {
    //    xp::test_string1();
    //    xp::test_string2();
    //    xp::test_string3();
    //    xp::test_string4();
    //    xp::test_string5();
    //    xp::test_string6();
    //    xp::test_string7();
    //    xp::test_string8();
    //    xp::test_string9();
    //    xp::test_string10();
    //    xp::test_string11();
        xp::test_string12();
    
    
        return 0;
    }
    
    
  • string.cpp文件

    //
    // Created by 徐鹏 on 2023/11/29.
    //
    
    
    #include "string.h"
    
    
    namespace xp {
        string::string(const char *s) {
            _size = strlen(s);//这里还没有size()呢,size()得自己写
            _capacity = _size;
            _str = new char[_capacity + 1];//多开一个空间存\0
            strcpy(_str, s);
        }
    
        //拷贝构造函数 --   传统写法
    //    string::string(const string &str) {
    //        _size = str._size;
    //        _capacity = str._capacity;
    //        _str = new char[_capacity + 1];
    //        strcpy(_str, str._str);
    //    }
    
        //  现代写法
        string::string(const string &str) : _str(nullptr), _size(0),
                                            _capacity(0) {
            //这里得使用初始化列表或者在private里得成员变量进行缺省,防止交换后tmp指向随机值,调用析构就会出错
            string tmp(str._str);
            swap(tmp);
        }
    
        void string::swap(string &str) {
            std::swap(_str, str._str);
            std::swap(_size, str._size);
            std::swap(_capacity, str._capacity);
        }
    
        void string::reserve(size_t n) {
            if (n > _capacity) {
                //扩容
                char *new_str = new char[n + 1];
                strcpy(new_str, _str);
                delete[] _str;
                _str = new_str;
                _capacity = n;
            }
            //小于等于则不改变容量
        }
    
        void string::resize(size_t n) {
            if (n > _capacity) {
                //需要重新开辟空间
                reserve(n);
            } else {
                //缩小容量和有效字符
                _capacity = n;
                _size = n;
                _str[n] = '\0';
            }
        }
    
        char &string::operator[](size_t pos) {
            assert(pos < _size);
            return _str[pos];
        }
    
        const char &string::operator[](size_t pos) const {
            assert(pos < _size);
            return _str[pos];
        }
    
        char &string::at(size_t pos) {
    //        assert(pos < _size);
    //        抛异常
            return _str[pos];
        }
    
        const char &string::at(size_t pos) const {
    //        assert(pos < _size);
    //        抛异常
            return _str[pos];
        }
    
        void string::append(const string &str) {
            size_t total_size = _size + str._size;
            //如果新插入的字符串加上之前的字符串的长度大于容量,则需要扩容
            if (total_size > _capacity) {
                reserve(total_size);
            }
            //把字符加进来
            for (int i = 0; i < str._size; ++i) {
                _str[_size++] = str[i];
            }
            _str[_size] = '\0';
        }
    
        void string::append(const char *s) {
            size_t total_size = _size + strlen(s);
            //如果新插入的字符串加上之前的字符串的长度大于容量,则需要扩容
            if (total_size > _capacity) {
                reserve(total_size);
            }
            //把字符加进来
            strcpy(_str + _size, s);
            _size += strlen(s);
        }
    
        string &string::operator+=(const string &str) {
            append(str);
            return *this;
        }
    
        string &string::operator+=(const char *s) {
            string str(s);
            append(str);
            return *this;
        }
    
        string &string::operator+=(char c) {
            push_back(c);
            return *this;
        }
    
        void string::push_back(char c) {
            size_t total_size = _size + 1;
            //如果新插入的字符加上之前的字符串的长度大于容量,则需要扩容
            if (total_size > _capacity) {
                reserve(total_size);
            }
            //把字符加进来
            _str[_size++] = c;
            _str[_size] = '\0';
        }
    
        string &string::insert(size_t pos, const string &str) {
            assert(pos < _size);
    //        size_t total_size = _size + str._size;
    //        //如果新插入的字符串加上之前的字符串的长度大于容量,则需要扩容
    //        if (total_size > _capacity) {
    //            reserve(total_size);
    //        }
    //        int end = _size;
    //        //pos后面数据先后移,每个元素后移str._size,这里end最后等于-1
    //        while (end >= (int) pos) {
    //            _str[end + str._size] = _str[end];
    //            --end;
    //        }
    //
            size_t end = total_size;
            //pos后面数据先后移,每个元素后移str._size,这里end最后等于0
            while (end > pos) {
                //当end减去str._size的时候,也就是预留的给插入的长度小于str._size时候,说明pos后面的数据移动完成,直接退出就行
                if (end - str._size < 0)
                    break;
                _str[end] = _str[end - str._size];
                --end;
            }
    //        //插入数据
    //        strncpy(_str + pos, str._str, str._size);
    //        _size = total_size;
            insert(pos, str.c_str());//复用
            return *this;
        }
    
        string &string::insert(size_t pos, const char *s) {
            assert(pos < _size);
            size_t total_size = _size + strlen(s);
            if (total_size > _capacity) {
                reserve(total_size);
            }
            size_t end = total_size;
            //pos后面数据先后移,每个元素后移str._size,这里是相当于元素后移,这里end最后等于0
            while (end > pos) {
                //当end减去strlen(str)的时候,也就是预留的给插入的长度小于strlen(str)时候,说明pos后面的数据移动完成,直接退出就行
                if (end - strlen(s) < 0)
                    break;
                _str[end] = _str[end - strlen(s)];
                --end;
            }
            //插入数据
            strncpy(_str + pos, s, strlen(s));
            _size = total_size;
            return *this;
        }
    
        string &string::insert(size_t pos, size_t n, char c) {
            assert(pos < _size);
            size_t total_size = _size + n;
            if (total_size > _capacity) {
                reserve(total_size);
            }
            size_t end = total_size;
            //pos后面数据先后移,每个元素后移str._size,这里是相当于元素后移,这里end最后等于0
            while (end > pos) {
                if (end - n < 0)
                    break;
                _str[end] = _str[end - n];
                --end;
            }
            //插入数据
            while (n--) {
                _str[pos++] = c;
            }
            _size = total_size;
            return *this;
        }
    
        string &string::erase(size_t pos, size_t len) {
            assert(pos < _size);
            if (len == std::string::npos || len + pos >= size()) {
                _str[pos] = '\0';//pos后面数据全部不要了
                _size = pos;
            } else {
                strcpy(_str + pos, _str + pos + len);
                _size -= len;
            }
            return *this;
        }
    
    
        size_t string::find(const string &str, size_t pos) const {
            assert(pos < _size);
            const char *ret = strstr(_str + pos, str._str);
            if (nullptr == ret) return std::string::npos;
            return ret - _str;
        }
    
        size_t string::find(const char *s, size_t pos) const {
            assert(pos < _size);
            const char *ret = strstr(_str + pos, s);
            if (nullptr == ret) return std::string::npos;
            return ret - _str;
        }
    
        size_t string::find(char c, size_t pos) const {
            assert(pos < _size);
            while (pos < _size) {
                if (_str[pos] == c) return pos;
                pos++;
            }
            //没找到
            return std::string::npos;
        }
    
        string string::substr(size_t pos, size_t len) const {
            assert(pos < _size);
            size_t end = pos + len;
            if (len == npos || end >= _size) {
                end = _size;
            }
            string str;
            str.reserve(end - pos);//开辟空间
            while (pos < end) {
                str += _str[pos++];
            }
            //把\0也带进去
            str._str[_size] = '\0';
            return str;
        }
    
        // 传统写法
    //    string &string::operator=(const string &str) {
    //        if (this != &str) {
    //            reserve(str._capacity);
    //            strcpy(_str, str._str);
    //            _size = str._size;
    //        }
    //
    //        return *this;
    //    }
    
        //  现代写法
        string &string::operator=(string str) {
            //这里不会改变赋值的值,这里str只是临时拷贝
            swap(str);
            return *this;
        }
    
        string &string::operator=(const char *s) {
            reserve(strlen(s));
            strcpy(_str, s);
            _size = strlen(s);
            return *this;
        }
    
        string &string::operator=(char c) {
            reserve(1);
            _str[0] = c;
            _str[1] = '\0';
            _size = 1;
            return *this;
        }
    
    
        ostream &operator<<(ostream &out, string &str) {
            for (auto e: str) {
                out << e;
            }
            return out;
        }
    
        istream &operator>>(istream &in, string &str) {
            str.clear();
            char buffer[128];//缓冲一下
            char c = in.get();
            int i = 0;
            while (c != ' ' && c != '\n') {
                buffer[i++] = c;
                if (i == 127) {
                    buffer[i] = '\0';
                    str += buffer;
                    i = 0;//重置
                }
                c = in.get();
            }
            //提前读到' ' 或者 '\n'
            if (i > 0) {
                buffer[i] = '\0';
                str += buffer;
            }
            return in;
        }
    
        string::~string() {
            delete[] _str;
            _str = nullptr;
            _capacity = 0;
            _size = 0;
        }
    
        void test_string1() {
            string s1;
            cout << s1.c_str() << endl;
            string s2("hello world");
            cout << s2.c_str() << endl;
    
            string s3(s2);
            cout << s3.c_str() << endl;
        }
    
        void test_string2() {
            string s1;
            string s2("hello world");
    
            cout << s2.capacity() << endl;
            s2.reserve(2);
            cout << s2.capacity() << endl;
    
            s2.resize(15);
            cout << s2.capacity() << endl;
    
            s2.resize(7);
            cout << s2.capacity() << endl;
            cout << s2.c_str() << endl;
    
        }
    
        void test_string3() {
            string s1("hello world");
            const string s2("nihao liangzai");
    
            string::iterator it1 = s1.begin();
            while (it1 != s1.end()) {
                cout << *it1 << " ";
                ++it1;
            }
            cout << endl;
    
            string::const_iterator it2 = s2.begin();
            while (it2 != s2.end()) {
                cout << *it2 << " ";
                ++it2;
            }
            cout << endl;
        }
    
        void test_string4() {
            string s1("hello world");
            const string s2("nihao liangzai");
    
    //        s1.append(s2);
    //        s1.append("hh");
    //        s1 += s2;
    //        s1 += "hh";
    //        s1 += 'x';
            s1.push_back('v');
    
            cout << s1.c_str() << endl;
        }
    
        void test_string5() {
            string s1("hello world");
            const string s2("nihao liangzai");
    
    //        s1.insert(0, s2);
    //        s1.insert(2, "hh");
            s1.insert(11, 5, 'x');
            cout << s1.c_str() << endl;
        }
    
        void test_string6() {
            string s1("hello world");
            const string s2("nihao liangzai");
            s1.erase();
            cout << s1.c_str() << endl;
        }
    
        void test_string7() {
            string s1("hello world");
            string s2("nihao liangzai");
            cout << s1.c_str() << endl;
            cout << s2.c_str() << endl;
            s1.swap(s2);
            cout << s1.c_str() << endl;
            cout << s2.c_str() << endl;
        }
    
        void test_string8() {
            string s1("hello world");
            const string s2("wo");
            size_t pos1 = s1.find(s2);
            size_t pos2 = s1.find("d");
            size_t pos3 = s1.find("f");
            size_t pos4 = s1.find('o');
    
            cout << pos1 << endl;
            cout << pos2 << endl;
            cout << (int) pos3 << endl;
            cout << (int) pos4 << endl;
        }
    
        void test_string9() {
            string s1("hello world");
            string s2(s1.substr(5, 2));
            const string s3("nihao liangzai");
            string s4(s3.substr(2, 5));
    
            cout << s2.c_str() << endl;
            cout << s4.c_str() << endl;
        }
    
        void test_string10() {
            string s1("hello world");
            const string s2("nihao liangzai");
    
            cout << s1.c_str() << endl;
            s1 = s2;
            cout << s1.c_str() << endl;
            s1 = "woshixp";
            cout << s1.c_str() << endl;
            s1 = '1';
            cout << s1.c_str() << endl;
        }
    
        void test_string11() {
            string s;
            cin >> s;
            cout << s << endl;
        }
    
        void test_string12() {
            string s1("111");
            string s2(s1);
            cout << s1 << endl;
            cout << s2 << endl;
    
            string s3;
            s3 = s2;
            cout << s3 << endl;
        }
    
    }
    
  • string.h文件

    //
    // Created by 徐鹏 on 2023/11/29.
    //
    
    #include <cstring>
    #include <iostream>
    #include <assert.h>
    #include <vector>
    
    using namespace std;
    
    
    #ifndef DEMO_42_STRING_H
    #define DEMO_42_STRING_H
    
    #endif  DEMO_42_STRING_H
    
    namespace xp {
    
        class string {
        public:
            //构造函数
            string() : _str(new char[1]), _size(0), _capacity(0) {
                _str[0] = '\0';//空字符串也包含一个\0
            }
    
            string(const char *s);
    
            //拷贝构造函数
            string(const string &str);
    
            //字符串大小
            size_t size() const {
                return _size;
            }
    
            //总容量
            size_t capacity() const {
                return _capacity;
            }
    
            //string转char*
            const char *c_str() const {
                return _str;
            }
    
            //要求string类对象的容量调整为计划好的容量
            void reserve(size_t n);
    
            //将有效字符的个数改成n个
            void resize(size_t n);
    
            //清空字符串
            void clear() {
                _str[0] = '\0';
                _size = 0;//只清空数据,不清空容量
            }
    
            //判断字符串是不是空字符串
            bool empty() {
                return _size == 0;
            }
    
            //下标访问
            char &operator[](size_t pos);
    
            const char &operator[](size_t pos) const;
    
            char &at(size_t pos);
    
            const char &at(size_t pos) const;
    
            const char &back() const {
                return _str[_size - 1];
            }
    
            const char &front() const {
                return _str[0];
            }
    
            //迭代器
            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;
            }
    
            //尾插字符或者字符串
            void append(const string &str);
    
            void append(const char *s);
    
            //尾插字符或者字符串
            string &operator+=(const string &str);
    
            string &operator+=(const char *s);
    
            string &operator+=(char c);
    
            //尾插字符
            void push_back(char c);
    
            //从某个位置前插字符
            string &insert(size_t pos, const string &str);
    
            string &insert(size_t pos, const char *s);
    
            string &insert(size_t pos, size_t n, char c);
    
            //从某个位置向后删除n个字符
            string &erase(size_t pos = 0, size_t len = std::string::npos);
    
            //交换
            void swap(string &str);
    
            //从某个位置开始查找字符、字符串、string对象位置
            size_t find(const string &str, size_t pos = 0) const;
    
            size_t find(const char *s, size_t pos = 0) const;
    
            size_t find(char c, size_t pos = 0) const;
    
            //取子串
            string substr(size_t pos = 0, size_t len = npos) const;
    
            //赋值
            string &operator=(string str);
    
            string &operator=(const char *s);
    
            string &operator=(char c);
    
            ~string();
    
        private:
            char *_str;
            size_t _capacity;
            size_t _size;
            static const size_t npos = -1;
        };
    
        //重载流插入
        ostream &operator<<(ostream &out, string &str);
    
        //重载流提取
        istream &operator>>(istream &in, string &str);
    
        void test_string1();
    
        void test_string2();
    
        void test_string3();
    
        void test_string4();
    
        void test_string5();
    
        void test_string6();
    
        void test_string7();
    
        void test_string8();
    
        void test_string9();
    
        void test_string10();
    
        void test_string11();
    
        void test_string12();
    
    }
    

OKOK,C++ STL容器之string类就到这里。如果你对Linux和C++也感兴趣的话,可以看看我的主页哦。下面是我的github主页,里面记录了我的学习代码和leetcode的一些题的题解,有兴趣的可以看看。

Xpccccc的github主页

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1483057.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在线上传解压PHP文件代码,压缩/压缩(网站一键打包)支持密码登录

在线上传解压PHP文件代码&#xff0c;压缩/压缩(网站一键打包)支持密码登录 资源宝分享&#xff1a;www.httple.net 如果你没有主机控制面板这个是最好选择&#xff0c;不需要数据库&#xff0c;上传当控制面板使用&#xff0c;无需安装任何扩展&#xff0c;安全高&#xff0c;…

算法刷题day20:二分系列

目录 引言一、借教室二、分巧克力三、管道四、技能升级五、冶炼金属六、数的范围七、最佳牛围栏 引言 这几天一直在做二分的题&#xff0c;都是上了难度的题目&#xff0c;本来以为自己的二分水平已经非常熟悉了&#xff0c;没想到还是糊涂了一两天才重新想清楚&#xff0c;想…

Redis主从复制+Redis哨兵模式+Redis群集模式

Redis主从复制Redis哨兵模式Redis群集模式一、Redis主从复制1、主从复制的作用2、主从复制过程3、搭建Redis主从复制3.1 所有节点服务器安装redis3.2 修改Redis配置文件(Master节点操作)3.3 修改Redis配置文件(Slave节点操作)3.4 验证主从效果 二、Redis哨兵模式1、哨兵模式的作…

2024最详细的接口测试用例设计教程

一、接口测试流程 1、需求讨论 2、需求评审 3、场景设计 4、数据准备 5、测试执行 二、分析接口文档元素 1、接口名称 2、接口地址 3、支持格式 4、请求方式 5、请求参数&#xff08;参数名称、类型、是否必填、参数说明等&#xff09; 6、返回参数&#xff08;返回…

博途PLC 面向对象系列之“双通气缸功能块“(SCL代码)

1、面向对象系列之找对象 https://rxxw-control.blog.csdn.net/article/details/136150027https://rxxw-control.blog.csdn.net/article/details/1361500272、博途PLC 面向对象系列之"单通气缸功能块" https://rxxw-control.blog.csdn.net/article/details/1363399…

DB-GPT:大模型 + 数据库,全流程自动化

DB-GPT&#xff1a;大模型 数据库&#xff0c;全流程自动化 提出背景DB-GPT 结构具体问题与解法背景分析对比其他工具DB-GPT系统设计 提出背景 论文&#xff1a;https://arxiv.org/pdf/2312.17449.pdf 代码&#xff1a;https://github.com/eosphoros-ai/DB-GPT 本文介绍了D…

pytorch --反向传播和优化器

1. 反向传播 计算当前张量的梯度 Tensor.backward(gradientNone, retain_graphNone, create_graphFalse, inputsNone)计算当前张量相对于图中叶子节点的梯度。 使用反向传播&#xff0c;每个节点的梯度&#xff0c;根据梯度进行参数优化&#xff0c;最后使得损失最小化 代码…

Dynamo幕墙探究系列(一)

一直想写个系列教程&#xff0c;但是没有那么多时间整理资料&#xff0c;这次呢&#xff0c;先弄个小系列吧&#xff0c;还是和之前差不多的幕墙测试&#xff0c;我们分几节课&#xff0c;一步一步深入研究。 今天先开个小头儿&#xff0c;要弄的&#xff0c;就是下面这么个模型…

小程序图形:echarts-weixin 入门使用

去官网下载整个项目&#xff1a; https://github.com/ecomfe/echarts-for-weixin 拷贝ec-canvs文件夹到小程序里面 index.js里面的写法 import * as echarts from "../../components/ec-canvas/echarts" const app getApp(); function initChart(canvas, width, h…

【IDEA】2023版IDEA安装破解教程

2023版IDEA安装破解教程 第一步&#xff1a;IDEA的卸载 这里以Windows11系统为例&#xff0c;首先我们打开控制面板&#xff0c;点击程序&#xff0c;找到自己的IDEA&#xff0c;双击卸载。&#xff08;或者可以直接找到idea所在文件位置&#xff0c;直接delete文件夹&#x…

化肥工业5G智能制造工厂数字孪生可视化平台,推进化肥行业数字化转型

化肥工业5G智能制造工厂数字孪生可视化平台&#xff0c;推进化肥行业数字化转型。随着科技的不断发展&#xff0c;数字化转型已经成为各行各业发展的必然趋势。在化肥工业领域&#xff0c;5G智能制造工厂数字孪生可视化平台的应用正在逐渐普及&#xff0c;为行业数字化转型提供…

微信小程序云开发教程——墨刀原型工具入门(页面交互+交互案例教程)

引言 作为一个小白&#xff0c;小北要怎么在短时间内快速学会微信小程序原型设计&#xff1f; “时间紧&#xff0c;任务重”&#xff0c;这意味着学习时必须把握微信小程序原型设计中的重点、难点&#xff0c;而非面面俱到。 要在短时间内理解、掌握一个工具的使用&#xf…

AI时代编程新宠!如何让孩子成为未来的编程大师?

文章目录 一、了解编程的基础概念二、选择适合的编程工具三、激发孩子的兴趣四、注重基础能力的培养五、提供实践机会六、鼓励孩子与他人合作七、持续支持与鼓励《信息学奥赛一本通关》本书定位内容简介作者简介目录 随着科技的迅猛发展&#xff0c;编程已经从一种专业技能转变…

C++三级专项 Hermite多项式

用递归的方法求Hermite多项式的值。 对给定的x和正整数n&#xff0c;求多项式的值&#xff0c;并保留两位小数。 输入 给定的n和正整数x. 输出 多项式的值。 输入样例 1 2 输出样例 4.00 解析&#xff1a;按照题目给出的要求&#xff1a;递归来解决就行&#xff0c;上…

『京墨』1.7.0 发布,开源的诗文(名句)、歇后语、成语、绕口令、节日等的阅读 APP

1.7.0 更新日志 优化 UI 显示&#xff1b;优化数据同步&#xff0c;尤其是诗文同步&#xff1b;【诗文名句】【成语】【歇后语】模块添加收藏功能&#xff1b;添加“滑动翻页”功能。 介绍 『京墨』开源的古诗词文&#xff08;名句&#xff09;、歇后语、成语、绕口令、节日…

C++_map与set

目录 一、set 1、set的用法 2、multiset 二、map 1、map的用法 2、map的operator[] 3、multimap 结语 前言&#xff1a; C中的map和set容器属于关联式容器&#xff0c;与序列式容器不同的地方在于&#xff08;序列式容器即vector、list&#xff0c;其底层是由线性数据…

牛客禁用题:求阶乘

思路&#xff1a;在新类中使用全局变量进行运算&#xff0c;在主类中定义新类数组&#xff0c;通过构造函数的调用次数返回阶乘 #include <type_traits> class add{public:static int count;static int tmp;add(){countcounttmp;tmp;} }; int add::count0; int add::t…

Flink:动态表 / 时态表 / 版本表 / 普通表 概念区别澄清

博主历时三年精心创作的《大数据平台架构与原型实现&#xff1a;数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行&#xff0c;点击《重磅推荐&#xff1a;建大数据平台太难了&#xff01;给我发个工程原型吧&#xff01;》了解图书详情&#xff0c;…

在golang中使用protoc

【Golang】proto生成go的相关文件 推荐个人主页&#xff1a;席万里的个人空间 文章目录 【Golang】proto生成go的相关文件1、查看proto的版本号2、安装protoc-gen-go和protoc-gen-go-grpc3、生成protobuff以及grpc的文件 1、查看proto的版本号 protoc --version2、安装protoc-…

Java 打包 SpringBoot 项目报错

Java 打包 SpringBoot 项目报错 问题重现 Please refer to xxxx for the individual test results. Please refer to dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstream. 解决问题 在 pom.xml 的 <properties> 中添加项目代码 <s…