• string的使用和模拟实现


    💓博主个人主页:不是笨小孩👀
    ⏩专栏分类:数据结构与算法👀 C++👀 刷题专栏👀 C语言👀
    🚚代码仓库:笨小孩的代码库👀
    ⏩社区:不是笨小孩👀
    🌹欢迎大家三连关注,一起学习,一起进步!!💓

    在这里插入图片描述

    string的介绍

    string是一个类,可以看成是一个用模板写的顺序表,它的底层结构和顺序表基本是一样的,一个字符指针和一个表示存储数据的个数的size,还有一个表示容量大小的capacity,我们知道C++需要兼容C语言,所以它的字符串后面也是需要有一个‘\0’,的但是size和capacity的大小是不包含这个‘\0’的,因此实际的容量要比capacity大一个。

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

    总结:

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

    结构

    string的结构和顺序表很相似,一个存放字符的指针,还有一个标记个数的size,还有一个标记容量的capacity。

    class
    {
      private:
          char* _str;
          size_t _size;
          size_t _capacity;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    string的常用接口

    构造和析构

    构造函数我们可以用个缺省参数,直接搞定无参和有参的情况,然后使用初始化列表把size和capacity给初始化一下,然后开个空间把参数拷贝过去就可以了。
    析构函数的话很简单,直接释放空间即可,把其他值置一下0即可。

    //构造
    string(const char* s = "")
        : _size(strlen(s))
        , _capacity(_size)
    {
        _str = new char[_capacity + 1];
        strcpy(_str, s);
    }
    //析构
    ~string()
    {
        delete[] _str;
        _str = nullptr;
        _size = _capacity = 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    遍历string

    1. 我们可以使用for循环来遍历
      在这里插入图片描述
      因为库里的string支持了[]运算符重载,我们可以就像遍历数组那样来遍历string。
    int main()
    {
        string s("123456");
        for (int i = 0; i < 6; i++)
        {
            cout << s[i] << " ";
        }
        cout << endl;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    因为它是传引用返回的,所以我们可读可写。它重载了一个const版本,所以当你传const对象时,就只能读不能写。

    int main()
    {
    	string s("123456");
    	for (int i = 0; i < 6; i++)
    	{
    		s[i]++;
    	}
    	cout << s << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 使用迭代器遍历

    迭代器我们可以理解为一个指针,然后需要一个范围,库里有几个函数,可以帮我们确定这个范围。

    在这里插入图片描述
    如果我们要正着遍历可以使用begin和end。

    在这里插入图片描述
    在这里插入图片描述
    它的返回值是一个iterator的迭代器类型,然后这个类型在string类里面的,所以我们需要确定类域。

    int main()
    {
        string s("123456");
        string::iterator it = s.begin();
        while (it != s.end())
        {
            cout << *it << " ";
            it++;
        }
        cout << endl;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    如果要倒着遍历,就需要使用了,rbegin和rend。
    在这里插入图片描述
    在这里插入图片描述
    只不过他们的返回类型是reverse_iterator。

    int main()
    {
        string s("123456");
        string::reverse_iterator it = s.rbegin();
        while (it != s.rend())
        {
    
            cout << *it << " ";
            it++;
        }
        cout << endl;
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这两对都是可读可写的,还剩两对前面带c的,是只能读,不能写。具体可戳标题详解。

    1. 使用范围for
    int main()
    {
    	string s("123456");
    	for (auto e : s)
    	{
    		cout << e << " ";
    	}
    	cout << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    size和length

    size和length的功能是一样的,都是返回字符串的长度,和C语言的strlen很相似。

    在这里插入图片描述

    在这里插入图片描述

    int main()
    {
    	string s("123456");
    	cout << s.size() << endl;
    	cout << s.length() << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    capacity

    capacity是返回已经开辟的容量的大小。

    在这里插入图片描述

    int main()
    {
    	string s("123456");
    	cout << s.capacity() << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    模拟实现

    const size_t size() const
    {
        return _size;
    }
    const size_t capacity() const
    {
        return _capacity;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    resize和reserve

    1. resize

    resize可以修改size的大小。

    在这里插入图片描述
    库里重载两个版本,resize分3个版本,它可能比capacity大,也可能在size和capacity中间,也有可能比size小。如果他比capacity大的话,会扩容,并且如果我们给字符参数,就会在我们原本的字符串后面全补我们给的那个字符,直到size变成我们设置的那个,我们没给字符的话,会补‘\0’,如果在size和capacity中间,不会扩容,但是还是会补充,如果比我们的size还小的话,就充当删除的功能了。

    int main()
    {
    	string s("123456");
    	s.resize(20);
    	s.resize(20,'x');
    	s.resize(10);
    	s.resize(4);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. reserve

    reserve可以设置capacity的大小,如果我们知道字符串多大的话,我们可以提前开好空间,因为让系统自己自动扩容的话,可能会频繁扩容,消耗太大了,所以我们在知道需要存储的字符串多大时可以提起开好空间。

    在这里插入图片描述

    int main()
    {
    	string s("123456");
    	cout << s.capacity() << endl;
    	s.reserve(100);
    	cout << s.capacity() << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    模拟实现

    void reserve(size_t n)
    {
         if (n > _capacity)
         {
             char* tmp = new char[n + 1];
             strcpy(tmp, _str);
             delete[] _str;
             _str = tmp;
    
             _capacity = n;
         }
     }
    void resize(size_t n, char c = '\0')
    {
        if (n <= _size)
        {
            _str[n] = '\0';
            _size = n;
        }
        else
        {
            reserve(n);
            size_t begin = _size;
            _size = n;
    
            while (begin < n)
            {
                _str[begin] = c;
                begin++;
            }
    
            _size = n;
            _str[begin] = '\0';
            
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    运算符重载

    1. 赋值运算符

    在这里插入图片描述

    也就是说我们可以直接用string对象或者字符串来给新的对象赋值。

    int main()
    {
    	string s;
    	s = "123456";
    	string s1("xxx");
    	s = s1;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. +和+=

    在这里插入图片描述

    在这里插入图片描述

    从这两张图我们就可以看出来,+=是成员函数,而+是全局函数。我们需要尾插是可以用+=也可以用+,但是+的代价是很大的,我们尽量使用+=。

    int main()
    {
    	string  s("123");
    	s += "456";
    	s += '7';
    	s = s + '8';
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    模拟实现

    string& operator+=(const char s)
    {
        push_back(s);
    
        return *this;
    }
    
    string& operator+=(const char* s)
    {
        append(s);
    
        return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1. 流插入和流提取
      在这里插入图片描述
      在这里插入图片描述

    也就是说,我们可以直接使用cout和cin来对sting对象来进行输入和输出了。

    模拟实现

    1. 流提取
      我们可以一个字符一个字符的提取,因为_str为私有的,我们无法访问,我们也可以使用友元,但是我们这里一个字符一个字符的访问。
    ostream& operator<<(ostream& out, const string& s)
    {
        int end = s.size();
        for (int i = 0; i < end; i++)
        {
            out << s[i];
        }
        return out;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. 流插入

    流插入也一样,我们一个字符一个字符的读,当读到‘\n’或者空格就停止,但是cin和scanf都无法读空格和换行,这是就需要一个函数,cin.get(),它的功能就和C语言的getcahr很相似。

    在这里插入图片描述

    istream& operator>>(istream& in, string& s)
    {
        s.clear();
        char ch = in.get();
    
        while (ch != ' ' && ch != '\n')
        {
            s += ch;
            ch = in.get();
        }
    
        return in;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    int main()
    {
    	string s;
    	cin >> s;
    	cout << s << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 比较运算符
      在这里插入图片描述
      我们可以直接使用比较运算符来对sting对象和字符串等进行比较了。

    模拟实现

    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+=(const char s)
    {
        push_back(s);
    
        return *this;
    }
    
    string& operator+=(const char* s)
    {
        append(s);
    
        return *this;
    }
    
    bool operator<(const string& s)
    {
        return strcmp(_str, s._str) < 0;
    }
    
    bool operator==(const string& s)
    {
        return strcmp(_str, s._str) == 0;
    }
    
    bool operator<=(const string& s)
    {
        return *this<s||*this==s;
    }
    
    bool operator>(const string& s)
    {
        return !(*this<=s);
    }
    
    bool operator>=(const string& s)
    {
        return !(*this<s);
    }
    
    bool operator!=(const string& s)
    {
        return !(*this==s);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    push_back和append

    1. push_back
      在这里插入图片描述

    尾插一个字符

    1. append
      在这里插入图片描述

    尾插一段字符串,库里面给的接口很多,我们都不是很常用,我们都喜欢用+=。

    模拟实现

    void push_back(const char ch)
    {
        if (_size == _capacity)
        {
            reserve(_capacity == 0 ? 4 : _capacity * 2);
        }
    
        _str[_size++] = ch;
        _str[_size] = '\0';
        
    }
    void append(const char* s)
    {
        size_t len = strlen(s);
        if (_size + len > _capacity)
        {
            reserve(_size+len);
        }
    
        strcpy(_str + _size, s);
    
        _size += len;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    insert和erase

    1. insert
      在这里插入图片描述

    我们一般用的都是在某个位置插入一个字符或者字符串。

    模拟实现

    string& insert(size_t pos, char ch)
    {
        assert(pos <= _size);
    
        if (_size == _capacity)
        {
            reserve(_capacity == 0 ? 4 : _capacity * 2);
        }
    
        int end = _size;
        //必须强转,不然会类型提升,当pos=0时发生死循环
        while (end >= (int)pos)
        {
            _str[end + 1] = _str[end];
            end--;
        }
        _str[pos] = ch;
        _size++;
        return *this;
    }
    
    string& insert(size_t pos, const char* str)
    {
        size_t len = strlen(str);
        if (_size + len > _capacity)
        {
            reserve(_size + len);
        }
    
        int end = _size;
        while (end >= (int)pos)
        {
            _str[end + len] = _str[end];
            end--;
        }
    
        strncpy(_str + pos, str, len);
    
        _size += len;
    
        return *this;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    1. erase
      在这里插入图片描述

    删除某个位置的len个字符

    模拟实现

    earse我们需要提供一个npos的半缺省参数,如果不给的话就表示将pos以后得数据都删了。
    nops是一个所有类都有的,所以我们可以定义为静态成员变量。

    string& erase(size_t pos, size_t len = npos)
    {
      assert(pos < _size);
       if (pos + len > _size || len == npos)
       {
           _str[pos] = '\0';
           _size = pos;
           return *this;
       }
       else
       {
           size_t begin = pos + len;
           while (begin <= _size)
           {
               _str[begin - len] = _str[begin];
               begin++;
           }
           _size -= len;
           return *this;
       }
    }
    public:
            const static int npos = -1;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    查找

    1. find
      在这里插入图片描述
      我们可以用这个函数来查找字符串或者字符。
    int main()
    {
    	string s("123456");
    	int i = s.find("23");
    	int j = s.find('5',2);
    	int x = s.find("123", 2, 3);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. rfind
      在这里插入图片描述
      rfind也是查找只不过是从最后开始找。和find相似。

    模拟实现

    size_t find(char c, size_t pos = 0) const
     {
         assert(pos < _size);
         for (size_t i = pos; i < _size; i++)
         {
             if (_str[i] == c)
             {
                 return i;
             }
         }
         return npos;
     }
    
     size_t find(const char* s, size_t pos = 0) const
     {
         size_t len = strlen(s);
         assert(pos + len < _size);
    
         char* str = strstr(_str + pos, s);
    
         if (str != nullptr)
         {
             return str - _str;
         }
    
         return npos;
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    截取字符串

    substr
    在这里插入图片描述

    从pos位置开始提取len个字符,不给len直接取到最后。

    模拟实现

    string substr(size_t pos = 0, size_t len = npos) const
    {
        string s;
        if (len == npos || pos + len > _size)
        {
            size_t i = 0;
            for (i = pos; i <= _size; i++)
            {
                s += _str[i];
            }
            return s;
        }
        else
        {
            size_t i = 0;
            for (i = pos; i < pos+len; i++)
            {
                s += _str[i];
            }
            s += '\0';
            return s;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    获取字符指针

    我们知道string是一个类,那我们想要得到底层的那个字符指针怎么获得呢?

    我们可以使用c_str这个函数
    在这里插入图片描述

    
    int main()
    {
    	string s("123456");
    	const char* str = s.c_str();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    模拟实现

    const char* c_str() const
    {
        return _str;
    }
    
    • 1
    • 2
    • 3
    • 4

    交换对象

    swap
    在这里插入图片描述

    对于string有一个专门的swap,可以用来交换两个string对象。

    int main()
    {
    	string s("123456");
    	string s1("12");
    	s.swap(s1);
    	cout << s << endl << s1 << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    模拟实现

    void swap(string& s)
     {
         std::swap(_str, s._str);
         std::swap(_size, s._size);
         std::swap(_capacity, s._capacity);
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    特殊读取

    我们知道cin和scanf一样,遇到空格或者换行都会提前结束,那我们需要读取的字符串包含空格或者换行怎么办呢,这时候就需要getline了。

    在这里插入图片描述

    int main()
    {
    	string s;
    	getline(cin, s);
    	cout << s;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    我们也可以自己指定读取结束的字符。

    今天的分享就到这里,感谢大家的关注和支持。

  • 相关阅读:
    这篇文章可能是把时间和空间复杂度讲得最透彻的了
    2024系统架构师---论软件系统架构风格
    【笔记】使用OrCAD对电路进行蒙特卡洛分析和最坏分析
    13:大数据与Hadoop|分布式文件系统|分布式Hadoop集群
    批量查询物流信息 根据派件员名称将手下的物流件筛选出来
    Kubeflow基础知识
    C# .Net AOP 演进历史POP OOP 代码细节篇
    Javascript 手写 LRU 算法
    Visual Studio2022安装教程【图文详解】(大一小白)编译软件
    【学习笔记】[COCI2018-2019#1] Teoretičar
  • 原文地址:https://blog.csdn.net/bushibrnxiaohaij/article/details/132570235