C++-STL-String重点知识

发布于:2025-07-31 ⋅ 阅读:(18) ⋅ 点赞:(0)

  基础介绍

        

        1.string是表示字符串的字符串类

        2.string在底层实际是:basic_string模板类的别名,typedef basic_string<char,char_traits, allocator> string;

   std::string与C风格字符串的主要区别

   std::string和C风格字符串(以\0结尾的字符数组)在多个关键方面有显著差异

      内存管理方面:

        std::string

        自动管理内存(构造时分配,析构时释放)

        可以动态调整大小(无需手动处理内存重新分配)

        C风格字符串

        需要手动管理空间(如malloc/free;new/delete)

        一般空间大小是固定的,除非显示重新分配(比如使用realloc尝试原地扩容,如果不行就分配新内存并复制)

        长度与安全性:

         std::string

        string.size()表示字符串实际长度(O(1)操作)(可以包含\0为有效内容)

        边界检查(at()会抛出异常)

        C风格字符串

        用strlen()遍历计算长度(O(n)操作)(\0为结束标志,不包含为内容)

        容易发生缓冲区溢出(如strcpy不安全)

转换方式:

// string -> C风格
std::string s = "hello";
const char* cstr = s.c_str();  // 只读访问
const char* data = s.data();   // C++17起可写(但需谨慎)

// C风格 -> string
const char* cstr = "world";
std::string s1(cstr);          // 构造转换
std::string s2 = cstr;         // 赋值转换

//其他基本类型转换

to_string()   其他-> string

#include <iostream>   // std::cout
#include <string>     // std::string, std::to_string

int main ()
{
  std::string pi = "pi is " + std::to_string(3.1415926);
  std::string perfect = std::to_string(1+2+4+7+14) + " is a perfect number";
  std::cout << pi << '\n';
  std::cout << perfect << '\n';
  return 0;
}

Output:

pi is 3.141593
28 is a perfect number              

//string->其他类型

 

 

常见操作

      1.构造:

string()                                        构造空的string类对象,即空字符串
string(const char* s)                   用C-string来构造string类对象
string(size_t n, char c)             string类对象中包含n个字符c
string(const string&s)                 拷贝构造函数
        2.容量:
size                 返回字符串有效字符长度
length              返回字符串有效字符长度
capacity           返回空间总大小
empty              检测字符串释放为空串,是返回true,否则返回false
clear                清空有效字符
reserve           为字符串预留空间
resize              将有效字符的个数该成n个,多出的空间用字符c填充

注意:

        resize(size_t n) 与 resize(size_t n, char c)在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
        reserve(size_t res_arg=0)为string预留空间,与resize不同,其不会改变有效元素个数

 

        3.修改:

push_back            在字符串后尾插字符c
append                 在字符串后追加一个字符串
operator+=          在字符串后追加字符串str
c_str                    返回C格式字符串
find + npos         从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
substr                 在str中从pos位置开始,截取n个字符,然后将其返回
        在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
        对string操作时,可以先通过reserve把空间预留好。

模拟实现

