初步理解string:
第一种遍历方式
operator[] 模拟的是数组的形式,他会自己显示调用operator,我们不用自己显示写出来
第二种遍历方式
迭代器:iterator 【通用的遍历方式,推荐掌握这类方法】
string::iterator it = s.begin();
while(it != s.end())
{
cout << *it <<" ";
++it;
}
cout << endl;
begin ———————— end【末尾的下一个位置】左闭右开
[ )
typeid (it).name可以查看类型
第三种遍历方式:范围for
for(auto e: s)
{
cout << e << " ";
}
cout << endl;
底层原理就是迭代器
string是动态增长的字符数组
迭代器才是通用,下标方括号只是string的特殊用法,迭代器可以看成是指针
iterator:
正向反向iterator
正向反向const_iterator
capacity:容器
size()(推荐使用)=length()长度
max_size()编译器的最大值
capacity()容量
clear()只是清除数据,但是空间没有清除
shrink_to_fit()缩容,无法缩小到0,最小缩小到16【里面放了一个buffer】
reserve 保留保存
reverse 反转翻转
reserve(数字)直接扩容到想要的数字大小就行,但是例如reverse(100)不一定会给你只开100个字节
reserve不会缩容,除非比capacity大才会扩容
知道要插入多少数据,减少扩容,提高工作效率
empty()判空
resize( ) size=17 capacity=32
10 n<size 删除
20 size<n<capacity 插入
40 n>capacity 扩容+插入
如果不给插入的值,会默认给'\0'
Element access:元素访问
运算符重载operator []
at()
例如:string s1("hello world")
s1[5] 断言异常
s1.at(5) 抛异常
但是两个越界访问的报错结果不一样,常用还是[]
front()取头元素
back()取尾元素
Modifiers:
push_back()尾插一个字符
append()尾插字符串或者尾插多个字符
operator +=【最常用的就是这个!!!!】
+=' ';
+="apple";
assign()对这块空间进行覆盖
insert()头部插入数据
erase()删除数据
replace()替换数据
上面这三个能少用就少用,因为要挪动数据,效率不高
find()查找数据
swap()交换数据
pop_back()尾插
String operations:
c_str( ) 用于兼容c语言的string
data和c_str 差不多
find( ) 从前往后找
substr()拷贝len个字符
可以用substr(pos1)或者substr(pos1,s1.size()-1)
rfind() 从后往前找
operator +
s1 = "xxx";
s2 = "yyy";
s1+s2 【也支持字符串+string】xxxyyy
getline( ) 获取一行string数据
to_string() 把整型和浮点数转成字符串
stoi() 把字符串转成整型和浮点数
不实用:
find_frist_of 正着找是否符合的字符
find_last_of 倒着找是否符合的字符
注意:cin 不认识空格和换行,他就会认为是分割
除了string还有wstring 【w是宽字符】
window编程会遇到这个wstring:贪吃蛇之类的游戏
string 1
wstring 2
u16string 2
u32string 4
理解string 的底层原理:
}
public:
string( )
:_str(new char[1])
,_size(0)
,_capacity(0);
{
_str[0] = '\0';
}
//string(const char* str)
//:_str(new char [strlen(str)+1]) //这里strlen调用了三次
//,_size(strlen(str))
//,_capacity(strlen(str))
//{
// strcpy(_str , str);
//}
string(const char* str = "")
:_str(strlen(str))
{
_capacity=_size;
_size = new char[_capacity + 1];
strcpy(_str , str);
}
//s2(s1)拷贝构造
//传统写法
string(const string& s)
{
_str = new char[s._capacity+1];
strcpy(_str,s._str);
_size = s._size;
_capacity = s._capacity;
}
//s2(s1)
//现代写法:本质是一种复用
string (const string& s)
{
string tmp(s.str);
swap(tmp);
}
//s1 = s3;
//传统写法
string& operator= (const string& s)
{
char* tmp = new char[s._capacity +1];
strcpy(tmp ,s._str);
delete[] _str;
_str = tmp;
_size = s._size;
_capacity = s._capacity;
}
//现代写法
string& operator= (string tmp)
{
swap(tmp);
return *this;
}
~string()
{
delete[] _str;
_str = nuullptr;
_size = _capacity = 0;
}
//遍历
//for循环到s1.size()
//s1[i]下标方括号
size_t size() const
{
return _size;
}
size_t capacity() const
{
return _capacity;
}
char& operator[] (size_t pos)
{
assert(pos < size );
return _str[pos];
}
const char& operator[] (size_t pos) const
{
assert(pos < size );
return _str[pos];
}
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
iterator begin() const
{
return _str;
}
iterator end() const
{
return _str + _size;
}
void reserve (size_t n)
{
if(n > _capacity)
{
cahr* tmp = new char [n];
strcpy(tmp ,_str);
delete[] _str;
_str =tmp;
_capacity = n+ 1;
}
}
void push_back(char ch)
{
if(_size == _capacity)
{
reserve( _capacity == 0 ? 4 :2 * _capacity);
}
_str[_size] = ch;
++_size;
_str [_size] = '\0';
}
void append(const char* 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)
{
assert(pos <= _size);
if(_size == _capacity)
{
reserve( _capacity == 0 ? 4 :2 * _capacity);
}
size_t end = _size;
while(end >= (int)pos)//强转
{
_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 )
{
reserve(_size + len );
}
size_t end =_size +len;
while(end > pos + len -1)
{
_str[end] = _str[end - len];
end--;
}
strncpy(_str + pos ,str ,len);
_size += len;
}
void erase(size_t pos,size_t len = npos)
{
assert(pos < _size);
if(len == npos || pos > = _size - len) //【写成pos + len>= _size】有溢出风险
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos , str + pos +len);
_size -= len;
}
}
void resize ( size_t n, char ch = '\0')
{
if(n < _size)
{
_str[n]= '\0';
_size = n;
}
else
{
reserve(n);
for(size_t i = _size ; i <n ; i++)
{
_str[i] = ch;
}
_str[n] = '\0';
_size = n;
}
}
void swap( string& s)
{
std::swap(_str,s._str);
std::swap(_size,s._size);
std::swap(_capacity,s._capacity);
}
size_t find(char ch) const
{
assert(pos < _size);
for(size_t i =0; i < _size;i++)
{
if(_str[i] == ch)
return i ;
}
return npos;
}
size_t find(char ch,size_t pos = 0) const
{
assert(pos < _size);
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
{
//kmp算法 实践不实用
assert(pos < _size);
const char* p = strstr(str,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];
}
}
}
bool operator== (const string& s1,const string& s2)
{
int ret = strcmp(s1.c_str,s2.c_str);
return ret == 0;
}
bool operator< (const string& s1,const string& s2)
{
int ret = strcmp(s1.c_str,s2.c_str);
return ret < 0;
}
bool operator<= (const string& s1,const string& s2)
{
return s1< s2 || s1== s2;
}
bool operator> (const string& s1,const string& s2)
{
return !(s1<=s2);
}
bool operator>= (const string& s1,const string& s2)
{
return !(s1<s2);
}
bool operator!= (const string& s1,const string& s2)
{
return !(s1==s2);
}
ostream& operator<<(ostream& out ,const string& s)
{
for(auto ch :s)
{
out << ch;
}
return out;
}
istream& operator>>(istream& in ,const string& s)
{
s.clear();
char ch;
//in >> ch;
ch = in.get( );
char buff[128];
size_t i =0;
while(ch != ' '&& ch != '\n')
{
buff[i++] = ch;
if( i == 127)
{
buff[127] = '\0';
s+=buff;
i = 0;
}
ch = in.get( );
}
if(i > 0)
{
buff[i] = '\0';
s+=buff;
}
return in;
}
void clear()
{
_size = 0;
_str[_szie] = '\0';
}
istream& getline(istream& in ,string& s)
{
s.clear();
char ch;
//in >> ch;
ch = in.get( );
while(ch != '\n ')
{
s+= ch;
ch = in.get( );
}
return in;
}
private:
const char* _str = nullptr;
size_t _size = 0 ;
size_t _capacity = 0;
public:
static const int npos;
};
我赌你的枪里没有子弹!!!!
问题一:析构多次
【智能指针】引用计数:析构时检查引用计数,如果减完以后计数 == 0 代表当前是最后一个管理资源的对象,那就释放
问题二:一个修改会影响另一个
发生写时拷贝:检查引用计数,如果 == 1,说明资源是自己独占的,不用拷贝
如果大于1,先拷贝(深拷贝)再写,这就是写时拷贝
反正都要拷贝,这样做的意义是啥?
不是每个对象都要修改,我赌你不会修改