目录
1. STL
在介绍 string 之前,先来了解一下 STL 吧
STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。有了它,许多底层的数据结构以及算法都不需要自己重新造轮子,站在前人的肩膀上,健步如飞的快速开发。
2. string类
2.1 概述
在C语言中,字符串可以定义为以'\0'结尾的字符数组。在C++有了类的概念,为了更加安全有效的使用字符串,有了string字符串类。可以进入这里瞅瞅。
2.2 常用接口
函数名称 | 功能说明 |
string() | 构造空的string类对象,即空字符串 |
string(const char* s) | 用C-string来构造string类对象 |
string(size_t n, char c) | string类对象中包含n个字符c |
string(const string&s) | 拷贝构造函数 |
string(const string&s, size_t n) | 用s中的前n个字符构造新的string类对象 |
//string() 构造空的string类对象,即空字符串
string s1;
// string(const char* s) 用C - string来构造string类对象
string s2("hello string!");
// string(size_t n, char c) string类对象中包含n个字符c
string s3(5, 'b');
// string(const string&s) 拷贝构造函数
string s4(s2);
// string(const string&s, size_t n) 用s中的前n个字符构造新的string类对象
string s5(s4, 5);
函数名称 | 功能说明 |
size_t size() const | 返回字符串有效字符长度 |
size_t length() const | 返回字符串有效字符长度 |
size_t capacity ( ) const | 返回空间总大小 |
bool empty ( ) const | 检测字符串释放为空串,是返回true,否则返回false |
void clear() | 清空有效字符 |
void resize ( size_t n, char c ) | 将有效字符的个数该成n个,多出的空间用字符c填充 |
void resize ( size_t n ) | 将有效字符的个数改成n个,多出的空间用0填充 |
void reserve ( size_t res_arg=0 ) | 为字符串预留空间 |
string s1("hello string!");
//size_t size() const 返回字符串有效字符长度
cout << "size_t size() const:" << s1.size() << endl;
// size_t length() const 返回字符串有效字符长度
cout << "size_t length() const:" << s1.length() << endl;
// size_t capacity() const 返回空间总大小
cout << "size_t capacity() const:" << s1.capacity() << endl;
// bool empty() const 检测字符串释放为空串,是返回true,否则返回false
cout << "bool empty() const:" << s1.empty() << endl;
// void clear() 清空有效字符
s1.clear();
cout << "void clear()" << endl;
s1 = "hello string!";
// void resize(size_t n, char c) 将有效字符的个数该成n个,多出的空间用字符c填充
cout <<"s1已恢复:s1 =="<< s1 << endl;
s1.resize(5, 'b');
cout << "void resize(size_t n == 5, char c):" << s1 << endl;
// void resize(size_t n) 将有效字符的个数改成n个,多出的空间用0填充
s1.resize(5);
cout << "void resize(size_t n == 5):" << s1 << endl;
// void reserve(size_t res_arg = 0) 为字符串预留空间
cout << "当前容量:" << s1.capacity() << endl;
s1.reserve(16);
cout << "void reserve(size_t res_arg = 16):" << s1.capacity() << endl;
(1)size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
(2)clear()只是将string中有效字符清空,不改变底层空间大小。
(3)resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。(4)reserve(size_t res_arg=0):当string的有效字符小于15个的时候,string的容量是15。容量!=size。且是二倍增加在额外多一些预留空间。
函数名称 | 功能说明 |
begin | 返回迭代器的开始 |
end | 返回迭代器的结尾的位置 |
rbegin | 返回迭代器的结尾的前一个位置 |
rend | 返回迭代器开始的位置 |
string s1( "hello string!");
string::iterator sit = s1.begin();//s1最开始的位置
string::iterator sit2 = s1.end();//s1最后一个位置的后一个位置
cout << "hello string!" << endl;
cout << "string::iterator:" << endl;
while (sit != sit2)//遍历字符串
{
cout << *sit << " ";
++sit;
}
cout << endl;
string::reverse_iterator sit3 = s1.rbegin();//s1最后一个位置
string::reverse_iterator sit4 = s1.rend();//s1第一个位置
cout << "string::reverse_iterator:" << endl;
while (sit3 != sit4)
{
cout << *sit3<< " ";
++sit3;//虽然是最后一个位置,还是++操作,底层进行了重载
}
cout << endl;
(1)迭代器不是指针,是类模板,表现的像指针
(2)迭代器返回的是对象引用而不是对象的值
(3)指针其实是狭义的迭代器,迭代器是指针的抽象
(4)注意迭代器失效的问题。迭代器只是指向当前的空间,如果空间发生改变,迭代器要重新获取。
后面凡是提到迭代器都要注意迭代器失效的问题
string str1("iterator");//创建一个字符串
string::iterator sit = str1.begin();//这时sit == 'i'
str1 += "useless!!!";//这时我追加了一串字符串,肯定要扩容的。初始容量只有15字节
cout << *sit << endl;//出现错误,这便是迭代器失效,sit指向原来的空间呢。
函数名称 | 功能说明 |
void push_back(char c) | 在字符串后尾插字符c |
string& append (const char* s); | 在字符串后追加一个字符串 |
string& operator+=(const string&str) | 在字符串后追加字符串str |
string& operator+=(const char* s) | 在字符串后追加C个数字符串 |
string& operator+=(char c) | 在字符串后追加字符c |
const char* c_str( )const | 返回C格式字符串 |
size_t find (char c, size_t pos = 0)const | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
size_t rfind(char c, size_t pos = npos) | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 |
string substr(size_t pos = 0, size_t n= npos)const | 在str中从pos位置开始,截取n个字符,然后将其返回 |
string str("hello change!");
//void push_back(char c) 在字符串后尾插字符c
str.push_back('!');
cout <<"在字符串后尾插字符'!':" <<str << endl;
//string& append(const char* s); 在字符串后追加一个字符串
string s2("hey");
str.append(s2);
cout << "在字符串后追加一个字符串:" << str << endl;
//string& operator+=(const string str) 在字符串后追加字符串str
str += s2;
cout << "在字符串后追加一个字符串(=+):" << str << endl;
// string& operator+=(const char* s) 在字符串后追加C个数字符串
str +="string";
cout << "在字符串后追加C个数字符串:" << str << endl;
// string& operator+=(char c) 在字符串后追加字符c
str += 's';
cout << "在字符串后追加字符c:" << str << endl;
// const char* c_str()const 返回C格式字符串
const char* ch = str.c_str();
cout << "返回C格式字符串:" << ch << endl;
// size_t find(char c, size_t pos = 0)const 从字符串pos位置开始往后找字符c,返回该字符在 字符串中的位置
size_t pos = str.find('s', 0);
cout << "回该's'在str中的位置:" << pos << endl;
// size_t rfind(char c, size_t pos = npos) 从字符串pos位置开始往前找字符c,返回该字符在 字符串中的位置
size_t pos2 = str.rfind('s', str.size());
cout << "回该's'在str中的位置:" << pos2 << endl;
// string substr(size_t pos = 0, size_t n = npos)const 在str中从pos位置开始,截取n个字符,然后将其返回
string sub = str.substr(0, 5);
cout << "在str中从0位置开始,截取5个字符" << sub << endl;
3. string的模拟实现
要想对接口有熟练的运用,模拟实现它是最好的理解方式了。
#pragma once
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
class newString
{
private://放前面,先认识其私有成员
char* str_; //指针
size_t size_; //有效字符串长度
size_t capacity_; //实际申请的空间大小
static const size_t npos; //静态成员。后面重载的时候会用到
public:
typedef char* iterator;
typedef const char* const_iterator;
//全缺省构造函数
newString(const char* str = "")//显示定义构造函数
{
//不能是空指针
if (nullptr == str)
{
assert(false);
return;
}
size_ = strlen(str);
capacity_ = size_;
str_ = new char[size_ + 1];
strcpy(str_, str);
}
//析构函数
~newString()
{
if (str_)
{
size_ = 0;
capacity_ = 0;
delete[] str_;
str_ = nullptr;
}
}
//交换函数
void Swap(newString& str)
{
swap(str_, str.str_);
swap(size_, str.size_);
swap(capacity_, str.capacity_);
}
//拷贝构造
newString(const newString& str)//引用
:str_(nullptr),
size_(0),
capacity_(0)
{ //创建临时对象
newString tmp(str.str_);//创建临时对象是因为不能直接交换指针
Swap(tmp); //直接交换指针,被拷贝的对象的内容就变了
}
//重载运算符 = &引用的目的:连续赋值
newString& operator=(newString str) //这里有一个临时对象
{
Swap(str);
}
//这是改变容量的大小capacity_。
void Reserve(size_t newcapacity)
{
if (newcapacity > capacity_)
{
char* tmp = new char[newcapacity + 1];
strcpy(tmp, str_);
delete[] str_; //释放原来的空间
str_ = tmp;
capacity_ = newcapacity;
}
}
//添加字符
void PushBack(char c)
{
//检查空间是否足够
if (size_ == capacity_)
{ //因为string类开始的容量是15
size_t newcapacity = (size_ == 0 ? 15 : capacity_ * 2);
Reserve(newcapacity);
}
str_[size_++] = c;
str_[size_] = '\0';
}
//插入字符串
void Append(const char* str)
{
size_t leng = strlen(str);
if (size_ + leng >= capacity_)
{
size_t newcapacity = (size_ + leng <= 15 ? 15 : size_ + leng);
Reserve(newcapacity);
}
strcpy(str_ + leng, str);
size_ += leng;
}
// 在pos位置上插入字符c/字符串str,并返回该字符的位置
newString& Insert(size_t pos, char c)
{
assert(pos <= size_);
if (size_ == capacity_)
{
size_t newcapacity = (size_ == 0 ? 15 : capacity_ * 2);
Reserve(newcapacity);
}
size_t end = size_;
while (pos < end)
{
str_[end] = str_[end - 1];
--end;
}
str_[pos] = c;
++size_;
return *this;
}
newString& Insert(size_t pos, const char* str)
{
assert(pos <= size_);
size_t len = strlen(str);
if (size_ + len >= capacity_)
{
size_t newcapacity = (size_ + len <= 15 ? 15 : size_ + len);
Reserve(newcapacity);
}
size_t end = size_ + len;
while (pos + len - 1 < end)
{
str_[end] = str_[end - len];
--end;
}
for (size_t i = 0; i < len; ++i)
{
str_[pos] = str[i];
}
size_ += len;
return *this;
}
//尾删
void PopBack()
{
if (size_ > 0)
{
--size_;
}
}
//删除函数
void Erase(size_t pos, size_t len)
{
assert(pos < size_);
if (pos + len >= size_)
{
size_ = pos;
str_[pos] = '\0';//pos后面的字符串
}
else
{
for (size_t i = pos + len; i <= size_; i++)
{
str_[pos++] = str_[i];
}
size_ = pos - 1;
}
}
// 返回c在newString中第一次出现的位置
size_t Find(char c, size_t pos = 0) const
{
while (pos < size_)
{
if (str_[pos] == c)
{
return pos;
}
++pos;
}
return npos;
}
// 返回子串s在newString中第一次出现的位置
// 判断字串
char* SubStr( char* str)
{
char* dst = str_;
char* src = str;
while (*dst)
{
if (*dst == *src)
{
char* mathdst = dst + 1;
char* mathsrc = src + 1;
while (*mathdst && * mathsrc)
{
if (*mathdst != *mathsrc)
{
return 0;
}
++mathdst;
++mathsrc;
}
if (mathsrc == '\0')
{
return dst;
}
}
++dst;
}
return 0;
}
size_t Find(const char* str, size_t pos = 0) const
{
char* res = SubStr(str);
}
// 截取newString从pos位置开始的n个字符
newString SubStr(size_t pos, size_t n);
void Clear()
{
size_ = 0;
str_[size_] = '\0';
}
//改变size
// n <= size_ ; 修改size_ = n, str[n] = '\n'
// size_ < n <= capacity_; size_ = n ; str[n] = '\n' 原来未用的空间按用字符填补
// n > capacity_ 进行扩容
void Resize(size_t newsize)
{
if (newsize > capacity_)
{
Reserve(newsize);
}
if (newsize > size_)
{
memset(str_ + size_, '\0', newsize - size_);
}
size_ = newsize;
str_[size_] = '\0';
}
//字符串长度
size_t Size() const
{
return size_;
}
//迭代器
const_iterator begin() const
{
return str_;
}
const_iterator end() const
{
return str_ + size_;
}
//各类重载
char& operator[](size_t pos)
{
assert(pos < size_);
return str_[pos];
}
const char& operator[](size_t pos) const
{
assert(pos < size_);
return str_[pos];
}
newString& operator+=(const char* str)
{
//size_t leng = strlen(str);
//if (this->size_ + leng > capacity_)
//{
// Reserve(this->size_ + leng);
//}
//strcpy(this->str_+ size_, str);
Append(str);
return *this;
}
newString& operator+=(const newString& str)
{
Append(str.str_);
return *this;
}
bool operator<(const newString& str)
{
if (strcmp(str_, str.str_) + 1)
{
return false;
}
return true;
}
bool operator<=(const newString& str)
{
return !(str_ > str.str_);
}
bool operator>(const newString& str)
{
if (strcmp(str_,str.str_))
{
return true;
}
return false;
}
bool operator>=(const newString& str)
{
return !(str_ < str.str_);
}
bool operator==(const newString& str)
{
return !((str_ > str.str_) || (str_ < str.str_));
}
bool operator!=(const newString& str)
{
return !(str_ == str.str_);
}
};
const size_t newString::npos = -1;