class string
    {
        friend std::ostream& operator<<(std::ostream& os, const string& s);
        friend std::istream& operator>>(std::istream& is, string& s);

    public:
        typedef char* iterator;
        static const size_t npos = -1;

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


        // 构造函数
        string(const char* str = "") : _size(strlen(str)) {
            _capacity = _size + 1;
            _str = new char[_capacity];
            strcpy(_str, str);
        }

        // 拷贝构造函数
        string(const string& s) : _size(s._size), _capacity(s._capacity) {
            _str = new char[_capacity+1];
            strcpy(_str, s._str);
        }

        // 赋值运算符
        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;
            // }
            if(this!=&s)
            {
                string tmp(s);
                swap(tmp); //tmp出作用域后析构,释放内存,避免手动释放原来的内存
            }
            return *this;
        }

        // 析构函数
        ~string() {
            delete[] _str;
            _str = nullptr;
            _size = _capacity = 0;
        }

        // 迭代器
        iterator begin() { return _str; }
        iterator end() { return _str + _size; }  //指向的是最后一个字符的下一个位置
        const iterator begin() const { return _str; }
        const iterator end() const { return _str + _size; } //const_iterator 用const修饰,不能修改内容

        // 修改操作

        void push_back(char c) {
            //先检查是否需要扩容
            if(_size + 1 > _capacity) {
                reserve((_capacity + 1) * 2); //扩容为原来的两倍
            }
            _str[_size] = c; //添加字符
            _size++; //增加字符串长度
            _str[_size] = '\0'; //添加结束符

        }

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

        void append(const char* str) {
            //追加字符串
            size_t len = strlen(str);
            if (_size + len > _capacity) {
                reserve(_size + len); //扩容
            }

            // for (size_t i = 0; i < len; ++i) {
            //     _str[_size + i] = str[i]; //添加字符
            // }
            strcpy(_str + _size, str); //直接使用strcpy函数添加字符
            _size += len; //增加字符串长度
        }

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

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


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

        // 容量操作
        size_t size() const { return _size; }
        size_t capacity() const { return _capacity; }
        bool empty() const { return _size == 0; }

        void resize(size_t n, char c = '\0') {
            if (n == _size) return; //大小相同,不需要修改
            if (n < _size) {  //小于当前大小,直接截断
                _size = n;
                _str[_size] = '\0';
            } 
            else if (n > _size) 
            {
                //大于会填充字符,默认是'\0'
                reserve(n);
                for (size_t i = _size; i < n; ++i) {
                    _str[i] = c;
                }
                _size = n;
                _str[_size] = '\0';
            }
        }

        void reserve(size_t n) {
            if (n > _capacity) {
                char* newStr = new char[n + 1]; //+1是为了添加结束符
                if (_str) {
                    strcpy(newStr, _str); //拷贝原来的字符串
                    delete[] _str; //释放原来的内存
                }
                _str = newStr; //指向新的内存
                _capacity = n; //更新容量
            }
        }

        // 访问操作符
        char& operator[](size_t index) { return _str[index]; }
        const char& operator[](size_t index) const { return _str[index]; }

        // 关系运算符
        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 strcmp(_str, s._str) > 0; }
        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); }

        // 查找
        size_t find(char c, size_t pos = 0) const {
            if (pos >= _size) return npos;
            const char* ptr = strchr(_str + pos, c);
            return ptr ? ptr - _str : npos;
        }

        size_t find(const char* s, size_t pos = 0) const {
            if (pos >= _size) return npos;
            const char* ptr = strstr(_str + pos, s);
            return ptr ? ptr - _str : npos;
        }

        // 插入
        string& insert(size_t pos, char c) {
            if (pos > _size) return *this;
            if (_size + 1 > _capacity) {
                reserve((_capacity + 1) * 2);
            }
            for (int i = _size; i > (int)pos; --i) {
                _str[i] = _str[i - 1];
            }
            _str[pos] = c;
            ++_size;
            _str[_size] = '\0';
            return *this;
        }

        string& insert(size_t pos, const char* str) {
            if (pos > _size) return *this;
            size_t len = strlen(str);
            if (len == 0) return *this;
            if (_size + len > _capacity) {
                reserve(_size + len);
            }
            // 使用int类型的循环变量,或者改变循环方式
            for (int i = _size; i >= (int)pos; --i) {  
                _str[i + len] = _str[i];
            }
            for (size_t i = 0; i < len; ++i) {
                _str[pos + i] = str[i];
            }
            _size += len;
            _str[_size] = '\0';  // 加结束符
            return *this;
        }

        
        // 删除
        string& erase(size_t pos, size_t len = npos) {
            if (pos >= _size) return *this;
            size_t maxLen = _size - pos;
            len = (len == npos || len > maxLen) ? maxLen : len;  //len的长度取决于pos和_size的差值
            if (len == 0) return *this;
            for (size_t i = pos; i <= _size - len; ++i) {
                _str[i] = _str[i + len];
            }
            _size -= len;
            _str[_size] = '\0';
            return *this;
        }

    private:
        char* _str = nullptr;
        size_t _capacity = 0;
        size_t _size = 0;
    };
    // 输出运算符
    std::ostream& operator<<(std::ostream& os, const string& s) {
        os << s.c_str();
        return os;
    }

    // 输入运算符
    std::istream& operator>>(std::istream& is, string& s) {
        s.clear();
        char c;
        while (is.get(c) && !isspace(c)) {
            s += c;
        }
        return is;
    }


网站公告

今日签到

点亮在社区的每一天
去签到