下面介绍string类最常用的接口,掌握以下基本就可以使用string类了,详细信息可以自己查询这个网站,string类接口详解,里面很全的,不过是全英文的。
#include
#include
using namespace std;
int main()
{
string s;
string s1("hellow");
string s2(10,'x');
string s3(s1);
return 0;
}
size和length完全一样,为了和其它容器的接口保持一致,所以引入的size。
例子:
1.输出字符串的长度以及空间大小
#include
#include
using namespace std;
int main()
{
string s("hollow world");
cout << s.size() << endl;
cout << s.capacity() << endl;
return 0;
}
运行结果:
capacity是它的容量,size/length是它有效字符的长度。
2. 判断上面的string类对象s是否为空?如果不为空就清空字符串,清空后再输出有效字符。
#include
#include
using namespace std;
int main()
{
string s("hollow world");
cout << s.size() << endl;
cout << s.capacity() << endl;
if (!s.empty())
s.clear();
cout << s.size() << endl;
return 0;
}
运行结果:
有效字符被清空,所以size为0,那容量会被情况嘛?不会,这个大家去验证,容量是不变的。
3.创建一个字符串s1,它的容量是100(capacity);再创建一个字符串s2它的有效字符数量是100(size)。
#include
#include
using namespace std;
int main()
{
string s1;
s1.reserve(100);
string s2;
s2.resize(100);
return 0;
}
通过调试来查看:
通过对比发现,reserve是调整容量;resize是调整有效字符的长度,而且默认初始为‘0’,也可以不初始为0,可以自己定义,比如:
s2.resize(100,'x');
这样就会被初始成‘x’。
注意
:
string是支持随机访问的,所以可以用[]来访问;也可以使用迭代器;范围for(语法糖)也是支持的,它的底层也是封装的迭代器。
所以综上有三种方式来访问string类的对象;
我们来一 一实现:
#include
#include
using namespace std;
int main()
{
string s("hellow world");
for (size_t i = 0; i < s.size(); i++)
{
cout << s[i];
}
return 0;
}
#include
#include
using namespace std;
int main()
{
string s("hellow world");
string::iterator it = s.begin();
while (it != s.end())
{
cout << *it;
++it;
}
cout<<endl;
string::reverse_iterator it1 = s.rbegin();
while (it1 != s.rend())
{
cout << *it1;
++it1;
}
return 0;
}
关于迭代器,这里先简单说,后面会有迭代器的实现;迭代器可以看作是返回指针的函数,begin返回的是s[0]的指针(第一个元素),end返回最后一个元素的指针;rbegin和rend则是和begin和end反着来的。所有的容器都支持迭代器,string我建议用[]下标的方式。
string的迭代器有:
begin和end
可以发现有两个重载,也就是有一个const版本,还有普通版本:
string::const_iterator it=s.begin();
string::const_iterator it=s.end();
rebgin和rend也都有两个版本:
就是这样,基本就可以使用迭代器了,用法比较简单,
看一下上面程序的运行结果:
一个正序打印,一个反序打印。
#include
#include
using namespace std;
int main()
{
string s("hellow world");
for (auto ch : s)
{
cout << ch;
}
return 0;
}
auto 类型的ch会自动的拷贝字符串s的每一个值,如果想用范围for去修改字符串只需要在ch前加一个&
,引用即可。
for (auto &ch : s)
{
ch+=1;
}
以上的函数,可以修改字符串的内容,比如往里面插入一个字符:头插,尾插,中间插;将string类对象转换为c语言字符串形式;查找某个字符;只截取字符串中的一部分;删除某个字符;
#include
#include
using namespace std;
int main()
{
string s;
s.insert(0, 1,'i');
return 0;
}
(2)尾插:push_back,append,+=,insert都可以完成操作;
#include
#include
using namespace std;
int main()
{
string s;
s.insert(0, 1,'i');
s.push_back('y');
s.append(1,'o');
s += 'u';
return 0;
}
push_back支持尾插一个字符,+=,append,insert支持插入n个字符(多个重载,下去可以看看);
(3)中间插入:只能用insert,在’y’前插入字符串"love";
#include
#include
using namespace std;
int main()
{
string s;
s.insert(0, 1,'i');
s.push_back('y');
s.append(1,'o');
s += 'u';
s.insert(1, "love");
return 0;
}
调试看一下,结果:
2. 返回c格式的字符串
明明都是字符串有啥可转换的?这就错了,c++中的那个是string类的对象,不是字符串;比如下面的操作,我们来看看会出现什么问题:
#include
#include
using namespace std;
int main()
{
string s("hellow");
const char* ps = s;
return 0;
}
#include
#include
using namespace std;
int main()
{
string s("hellow");
const char* ps = s.c_str();
cout << ps;
return 0;
}
erase(开始删除的位置,删除多少(默认是删除完));
<<
,>>
;也就是说我们可以用cin,向string类对象输入,输出也可以用cout:string sl;
cin>>sl;
cout<<sl;
但是需要注意一点,我们输入的时候,如果想要往string类对象中输入一个’ ‘空格,单纯用cin是不可以的,这可能会导致你刷题时出错,因为我们用cin输入的时候,输入’ ‘或者’\n’相当于结束本次输入;
解决方案:用getline就可以,它可以接受空格。
string sl;
getline(cin,sl);
class string
{
private:
char *_str;//指向存字符串的指针
size_t _size;//有效字符长度
size_t capicity;//容量
static const size_t nops;//静态成员nops,size_t无符号数,所以最大是-1(二进制全1)
}
const size_t string::nops = -1;//在类外初始化静态成员变量
string(const char* str="")
:_size(strlen(str))
, _capicity(strlen(str))
{
if(str==nullptr)
assert(false);
_str = new char[_size + 1];
strcpy(_str, str);
}
构造函数,默认传参为""
,注意这个字符串里面有\0
,不要以为里面是空的,有效字符为空,但是还有\0
;如果传来空指针,直接断言报错;_str指向一个和传来字符一样大的空间,再把内容拷贝进去。
调试验证:
~string()
{
if(_str)//判断_str是否为空
delete[] _str;
_size = _capicity = 0;
}
void Swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capicity, s._capicity);
}
//自己动手写
string(const string& str)
:_size(str._size),
_capicity(str._capicity)
{
if (_str)
delete[] _str;
_str = new char[str._size + 1];
strcpy(_str, str._str);
}
//复用构造函数
string(const string& s)
:_str(nullptr),
_size(0),
_capicity(0)
{
string tmp(s._str);
Swap(tmp);
}
这个拷贝构造,必须得写;默认得拷贝构造是浅拷贝,浅拷贝:按字节序拷贝。浅拷贝会导致_str和str._str指向同一个空间,在析构时会报错,因为一块空间不可以被析构多次。所以必须用深拷贝,深拷贝:给每个对象独立的空间,拷贝内容,不会共用一块空间。
可以看到上面所写的就是深拷贝,我先释放了原来所指的空间,再让它开辟一个和要拷贝对象中字符串一样的大小,最后拷贝内容就行了。
第二个版本是复用了构造函数,这是简单巧妙的写法。甚至不需要我们去手动的释放空间。我们构造了一个临时对象tmp,tmp的内容就是拷贝的传参来的_str。然后交换一下tmp和this->_str的内容就可以了。我们之前不是要手动的释放this->_str空间嘛,由于tmp交换了空间,而且它是临时对象,所以这个拷贝构造函数结束调用时,临时对象会自动的调用它的析构函数,也就释放了它的空间。
=
默认的=
重载也是浅拷贝,所以需要自己实现。
//传统版本
string& operator= (const string& str)
{
if (this != &str)
{
char* tmp = new char[str._size+ 1];
strcpy(tmp, str._str);
_str = tmp;
_size = str._size;
_capicity = str._capicity;
}
return *this;
}
//现代版本
string& operator=(string s)
{
Swap(s);
return *this;
}
传统版本:我创建了一个tmp,让它去开辟空间,并且拷贝内容,最后使.str=tmp,就可以了。当然上面的拷贝构造也可以用这样的方法。
现代版本:利用传值传参,会调用拷贝构造,也就是说s是传参的一份临时拷贝。然后只需要交换一下this和s,就可以,而且交换后,因为是临时拷贝,函数调用结束后就会自动的析构其空间。
随机访问上面也说过,就是三种方式:[],迭代器,范围for。我们都来实现一下:
(1)[]下标随机访问
char& operator[](const size_t pos)
{
assert(pos < this->_size);
return this->_str[pos];
}
(2)迭代器
在string里迭代器就是指针,但是不是所有的迭代器都是指针,比如list
typedef char* iterator;
typedef char* const_iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
(3)范围for就是利用的迭代器,上面写好迭代器后就可以使用,但是必须规范迭代器的函数比如:必须是begin,end这样的函数,如果写成Begin,End就不可以了,auto推不出来。
(1)reserve,调整容量;如果n>_capicity,那么需要扩大容量,我可以创建一个临时变量tmp,让它去开辟空间,并且把_str的字符拷贝到tmp中,然后把_str释放掉,最后用_str=tmp,_capicity=n,就完成了扩容。
void reserve(size_t n)
{
if (n > _capicity)
{
char* tmp = new char[n+1];
strcpy(tmp,_str);
delete[] _str;
_str = tmp;
_capicity = n;
}
}
(2)resize,调整有效字符的大小,这个分三种情况。具体实现如下:
void resize(size_t n, char c = '\0')
{
//有效字符变短
if (n <= _size)
{
_size = n;
_str[_size] = '\0';
}
//有效字符变长
else
{
//容量不够容纳,扩容
if (n > _capicity)
{
reserve(n + 1);
}
//将_str+_size后,n-_size长度都初始化成 c,之前的不动
memset(_str+_size, c, n -_size);
_size = n;
//末尾必须有‘\0’
_str[_size] = '\0';
}
}
(3)判断_str是否为空,查看容量,有效字符长度
//有效字符长度
size_t size()const
{
return _size;
}
//容量大小
size_t capacity()const
{
return _capicity;
}
// 判断是否为空
bool empty()const
{
if (_size == 0)
return true;
else
return false;
}
(1)push_back,尾部插入一个字符,先要判断是否需要扩容
void push_back(char c)
{
if (_size == _capicity)
{
reserve(_capicity == 0 ? 4 : 2 * _size);
}
_str[_size] = c;
_size++;
_str[_size] = '\0';
}
(2)append,尾部插入一个字符串,判断扩容,copy一下就ok了。
void append(const char* str)
{
int n = strlen(str);
if (_size + n > _capicity)
{
reserve(_size + n+1);
}
strcpy(_str + _size, str);
_size += n;
}
(3)运算符重载+=
,这个就简单了,直接复用上面的push_back,append.
string& operator+=(char c)
{
this->push_back(c);
return *this;
}
string& operator+=(const char* str)
{
this->append(str);
return *this;
}
(4)insert,插入字符,插入字符串都需实现
string& insert(char c, size_t pos=0)
{
assert(pos <= _size);
//扩容
if (_size == _capicity)
{
reserve(_capicity == 0 ? 4 : 2 * _size);
}
//挪动数据
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
end--;
}
_str[pos] = c;
++_size;
return *this;
}
string& insert(const char* str,size_t pos=0)
{
assert(pos <= _size);
int n = strlen(str);
if (_size+n > _capicity)
{
reserve(_size+n+1);
}
size_t end = _size + n;
while (end>pos+n)
{
_str[end] = _str[end - n];
end--;
}
strncpy(_str + pos, str, n);
return *this;
}
查找一个字符比较简单,从头找,找到后返回下标,找不到返回nops。
查找一个字符串的话,可以自己实现,也能用strstr来实现。
// 返回c在string中第一次出现的位置
size_t find(char c, size_t pos = 0) const
{
for (size_t i = pos; i < this->_size; i++)
{
if (_str[i] == c)
return i;
}
return nops;
}
// 返回子串s在string中第一次出现的位置
size_t find(const char* s, size_t pos = 0) const
{
const char* ptr = strstr(_str + pos, s);
if (ptr == nullptr)
{
return nops;
}
else
{
return ptr-_str;
}
}
(1)erase:
pos指的是删除某个位置,len代表删除多长的长度
string& erase(size_t pos=0, size_t len=nops)
{
assert(pos < _size);
if (len == nops || pos + len >= _size)
{
//pos后面的位置全删,直接pos位置放'\0'就好了
_str[pos] = '\0';
_size = pos;
}
else
{
//从后往前覆盖
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}
(2)clear,全部删除
void clear()
{
_str[0] = '\0';
_size = 0;
}
const char* c_str()const
{
return _str;
}
比大小的话,只需要实现俩个(<,= =) 或者 ( >,= =),然后复用就可以了。比如:我实现了<
和==
,那么>
就是<=
取反,>=
就是<
取反。
我们实现直接利用strcmp函数就可以,
根据其返回值如下:
//实现==
bool operator==(const string& s)
{
return strcmp(this->c_str(), s.c_str())==0;
}
//实现<,不想用strcmp也能实现,感兴趣的可以看看
bool operator<(const string& s)
{
/*size_t s1 = 0; size_t s2 = 0;
while (s1 < this->size() && s2 < s.size())
{
if ((*this)[s1] < s[s2])
{
return true;
}
else if((*this)[s1]>s[s2])
{
return false;
}
else
{
s1++;
s2++;
}
}
return s2 < s.size() ? true : false;*/
return strcmp(this->c_str(),s.c_str())<0;
}
接下来复用即可。
bool operator<=(const string& s)
{
return *this < s || *this == s;
}
bool operator>(const string& s)
{
return !(*this <= s);
}
bool operator>=(const string& s)
{
return !(*this < s);
}
bool operator!=(const string& s)
{
return !(*this == s);
}
<<
和>>
想要直接用cout输出string,用cin直接向string输入;那就需要重载<<和>>。
//直接定义在类里,友元函数,
//还得用std里的cin和cout
friend std::ostream& operator<<(std::ostream& _cout, const bit::string& s)
{
//自己写
/*for (size_t i = 0; i < s.size(); i++)
{
_cout << s[i];
}*/
//用auto省事
for (auto ch : s)
{
_cout << ch;
}
return _cout;
}
friend std::istream& operator>>(std::istream& _cin, bit::string& s)
{
s.clear();
char ch=_cin.get();
while (ch != ' '&& ch != '\n')
{
s += ch;
ch = _cin.get();
}
return _cin;
}
namespace ly
{
class string
{
friend std::ostream& operator<<(std::ostream& _cout, const ly::string& s)
{
//自己写
/*for (size_t i = 0; i < s.size(); i++)
{
_cout << s[i];
}*/
//用auto省事
for (auto ch : s)
{
_cout << ch;
}
return _cout;
}
friend std::istream& operator>>(std::istream& _cin, ly::string& s)
{
s.clear();
char ch=_cin.get();
while (ch != ' '&& ch != '\n')
{
s += ch;
ch = _cin.get();
}
return _cin;
}
public:
typedef char* iterator;
typedef char* const_iterator;
public:
void Swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capicity, s._capicity);
}
string(const char* str = "")
:_size (strlen(str)),
_capicity(strlen(str))
{
_str = new char[_capicity+1];
strcpy(_str, str);
}
string(const string& s)
:_str(nullptr),
_size(0),
_capicity(0)
{
string tmp(s._str);
Swap(tmp);
}
string& operator=(string s)
{
Swap(s);
return *this;
}
~string()
{
delete[] _str;
_str = nullptr;
_size = _capicity = 0;
}
//
// iterator
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
/
// modify
void push_back(char c)
{
if (_size == _capicity)
{
reserve(_capicity == 0 ? 4 : 2 * _size);
}
_str[_size] = c;
_size++;
_str[_size] = '\0';
}
string& operator+=(char c)
{
this->push_back(c);
return *this;
}
void append(const char* str)
{
int n = strlen(str);
if (_size + n > _capicity)
{
reserve(_size + n+1);
}
strcpy(_str + _size, str);
_size += n;
}
string& operator+=(const char* str)
{
this->append(str);
return *this;
}
void clear()
{
_str[0] = '\0';
_size = 0;
}
const char* c_str()const
{
return _str;
}
/
// capacity
size_t size()const
{
return _size;
}
size_t capacity()const
{
return _capicity;
}
bool empty()const
{
if (_size == 0)
return true;
else
return false;
}
void resize(size_t n, char c = '\0')
{
if (n <= _size)
{
_size = n;
_str[_size] = '\0';
}
else
{
if (n > _capicity)
{
reserve(n + 1);
}
memset(_str+_size, c, n - _size);
_size = n;
_str[_size] = '\0';
}
}
void reserve(size_t n)
{
if (n > _capicity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capicity = n;
}
}
/
// access
char& operator[](size_t index)
{
return _str[index];
}
const char& operator[](size_t index)const
{
return _str[index];
}
/
//relational operators
bool operator<(const string& s)
{
/*size_t s1 = 0; size_t s2 = 0;
while (s1 < this->size() && s2 < s.size())
{
if ((*this)[s1] < s[s2])
{
return true;
}
else if((*this)[s1]>s[s2])
{
return false;
}
else
{
s1++;
s2++;
}
}
return s2 < s.size() ? true : false;*/
return strcmp(this->c_str(),s.c_str())<0;
}
bool operator<=(const string& s)
{
return *this < s || *this == s;
}
bool operator>(const string& s)
{
return !(*this <= s);
}
bool operator>=(const string& s)
{
return !(*this < s);
}
bool operator==(const string& s)
{
return strcmp(this->c_str(), s.c_str())==0;
}
bool operator!=(const string& s)
{
return !(*this == s);
}
// 返回c在string中第一次出现的位置
size_t find(char c, size_t pos = 0) const
{
for (size_t i = pos; i < this->_size; i++)
{
if (_str[i] == c)
return i;
}
return nops;
}
// 返回子串s在string中第一次出现的位置
size_t find(const char* s, size_t pos = 0) const
{
const char* ptr = strstr(_str + pos, s);
if (ptr == nullptr)
{
return nops;
}
else
{
return ptr-_str;
}
}
// 在pos位置上插入字符c/字符串str,并返回该字符的位置
string& insert(char c, size_t pos)
{
assert(pos <= _size);
if (_size == _capicity)
{
reserve(_capicity == 0 ? 4 : 2 * _size);
}
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
end--;
}
_str[pos] = c;
++_size;
return *this;
}
string& insert(const char* str,size_t pos)
{
assert(pos <= _size);
int n = strlen(str);
if (_size+n > _capicity)
{
reserve(_size+n+1);
}
size_t end = _size + n;
while (end>pos+n)
{
_str[end] = _str[end - n];
end--;
}
strncpy(_str + pos, str, n);
return *this;
}
// 删除pos位置上的元素,并返回该元素的下一个位置
string& erase(size_t pos=0, size_t len=nops)
{
assert(pos < _size);
if (len == nops || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}
private:
char* _str;
size_t _capicity;
size_t _size;
static const size_t nops;
};
const size_t string::nops = -1;
};
面试当中写string,不需要完成太多的功能。string中只要有一个char *就可以了,不需要size,capicity,只需保证我们构造的string末尾有\0
。而且考虑实现操作:构造、析构、拷贝构造、赋值。这四个操作就满足了。
#include
#include
class String
{
public:
String()
: data_(new char[1])
{
*data_ = '\0';
}
String(const char* str)
: data_(new char[strlen(str) + 1])
{
strcpy(data_, str);
}
String(const String& rhs)
: data_(new char[rhs.size() + 1])
{
strcpy(data_, rhs.c_str());
}
/* Delegate constructor in C++11
String(const String& rhs)
: String(rhs.data_)
{
}
*/
~String()
{
delete[] data_;
}
/* Traditional:
String& operator=(const String& rhs)
{
String tmp(rhs);
swap(tmp);
return *this;
}
*/
String& operator=(String rhs) // yes, pass-by-value
{
swap(rhs);
return *this;
}
// C++ 11
String(String&& rhs)
: data_(rhs.data_)
{
rhs.data_ = nullptr;
}
String& operator=(String&& rhs)
{
swap(rhs);
return *this;
}
// Accessors
size_t size() const
{
return strlen(data_);
}
const char* c_str() const
{
return data_;
}
void swap(String& rhs)
{
std::swap(data_, rhs.data_);
}
private:
char* data_;
};
结尾语: 以上就是本篇string的内容,祝大家早日拿offer。