前言
一、string是什么?
string 类是basic_string 模板类的一个实例
使用char来实例化basic_string 模板类
string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator> string;
二、使用
1.引入库
#include<string>
2.具体使用
1.构造 拷贝构造
1直接用字符串初始化
2用另一个string 拷贝构造
3赋值构造用字符串
4赋值构造用另一个string
void test1()
{
string s1 ( "abc");//构造
string s2 ( s1);//拷贝构造
string s3 = "abc";//赋值函数重载
string s4 = s1;//赋值函数重载
}
2增删查改
获取size capacity
size是有效元素个数,capacity是可用的空间
string s = "123456";
//size 大小
s.size();
//capacity容量
s.capacity();
插入
尾插一个字符
//尾插
s.push_back('1');
尾插字符串或字符
s += "abc";//一般用这个更便捷
s.append("23");//加字符串
append也可用迭代器 迭代器是一个类似指针的东西(但不一定是指针
s.append(s.begin(), s.end());
扩容//注意只扩容 不缩容
s.reserve(5);
改变大小为5 扩充size 用’0‘填充 或减少size 不够空间就扩capacity
s.resize(5,'0');
clear 置空 即把size置空 capacity不变
s.clear();
访问元素
区别 对于越界的处理不同 s[10]如果越界会抛异常
s.at(10);
s[10];
插入 在0位置插入
s.insert(0, "123");
删除 在开头删除5个字符
s.erase(0, 5);
替换 在0位置的后三个字符替换为"a"
s.replace(0, 3, "a");
find 完全相同才算找到 //从3位置找a (3可以缺省,表示从头找到尾)
也可找字串
s.find('a', 3);
s.find("abc", 4);
rfind 从后面开始寻找
s.rfind('a');
find_first_of有一个字符就返回下标//类似strstr(s1,s2)在s1找s2中的任一个字符
s.find_first_of("sga");
返回const char* 为了兼容c
s.c_str();
//转化
stod为string to double 将字符串转到double 其他类似
int i=stoi("21");
double j = stod("1");
long double x = stold("12");
string s = to_string(1);
3模拟实现(简化版)
一些比较重要的函数
构造函数
不传参时这里的str为\0 ,注意不是'/0'这是字符 , 也不是"/0" 这等效/0/0
这样可以与下面size和capacity对应为0
string(const char* str = "")
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];//开多一个保存\0
strcpy(_str, str);
}
析构函数
~string()
{
delete[]_str;
_str = nullptr;
_size = _capacity = 0;
}
swap
不使用算法库里类模板的swap直接交换是为了 减少三次构造拷贝
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
拷贝构造 直接复用构造函数
注意 比如s2(s1) 本质时s2与tmp进行交换 tmp._str指向的内容变成了s2指向的内容 如果_str没有初始化 临时变量 tmp析构时会释放未申请的空间
swap(tmp);如果_str没有初始化 临时变量 tmp析构时会释放未申请的空间
所以_str=nullptr 最好初始化一下
string(const string& s1)
{
string tmp(s1._str);
swap(tmp);
}
赋值
string& operator=(string s)
{
swap(s);
return *this;
}
这里如果传const string &s 用传统写法
则要i判断一下s与(*this)是否相同
reverse
创建n+1 是为了保存多一个\0
void reverse(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[]_str;
_str = tmp;
_capacity = n;
}
}
+= 注意点1.要记得最后加上‘\0’
2.扩容时要考虑一下容量为0的情况
string& operator+=(char ch)
{
if (_size == _capacity)
reverse(_capacity == 0 ? 4 : 2 * _capacity);
_str[_size] = ch;
_size++;
_str[_size] = '\0';
return *this;
}
string& operator+=(const string& s)
{
size_t len = strlen(s._str);
if (len + _size > _capacity)
reverse(len + _size);
strcpy(_str + _size, s._str);
_size += len;
return *this;
}
插入
void insert(size_t pos,char ch)//输入参数
{
assert(pos <= _size);
if (_size == _capacity)
reverse(_capacity == 0 ? 4 : 2 * _capacity);
int end = _size;//不是size_t类型
while (end>=(int)pos)//防止int类型的end整形提升为size_t
{
_str[end + 1] = _str[end];
end--;
}
_str[pos] = ch;
_size++;
}
void insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (_size + len > _capacity)
reverse(_size + len);
int end = _size;
while (end >= (int)pos)
{
_str[end + len] = _str[end];
end--;
}
strncpy(_str + pos, str, len);
_size += len;
}
getline
void getline(string& s)
{
s.clear();
char buff[128];
int i = 0;
char ch = cin.get();
while (ch != '\n')
{
buff[i++] = ch;
if (i == 127)
{
buff[i] = '\0';
s += buff;
i = 0;
}
ch = cin.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
}
完整版
class String
{
public:
String(const char* str = "")//注意不是'/0'这是字符 也不是"/0"等效/0/0
:_size(strlen(str))//strlen为0
{
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
const char* c_str()const
{
return _str;
}
//遍历
size_t size()const
{
return _size;
}
char& operator[](const size_t i)
{
return _str[i];
}
const char& operator[](const size_t i)const
{
return _str[i];
}
//旧写法
//String(const String& s)///注意要用引用 否则无穷递归
//{
// char* tmp = new char[s._capacity+1];//要加一 拷贝'\0'
// //_str = new char[s._capacity+1];
// //直接赋值也行 因为_str原本为空
// strcpy(tmp, s._str);
// delete[]_str;
// _str = tmp;
// _size = s._size;
// _capacity = s._capacity;
//}
//新写法
String(const String& s)
{
String tmp(s._str);//构造函数
swap(tmp);//非算法库的交换
//注意 s2(s1) 本质时s2与tmp进行交换 tmp._str指向的内容变成了s2指向的内容 如果_str没有初始化 临时变量 tmp析构时会释放未申请的空间
//所以——str=nullptr 最好初始化一下
//但vs编译器处理了
}
//String& operator=(String s)//s2=s1;//先拷贝构造 s
//{
// //String tmp(s);拷贝构造
// //swap(tmp);
// swap(s);//更简介
// return *this;
//}
~String()
{
delete[]_str;
_str = nullptr;
_size = _capacity = 0;
}
void clear()
{
_size = 0;
_str[0] = '\0';
}
typedef char* iterator;
typedef const char* const_iterator;
const_iterator begin()const
{
return _str;
}
const_iterator end()const
{
return _str + _size;
}
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
void reserve(size_t n)
{
if (n > _capacity)//拷贝
{
char* tmp = new char[n + 1];//注意不是n
strcpy(tmp, _str);
delete[]_str;
_str = tmp;
_capacity = n;
}
}
void push_back(char ch)//扩容2倍
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 * _capacity);
_capacity = 2 * _capacity;
}
_str[_size++] = ch;
_str[_size] = '\0';//注意
}
void append(const char* str)//扩容 根据str算
{
size_t len = strlen(str);
if (_size + len > _capacity)
reserve(_size + len);
strcpy(_str + _size, str);//注意
_size += len;
}
String& operator+=(char ch)
{
push_back(ch);
return *this;
}
String& operator+=(const char* str)
{
append(str);
return *this;
}
void insert(size_t pos, char ch) // 这里的pos可以改成int 则检查时用pos >= 0
{
assert(pos <= _size);//assert(pos >= 0 && pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 * _capacity);//注意
_capacity = 2 * _capacity;
}
//易错 如果pos是无符号整型 end是有符号整形 有符号整形会提升为无符号整形
// //则end永远不会为复数
// //改进方法
// //1 将—_size强制转换为(int)
// 改法一
// int end = _size;
//while (end >= (int)pos)
//{
// _str[end + 1] = _str[end];
// end--;
//}
//改法二
//end=pos+1;以end大于pos为结束条件
int end = _size + 1;
while (end > (int)pos)
{
_str[end] = _str[end - 1];
end--;
}
_size++;
_str[pos] = ch;
}
void insert(size_t pos, const char* str)//这里的pos可以改成int 则检查时用pos>=0
{
assert(pos <= _size);//assert(pos >= 0 && pos <= _size);
int len = strlen(str);
if (len + _size > _capacity)
{
reserve(len + _size);
}
int end = _size;
while (end >= (int)pos)
{
_str[end + len] = _str[end];//将字符串拷贝到另一个字符串中间 不包括\0
end--;
}
strncpy(_str + pos, str, len);
_size += len;
}
void erase(size_t pos, size_t len = npos)//静态成员变量 类内声明 类外定义
{
/*if (len == npos || pos + len > _size)*///如果len为npos-1,pos为2会溢出
//尽量不要加法比较
if (len == npos || pos > _size - len)//重点
{
_str[pos] = '\0';
_size = pos;
}
assert(pos < _size);
//法一
strcpy(_str + pos, _str + pos + len);
//法二
/*int begin = pos;
while (begin+len<=_size)
{
_str[begin] = _str[begin + len];
begin++;
}*/
}
void resize(size_t n, char ch = '\0')
{
if (n <= _size)
{
_str[n] = '\0';
_size = n;
}
else
{
reserve(n);
while (n > _size)
{
_str[_size] = ch;
_size++;
}
_str[_size] = '\0';
}
}
String& operator=(const String&s)
{
char*tmp = new char[s._capacity + 1];//赋值要用临时变量暂时存储
strcpy(tmp, s._str);
delete[]_str;
_str = tmp;
_capacity = s._capacity;
_size = s._size;
return *this;
}
void swap(String& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
size_t find(char ch,size_t pos=0)const
{
for (size_t i = pos;i < _size;i++)
{
if (_str[i] == ch)
return i;
}
return npos;
}
size_t find(const char* sub, size_t pos = 0)const//寻找字串 strstr kmp
{
const char* p=strstr(_str+pos, sub);//匹配成功返回对应位置的指针
if (p)
return p - _str;
else
return npos;
}
String substr(size_t pos = 0, size_t len = npos)
{
String sub;
if (len == npos || len > _size - pos)
{
for (size_t i = pos;i < _size;i++)
{
sub += _str[i];
}
}
else
{
for (size_t i = pos;i < pos + len;i++)
{
sub += _str[i];
}
}
return sub;
}
private:
char* _str=nullptr;
size_t _size=0;
size_t _capacity=0;
public:
static const int npos;//静态成员变量 类内声明 类外定义 属于每一个对象
};
bool operator==(const String& s1, const String& s2) //应该放在全局函数 可以比较
{
return strcmp(s1.c_str(), s2.c_str());//strcmp比较字符串 相等返回0
//不能访问私有成员 用c_str 有隐式类型转化 可以比较 "abc"=s1
}
bool operator<(const String& s1, const String& s2)
{
return strcmp(s1.c_str(), s2.c_str());
}
bool operator>(const String& s1, const String& s2)
{
return strcmp(s1.c_str(), s2.c_str());
}
void swap(String& s1, String& s2)
{
s1.swap(s2);
}
ostream& operator<<(ostream& out,const String& s) //不用友元 流插入不一定要友元 没有访问私有成员 一定是全局
{
//out << s.c_str();
for (auto i : s)//已经实现迭代器
{
cout << i;
}
return out;
}
//istream& operator>>(istream& in, String& s)
//{
// //in>>s._str;
// s.clear();
// char ch;
// //in >> ch;//in读不到空格和\0
// cin.get(ch);//可以读字符//cin是个类
// while (ch != ' ' && ch != '\n')
// {
// s += ch;//
// //in >> ch;
// cin.get(ch);
// }
// return in;
//}
istream& operator>>(istream& in, String& s)
{
size_t i = 0;
char buff[128];
char ch = cin.get();
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == 127)//到结尾后
{
buff[i] = '\n';
s += buff;
i = 0;
}
}
if (i > 0)//i==0 buff没有有效内容了
{
buff[i] = '\n';
s += buff;
}
return in;
}
void getline(istream& in, String& s)
{
s.clear();
char ch;
//in >> ch;//in读不到空格和\0
cin.get(ch);//可以读字符//cin是个类
while( ch != '\n')
{
s += ch;//
//in >> ch;
cin.get(ch);
}
}
const int String::npos = -1;//不用static
void test_MyString1()
{
String s = "1234";;
const String s1 = "1234";
String::const_iterator it = s1.begin();//类域名 iterator 有很多
while (it != s1.end())
{
cout << *it;
it++;
}
cout << endl;
//for (auto i : s)//范围for 本质是迭代器的替换
//{
// cout << i;
//}
}
//增加
void test_MyString2()
{
String s = "hello";
s.push_back('1');
s.append("ad");
cout << s.c_str() << endl;
s += "134";
s += '1';
cout << s.c_str();
}
void test_MyString3()
{
String s = "helo";
s.insert(0, "abc");
s.insert(0, "abc");
s.insert(0, "abc");
s.insert(0, "abc");
//s.erase(0, 3);
cout << s.c_str();
}
void test_MyString4()
{
String s;
s.resize(5, 'c');
cout << s.c_str() << endl;
}
//深浅拷贝
void test_MyString5()
{
String s1 = "hello world";//构造函数
String s2 = s1;//等效String s2(s1);拷贝构造
/*cout << s1.c_str() << endl;
cout << s2.c_str() << endl;*/
//系统自动生成的拷贝构造 拷贝的_str指向同一块空间 在析构的时候
//对同一块空间析构了两次
String s3 = "hahaha";
String s4 = "hehehe";
cout << s3.c_str() << endl;
//cout << s4.c_str() << endl;
//s3 = s4;
//cout << s3.c_str() << endl;
//cout << s4.c_str() << endl;
//s3 = "66666";//这里的拷贝构造 是构造函数
cout << s3.c_str() << endl;
//赋值函数重载 也是浅拷贝
}
//swap
void test_MyString6()
{
String s1 = "hello world";
String s2 = "hehe";
cout << s1.c_str() << endl;
cout << s2.c_str() << endl;
swap(s1, s2);//防止勿用
//用非成员函数重载 算法库里的swap是模板 模板和具体函数重名时 优先调用具体函数
s1.swap(s2);
std::swap(s1, s2);//三次拷贝//算法库里的swap
cout << s1.c_str() << endl;
cout << s2.c_str() << endl;
}
//find
void test_MyString7()
{
string s1 = "hello world";
string s2 = "world";
size_t pos1 = s1.find(s2);
if (pos1 != String::npos)
cout << pos1 << endl;;
size_t pos2 = s1.find('w');
if (pos2 != String::npos)
cout << pos2 << endl;;
}
//substr
void test_MyString8()
{
String s1 = "hello world";
String s2 = s1.substr(6, 5);
cout << s2.c_str() << endl;
}
//==
void test_MyString9()
{
String s1 = "hello world";
String s2 = s1;
cout << (s1==s2)<< endl;
}
//<< >>
void test_MyString10()
{
String s1 = "hello world";
String s2;
cout << s1 << endl;
cin >> s1;
cout << s1;
}
}