string类
string支持流插入和流提取
string初始化
赋值符的重载
遍历string
string的大小:
两个长度没有任何不同
第一种
(写两个函数重载是为了防止字符串为const)
使用引用返回就可以修改得到的变量的值
第二种:迭代器
迭代器iterator
迭代器是一个类似于指针的东西(不一定是指针)
begin返回第一个位置的指针,end返回最后一个字符的下一个位置的指针
遍历方式
迭代器相比于下标访问的优越性
不是所有东西都支持下标访问,但是都支持迭代器
迭代器是一个主流的通用的方法
第三种:范围for
所有结构都支持范围for,因为他的底层就是迭代器
auto后加&可以就可以遍历修改数据了
范围for遍历自动取数据,自动++,自动判断结束
但是,如果类型是const string,就不能用上述遍历修改了
begin存在两种返回数据
反向迭代器
反向迭代器存在rbegin和rend
他的目的是反向遍历
他的++和--是相反的,
++向后走,--向前走
算法:数据排序
参数:起始和结束位置,左闭右开(即传入最后一个的下一个位置)
插入和修改
1.
单个字符
2.
字符串
3.
assign的本质是一种变相的赋值
insert
insert是插入
insert应该慎用,因为效率不高(O(N))
erase
erase是删除
同样慎用,效率不高
如果结束位置较大或不传,则全部删完后停止
但是注意第一个参数pos不能越界
replace
replace的作用是替换
string的部分接口
string的扩容
起初是存在临时数组buf中,buf空间不够,数据会转移到堆中
在vs下的扩容
可以是1.5倍扩容,也可以是两倍扩容
在Linux下的扩容
对于字符串的存储,如果字符串大小小于16
会存储在临时生成的一个数组buf中
如果字符串大于16
就不会存储在buf中,转而存储在堆中
resize和reserve
resize影响size和capacity
reserve只影响capacity
reserve
reserve的作用是扩容
vs下
可以看到申请扩容100,但是实际上扩容了111
linux下
而linux下则是正常的100
同时reserve不仅可以用于扩容,还可以用于缩容
(小于15就会缩,大于等于16,不缩)
但是Linux会缩容
注意,reserve只是扩容(改变capacity),不改变size,导致不能直接用下标访问
resize
resize会改变size
eg.
如果改变后的size大于capacity,则扩容
改变size后会删除多余的字符串
缩容
由于空间不能分段释放,所以缩容的本质是开辟一块更小的空间,然后将数据拷贝
是时间换空间
at
访问pos位置的字符
string operations
c_str
获取指向字符串的指针(首元素的地址)
可以和c语言更好的兼容
find和rfind
find是正着找,rfind是反着找
获取子串
find_first_of
查找aeiou中的任意一个字符
时间复杂度是m*n
operator +
模拟实现
#pragma once
#include <iostream>
#include<assert.h>
using namespace std;
namespace bit
{
class string
{
public:
//迭代器
typedef char* iterator;//封装,屏蔽了底层实现的细节
typedef const char* const_iterator;
//const防止数据被修改
//提供了一种简单通用的访问容器的方式
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
//MyString();
string(const char* str = "");//给缺省值,减少无参数构造
~string();
/*string& operator=(const string& s);*/
string& operator=(string tmp);
string(const string& s);
const char* c_str() const;
size_t size() const;
char& operator[](size_t pos);
const char& operator[](size_t pos) const;
void reserve(size_t n);
void push_back(char ch);
void append(const char* str);
string& operator+=(char ch);
string& operator+=(const char* str);
void insert(size_t pos, char ch);
void insert(size_t pos, const char* str);
void erase(size_t pos = 0, size_t len = npos);
size_t find(char ch, size_t pos = 0);
size_t find(const char* str, size_t pos = 0);
void swap(string& s);
string substr(size_t pos = 0, size_t len = npos);
bool operator<(const string& s) const;
bool operator>(const string& s) const;
bool operator<=(const string& s) const;
bool operator>=(const string& s) const;
bool operator==(const string& s) const;
bool operator!=(const string& s) const;
void clear();
private:
char* _str;
size_t _size;
size_t _capacity;
const static size_t npos;
//静态成员变量,属于整个类
};
istream& operator>> (istream& is, string& str);
ostream& operator<< (ostream& os, const string& str);
}
#include "MyString.h"
//MyString::MyString()
//{
// _str = new char[1] {'\0'};
// _size = 0;
// _capacity = 0;
//}
namespace bit
{
const static size_t npos = -1;
string::string(const char* str)
:_size(strlen(str))
//初始化列表,类尽量在初始化列表中初始化
//由于strlen效率较低,因此尽量避免函数的重复使用
//可以将初始化列表和函数体结合使用
{
_str = new char[_size + 1];
_capacity = _size;
strcpy(_str, str);
}
string::~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
//传统写法
//string::string(const string& s)//拷贝构造
//{
// _str = new char[s._capacity + 1];
// strcpy(_str, s._str);
// _size = s._size;
// _capacity = s._capacity;
//}
//现代写法
string::string(const string& s)
{
string tmp(s._str);//利用构造函数构造一个一样的对象,然后交换
//方法1
/*std::swap(tmp._str, _str);
std::swap(tmp._size, _size);
std::swap(tmp._capacity, _capacity);*/
//方法2
swap(tmp);//调用自己的swap,默认的第一个参数是*this
}
//传统写法
/*string& 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;
}
}*/
//现代写法
// 方法1
//string& string::operator=(const string& s)
//{
// if (this != &s)
// {
// string tmp(s._str);
// swap(tmp);
// }
// return *this;
//}
//方法2
string& string::operator=(string tmp)//在参数中拷贝构造
{
swap(tmp);//和拷贝交换
return *this;
}
const char* string::c_str() const
//类的成员函数后面加 const,表明这个函数不会对这个类对象的数据成员(准确地说是非静态数据成员)作任何改变。
{
return _str;
}
size_t string::size() const
{
return _size;
}
char& string::operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
string::iterator string::begin()
{
return _str;
}
string::iterator string::end()
{
return _str + _size;
}
string::const_iterator string::begin() const
{
return _str;
}
string::const_iterator string::end() const
{
return _str + _size;
}
const char& string::operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
void string::reserve(size_t n)//扩容
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete _str;
_str = tmp;
_capacity = n;
}
}
void string::push_back(char ch)//插入单个字符
{
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
_str[_size + 1] = '\0';
++_size;
}
void string::append(const char* str)//插入字符串
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str + _size, _str);
_size += len;
}
string& string::operator+=(char ch)
{
push_back(ch);
return *this;
}
string& string::operator+=(const char* str)
{
append(str);
return *this;
}
void string::insert(size_t pos, char ch)
{
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
size_t end = _size + 1;
while (end > pos)//后移一位
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = ch;
++_size;
}
void string::insert(size_t pos, const char* str)
{
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;
}
memcpy(_str + pos, str, len);
_size += len;
}
void string::erase(size_t pos, size_t len = npos)
{
assert(pos < _size);
if (len >= _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
}
size_t string::find(char ch, size_t pos)
{
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
size_t string::find(const char* sub, size_t pos)
{
const char* p = strstr(_str + pos, sub);//找到字串并返回下标
return p - _str;
}
void string::swap(string& s)
{
std:: swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
//获取特定字串
string string::substr(size_t pos, size_t len = npos)
{
if (len > _size - pos)
{
string sub(_str + pos);
return sub;
}
else
{
string sub;
sub.reserve(len);
for (size_t i = 0; i < len; i++)
{
sub += _str[pos + i];
}
return sub;
}
}
bool string::operator<(const string& s) const
{
return strcmp(_str, s._str) < 0;
}
bool string::operator>(const string& s) const
{
return !(*this <= s);
}
bool string::operator<=(const string& s) const
{
return *this < s || *this == s;
}
bool string::operator>=(const string& s) const
{
return !(*this < s);
}
bool string::operator==(const string& s) const
{
return strcmp(_str, s._str) == 0;
}
bool string::operator!=(const string& s) const
{
return !(*this == s);
}
istream& operator>> (istream& is, string& str)
{
//当输入一段很长的字符串时
//由于ch是单个字符,不断+=就需要不断扩容,导致浪费
//而提前reserve又可能会导致空间的大量浪费
//方法1
//str.clear();
//char ch = is.get();
//
//while (ch != ' ' && ch != '\n')
//{
// str += ch;
// ch = is.get();
//}
//return is;
//
//方法2
//临时数组
str.clear();
char buff[128];
int i = 0;
char ch = is.get();
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
//0-126存有效字符
if (i == 127)
{
buff[i] = '\0';
str += buff;
i = 0;
}
ch = is.get();
if (i != 0)
{
buff[i] = '\0';
str += buff;
}
}
return is;
}
void string::clear()
{
_str[0] = '\0';
_size = 0;
}
ostream& operator<< (ostream& os, const string& str)
{
for (size_t i = 0; i < str.size(); i++)
{
os << str[i];
}
return os;
}
}