基础介绍

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;
}