01. string类
string
是表示字符串的字符串类- 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作
string
的常规操作。 - string在底层实际是:
basic_string
模板类的别名,typedef basic_string<char, char traits, allocator> string
- 不能操作多字节或者变长字符的序列。
- 在使用
string
类时,必须包含#include
头文件以及using namespace std
string类是basic string模版类的一个实例,basic string实例化出了四种类型。
02. string默认成员函数
2.1 构造函数
构造函数 | 说明 |
---|---|
string() | 默认构造,创建空字符串(长度为0 ) |
string(const char* s) | 用C风格字符串初始化 |
string(const char* s, size_t n) | 用字符数组前n 个字符初始化(示例取"hello_") |
string(const string& str) | 拷贝构造,用另一个string对象初始化 |
string(const string& str, size_t pos, size_t len) | 从pos 开始截取len 个字符(示例从s2 位置2 取4 个字符,得"llo_" ) |
string(size_t n, char ch) | 创建包含n 个ch 的字符串(示例生成"AAAAAA" ) |
注意:
npos
是string
类的静态常量,表示最大可能值- 示例中
s5
的len
若超出范围自动截断到字符串末尾 - 所有构造方式均会分配内存并管理自己的字符存储(深拷贝)
void test(){
//我们不传参数,系统为我们创建一个长度为0的字符串
string s1; // string();
//使用传递的字符串创建`string`类,并进行初始化
string s2("hello_world"); // string(const char* s);
//用字符数组的前`n`个字符来初始化string类对象。
string s3("hello_world", 6); // string(const char* s, size_t n),
//通过同类型的对象来初始化(拷贝构造)
string s4(s2); // string(const string& str);
//用`str`中的第`pos`个位置后面`len`个字符进行初始化对象。
string s5(s2, 2, 4); // string(const string& str, size t pos, size t len=npos);
//创建`string`类对象,并将其内容设置成n个ch。
string s6(6, 'A'); // string(size tn, char ch);
}
int main(){
test();
return 0;
}
2.2 析构函数
2.3 复制运算符重载
void test1(){
string s1;
string s2="hello_world";//拷贝构造
s1 = s2;//string& operator= (const string& str);
string s3;
s3 = "helle_";//string& operator= (const char* s);
string s4;
s4 = 'q';//string& operator= (char c);
}
int main(){
test1();
return 0;
}
03. string使用
string类对象的访问及遍历
04. string模拟实现
4.1 深浅拷贝
默认拷贝构造/赋值的行为,即不手动实现时,默认进行浅拷贝,多个对象共享同一块内存,这个时候当程序执行完调用析构函数释放空间时,对指向的同一块区域多次释放,导致程序崩溃。
上述string类
没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1
构造s2
时,编译器会调用默认的拷贝构造。最终导致的问题是,s1
、s2
共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。
4.2 构造函数模拟实现
string(const char *str = "") // 全缺省构造函数
: _str(new char[strlen(str) + 1]), _size(0), _capacity(0)
{ // 深拷贝
strcpy(_str, str);
}
string(const string &s) // 拷贝构造 string s1(s2);this指向s1被新创的对象
: _str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
string(const char *s)
{
size_t len = strlen(s);
_str = new char[len + 1];
memcpy(_str, s, len + 1);
_size = len;
_capacity = len;
}
string(const char *s, size_t n)
{
int len = strlen(s);
if (n > len)
{
n = len;
}
_str = new char[n + 1];
memcpy(_str, s, n);
*(_str + n) = '\0'; // 重,第n个位置补\0
_size = n;
_capacity = n;
}
string(size_t n, char c)
{
_str = new char[n + 1];
for (size_t i = 0; i < n; i++)
{
_str[i] = c;
}
_str[n] = '\0';
_size = n;
_capacity = n;
}
4.3 operator[]模拟实现
string &operator=(const string &str) // s1=s2,s1是新的
{
if (this != &str)
{
char *tmp = new char[strlen(str._str) + 1];
memcpy(tmp, str._str, strlen(str._str) + 1);
delete[] _str;
_str = tmp;
_size = str._size;
_capacity = str._capacity;
return *this;
}
}
string &operator=(const char *s) // s1="ssssss"
{
delete[] _str;
_str = new char[strlen(s) + 1];
memcpy(_str, s, strlen(s) + 1);
_size = _capacity = strlen(s);
return *this;
}
string &operator=(char c) // s1='c'
{
delete[] _str;
_str = new char[2];
_str[0] = c;
_str[1] = '\0';
return *this;
}
4.4 迭代器与下标访问
4.4.1 迭代器
在string中将迭代器简单理解成一个指针,但实际不是这样,更复杂。
typedef char* iterator;
typedef const char* const iterator;
iterator begin(){
return _str;
}
iterator end(){
return(_str+_size);
}
iterator begin()const{...}
begin()
和end()
: 正向迭代器rbegin()
和rend()
: 反向迭代器
...
string str = "Hello, World!";
// 正向迭代器遍历
string::iterator it = str.begin();
while (it != str.end()) {
std::cout << *it;
++it;
}
4.4.2 operator[]访问
char& operator[](sie_t pos){
return *(_str+pos);//==str[pos]
}
char& operator[](sie_t pos)const{...}
4.5 增删查改
4.5.1 reserve()扩容
当我们进行增加元素时,需要判断当前元素是否够用。然后开辟新空间,把原来数据转移到新空间,释放旧空间。
void reserve(size_t n){
if(n>_capacity){
//开辟新空间
char* tmp = new char[n + 1];
memcpy(tmp, _str, _size);
delete[] _str;//释放旧空间
_str = tmp;//指向新空间
_capacity = n;//改变现在容量
}
}
4.5.2 append()追加
string &append(const string &str)
{ // 追加
// 开辟新空间
size_t len = str._size;
if (len + _size > _capacity)
{
reserve(len + _size);
}
for (size_t i = 0; i < len; i++)
{
_str[_size + i] = str._str[i];
}
_size = len + _size;
_str[_size] = '\0';
}
string &append(const char *str)
{ // 字符串存在代码段
// int len = strlen(s);
// if (len+_size>_capacity)
// {
// reserve(len + _size);
// }
// for (size_t i = 0; i < len;i++){
// _str[_size + i] = s[i];
// }
// _size = len + _size;
// _str[_size] = '\0';
// 方法二 strcpy
int len = strlen(str);
if (len + _size > _capacity)
{
reserve(len + _size);
}
strncpy(_str + _size, str, len);
_size += len;
}
string &append(size_t n, char c)
{
if (n + _size > _capacity)
{
reserve(n + _size);
}
for (size_t i = 0; i < n; i++)
{
_str[_size + i] = c;
}
_size += n;
_str[_size] = '\0'; // 补补补
}
4.5.3 operator[] ()
string&operator+=(const string& str){//s1+="hello",,,,this指向的是s1
append(str);
return *this;
}
string&operator+=(const char*str){
this->append(str);
return *this;
}
string&operator+=(char ch){
// append(1, ch);或者下面这个函数调用
push_back(ch);
return *this;
}
4.5.4 push_back()
void push_back(const char ch){
if(_size==_capacity){
reserve((_capacity == 0 ? 2 : 2 * _capacity));
}
_str[_size] = ch;
_size += 1;
_str[_size] = '\0';
}
4.5.5 insert()
void insert(size_t pos, const string &str)
{
assert(pos >= 0 && pos <= _capacity);
size_t len = str._size;
if (_size == _capacity)
{
reserve(len + _size);
}
// 数据挪动
int end = _size;
while (end >= pos)
{
_str[end + len] = _str[end];
--end;
}
memcpy(_str + pos, str._str, len);
_size += len;
_str[_size] = '\0';
}
void insert(size_t pos, const char *str)
{
assert(pos >= 0 && pos <= _capacity);
size_t len = strlen(str);
size_t n = len + _size;
if (_size == _capacity)
{
reserve(len + _size);
}
// 数据挪动
for (size_t i = n; i >= pos + len; i--)
{
_str[i] = _str[i - len];
}
for (size_t i = 0; i < len; i++)
{
_str[pos + i] = str[i];
}
}
void insert(size_t pos, size_t n, char c)
{
assert(pos >= 0 && pos < _size);
if (_size + n > _capacity)
{
reserve(_size + n);
}
// 挪动数据
for (size_t i = _size + n; i >= pos + n; i--)
_str[i] = _str[i - n];
for (size_t i = 0; i < n; i++)
_str[pos + i] = c;
_size += n;
_str[_size] = '\0';
}
4.5.6 erase()
void erase(size_t pos, size_t len = npos) // npos
{
assert(pos >= 0 && pos < _size);
if (len == npos)
_str[0] = '\0';
_size = 0;
return;
for (size_t i = pos; (len + i) < _size; i++)
_str[i] = _str[i + len];
_size -= len;
_str[_size] = '\0';
}
4.5.7 find()
size_t find(const string &str, size_t pos = 0)
{
assert(pos >= 0 && pos < _size);
char *tmp = strstr(_str + pos, str._str);
if (tmp == nullptr)
{
return -1;
}
return tmp -_str;
}
4.6 swap()
void swap(string &str)
{ //::表示全局域
::Swap(_str, str._str);
::Swap(_size, str._size);
::Swap(_capacity, str._capacity);
}
//类外
template <typename T>
void Swap(T &x, T &y)
{
T tmp = x;
X = y;
y = tmp;
}
4.6.1 现代写法改写operator[]
string& operator=(const string& str){
string tmp(str);//狸猫换太子
swap(tmp);
return *this;
}
串操作。。。流操作。。。
全部代码…
#include <iostream>
#include <string.h>
#include <assert.h>
using namespace std;
class string
{
public:
string(const char *str = "") // 全缺省构造函数
: _str(new char[strlen(str) + 1]), _size(0), _capacity(0)
{ // 深拷贝
strcpy(_str, str);
}
string(const string &s) // 拷贝构造 string s1(s2);this指向s1被新创的对象
: _str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
string(const char *s)
{
size_t len = strlen(s);
_str = new char[len + 1];
memcpy(_str, s, len + 1);
_size = len;
_capacity = len;
}
string(const char *s, size_t n)
{
int len = strlen(s);
if (n > len)
{
n = len;
}
_str = new char[n + 1];
memcpy(_str, s, n);
*(_str + n) = '\0'; // 重,第n个位置补\0
_size = n;
_capacity = n;
}
string(size_t n, char c)
{
_str = new char[n + 1];
for (size_t i = 0; i < n; i++)
{
_str[i] = c;
}
_str[n] = '\0';
_size = n;
_capacity = n;
}
string &operator=(const string &str) // s1=s2,s1是新的
{
if (this != &str)
{
char *tmp = new char[strlen(str._str) + 1];
memcpy(tmp, str._str, strlen(str._str) + 1);
delete[] _str;
_str = tmp;
_size = str._size;
_capacity = str._capacity;
return *this;
}
}
//string& operator=(const string& str){
// string tmp(str);
// swap(tmp);
// return *this;
// }
string &operator=(const char *s) // s1="ssssss"
{
delete[] _str;
_str = new char[strlen(s) + 1];
memcpy(_str, s, strlen(s) + 1);
_size = _capacity = strlen(s);
return *this;
}
string &operator=(char c) // s1='c'
{
delete[] _str;
_str = new char[2];
_str[0] = c;
_str[1] = '\0';
return *this;
}
void reserve(size_t n)
{ // 扩容
if (n > _capacity)
{
// 开辟新空间
char *tmp = new char[n + 1];
memcpy(tmp, _str, _size);
delete[] _str; // 释放旧空间
_str = tmp; // 指向新空间
_capacity = n; // 改变现在容量
}
}
void append(const string &str)
{ // 追加
// 开辟新空间
size_t len = str._size;
if (len + _size > _capacity)
{
reserve(len + _size);
}
for (size_t i = 0; i < len; i++)
{
_str[_size + i] = str._str[i];
}
_size = len + _size;
_str[_size] = '\0';
}
void append(const char *str)
{ // 字符串存在代码段
// int len = strlen(s);
// if (len+_size>_capacity)
// {
// reserve(len + _size);
// }
// for (size_t i = 0; i < len;i++){
// _str[_size + i] = s[i];
// }
// _size = len + _size;
// _str[_size] = '\0';
// 方法二 strcpy
int len = strlen(str);
if (len + _size > _capacity)
{
reserve(len + _size);
}
strncpy(_str + _size, str, len);
_size += len;
}
void append(size_t n, char c)
{
if (n + _size > _capacity)
{
reserve(n + _size);
}
for (size_t i = 0; i < n; i++)
{
_str[_size + i] = c;
}
_size += n;
_str[_size] = '\0'; // 补补补
}
void push_back(const char ch)
{
if (_size == _capacity)
{
reserve((_capacity == 0 ? 2 : 2 * _capacity));
}
_str[_size] = ch;
_size += 1;
_str[_size] = '\0';
}
string &operator+=(const string &str)
{ // s1+="hello",,,,this指向的是s1
append(str);
return *this;
}
string &operator+=(const char *str)
{
this->append(str);
return *this;
}
string &operator+=(char ch)
{
// append(1, ch);或者下面这个函数调用
push_back(ch);
return *this;
}
void insert(size_t pos, const string &str)
{
assert(pos >= 0 && pos <= _capacity);
size_t len = str._size;
if (_size == _capacity)
{
reserve(len + _size);
}
// 数据挪动
int end = _size;
while (end >= pos)
{
_str[end + len] = _str[end];
--end;
}
memcpy(_str + pos, str._str, len);
_size += len;
_str[_size] = '\0';
}
void insert(size_t pos, const char *str)
{
assert(pos >= 0 && pos <= _capacity);
size_t len = strlen(str);
size_t n = len + _size;
if (_size == _capacity)
{
reserve(len + _size);
}
// 数据挪动
for (size_t i = n; i >= pos + len; i--)
{
_str[i] = _str[i - len];
}
for (size_t i = 0; i < len; i++)
{
_str[pos + i] = str[i];
}
}
void insert(size_t pos, size_t n, char c)
{
assert(pos >= 0 && pos < _size);
if (_size + n > _capacity)
{
reserve(_size + n);
}
// 挪动数据
for (size_t i = _size + n; i >= pos + n; i--)
_str[i] = _str[i - n];
for (size_t i = 0; i < n; i++)
_str[pos + i] = c;
_size += n;
_str[_size] = '\0';
}
void erase(size_t pos, size_t len = npos) // npos
{
assert(pos >= 0 && pos < _size);
if (len == npos)
_str[0] = '\0';
_size = 0;
return;
for (size_t i = pos; (len + i) < _size; i++)
_str[i] = _str[i + len];
_size -= len;
_str[_size] = '\0';
}
size_t find(const string &str, size_t pos = 0)
{
assert(pos >= 0 && pos < _size);
char *tmp = strstr(_str + pos, str._str);
if (tmp == nullptr)
{
return -1;
}
return tmp - _str;
}
void swap(string &str)
{ //::表示全局域
::Swap(_str, str._str);
::Swap(_size, str._size);
::Swap(_capacity, str._capacity);
}
int size()
{
return _size;
}
char &operator[](const int i)
{
return *(_str + i);
}
~string()
{
_str = nullptr;
delete[] _str;
_capacity = _size = 0;
}
private:
char *_str;
int _size; // 已经有多少个有效字符
int _capacity; // 能存多少个有效字符, \0除外
};
template <typename T>
void Swap(T &x, T &y)
{
T tmp = x;
X = y;
y = tmp;
}
int main()
{
system("pause");
return 0;
}