C++-STL-String重点知识

基础介绍

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把空间预留好。

模拟实现

cpp 复制代码
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;
    }
相关推荐
Yolo566Q1 小时前
R语言与作物模型(以DSSAT模型为例)融合应用高级实战技术
开发语言·经验分享·r语言
Felven1 小时前
C. Challenging Cliffs
c语言·开发语言
Dreamsi_zh1 小时前
Python爬虫02_Requests实战网页采集器
开发语言·爬虫·python
_君落羽_2 小时前
C语言 —— 指针(4)
c语言·开发语言
weixin_448617053 小时前
疏老师-python训练营-Day30模块和库的导入
开发语言·python
望星空听星语3 小时前
C语言自定义数据类型详解(四)——联合体
c语言·开发语言
壹立科技3 小时前
Java源码构建智能名片小程序
java·开发语言·小程序
小小李程序员4 小时前
JSON.parse解析大整数踩坑
开发语言·javascript·json
宋辰月4 小时前
Vue2-VueRouter
开发语言·前端·javascript
golitter.5 小时前
python的异步、并发开发
开发语言·python