目录
1.什么为String类
在c语言中我们在对字符对象进行操作的使用时候常常会注意到字符或者字符串对象都是以'\0'为结尾的,我们想要进行一个字符串的遍历以及增删查改,我们需要创建一个字符数组再来进行操作。这样好像创建一个对象再来进行操作,每次都会消耗我们时间。然而在C++中,之所以说是面向对象的编程,就是我们可以直接可以对STL里面的每个容器进行操作。本章主要介绍String类的使用方法。
2.标准库中的String类
这是String类的官方介绍,我们通过一些例子会对String有进一步的了解。
2.1String类的常用接口说明
1.string类对象的常见构造
constructor | 构造string对象 |
string() | 构造一个空的String对象,就是空字符串 |
String(const char*s) | 构造函数初始化 |
string(size_t n,char c) | string对象中前n元素都为字符c |
string(const string&s) | 拷贝构造函数 |
//构造函数初始化,这里我们用到初始化列表来对
//构造函数进行初始化,
string(const char* str="")
:_size(strlen(str))
,_capacity(_size)
{
_str = new char[_capacity + 1];
strcpy(_str, str);
}
int main()
{
string s1("hello world!");
string s2(s1); //使用s1对象来拷贝一个即将生成的S2对象。
string s3("sort");
}
这里在初始化列表给的都是空值,而到了里面的我们需要多给出一个字符的位置来存放’\0‘;
2.string类对象的容量操作
size | 返回字符串有效长度哦不包括’\0‘(用法同length两者可以互换) |
capacity | 返回空间总大小 |
empty | 检查字符串空间是否为空,空返回true,否则返回false |
clear | 清除有效字符 |
reserve | 相当于对字符串空间进行增容。 |
resize | 将字符串的有效字符改为n个,多出的字符由给定的值确定 |
length | 与size用法相同 |
3.string对象的遍历操作
函数名称 | 功能说明 |
operator【】 | 返回pos位置元素,const string 类对象调用 |
begin+end | 获取一个字符的迭代器到end的下一个位置的迭代器 |
rbegin+rend | 可以立即为反向迭代器 |
for(auto e:s1) | 语法糖,这个称之为语法糖因为用起来很甜。基于迭代器 |
- 遍历方式,string遵循的三种遍历方式。
cout << s1[9] << endl;
for (auto e : s1)
{
cout << e << " ";
}
string::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
it++;
}
4.对string对象元素修改操作
函数名称 | 功能说明 |
push_back | 尾部插入一个字符 |
append | 尾部追加一个字符串 |
operator+= | 在原有字符串追加一个字符串str返回被追加的对象 |
c_str | 返回c格式字符串 |
fing+npos | 从pos位置往后找,返回该字符在字符串中的位置 |
rfind | 从pos位置向前找,返回字符位置 |
substr | 在str中从pos位置开始,截取n个字符然后将其返回 |
以上就是string对象的一些常用的函数介绍了~。
3.string对象的模拟实现
3.1类成员介绍
通过我们以上对string里面函介绍,猜你应该不难想到这么方便一个对象它是怎么实现的呢?其实它的显示就是一个顺序表,因为它还支持随即方访问, 只不过在原有的随机访问上增加了迭代器。
模拟实现中的成员变量
private:
char* _str;
int _size;
int _capacity;
我们可以看到string的实现就还是使用顺序表的方式来整的。不过为一个动态增容的。嗯~~~
我们这里主要介绍一下拷贝构造和赋值的现代比较先进的写法吧,我们先看一下下面这两段拷贝构造的代码分析一下它们有什么不同
//拷贝构造s3(s1)
string(const string& s)
:_size(s._size)
,_capacity(s._capacity)
{
_str = new char[_capacity + 1];
strcpy(_str, s._str);
}
//现代写法完成深拷贝
string(const string& s)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
string tmp(s._str);
Swap(tmp);
}
以上两端代码都没有问题,但是这里需要注意的是,看似简单的代码其实涉及到了深浅拷贝的问题。浅拷贝问题及是我们在用S1去拷贝构造S2时并没有给S2合适的空间和容量,只是把S1的值拷贝给S2其实这样S2里面的指针就也会指向和S1相同的地址来进行遍历,不过在释放的时候,系统会向将S2所指向的空间释放,然而S1却还指向S2所指向的空间,这样会导致相同的一块空间被重复释放。所以我们在初始化列表就对S2进行深拷贝。那么下面这个现代写法深拷贝进去把要被构造的对象全部初始化为空,然后创建一临时对象tmp但是这个临时对象tmp会先去调用它自己的构造函数,然后再构造函数里面使用传过去的s._str来进行初始化,然后回来和this指针进行交换,在交换过后又调用析构函数tmp对象被析构。
赋值构造
string& operator=(string& s)
{
//传统写法赋值
delete[]_str;
_str = new char[s._capacity+1];
_size = s._size;
_capacity = s._capacity;
strcpy(_str, s._str);
//现代写法赋值
Swap(s);
return *this;
//这个是不可以的!!
delete[]_str;
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
return *this;
}
这里现代写法也是使用了交换两个对象的内容然后出来调用析构函数。很方便的现代写法
4.string模拟实现全代码
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<assert.h>
using namespace std;
namespace qzh
{
class string
{
friend bool operator<(const string& s1, const string& s2);
public:
//迭代器
typedef char* interator;
interator begin()
{
return _str;
}
interator end()
{
return _str + _size;
}
//构造函数初始化
string(const char* str="")
:_size(strlen(str))
,_capacity(_size)
{
_str = new char[_capacity + 1];
strcpy(_str, str);
}
拷贝构造s3(s1)
//string(const string& s)
// :_size(strlen(s._str))
// ,_capacity(s._capacity)
//{
// _str = new char[s._capacity + 1];
// strcpy(_str, s._str);
//}
void Swap(string&s)
{
swap(_str, s._str);
swap(_size, s._size);
swap(_capacity, s._capacity);
}
//现代写法完成深拷贝
string(const string& s)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
string tmp(s._str);
Swap(tmp);
}
//赋值s3=s1
string& operator=(string& s)
{
//传统写法赋值
/*delete[]_str;
_str = new char[s._capacity+1];
_size = s._size;
_capacity = s._capacity;
strcpy(_str, s._str);*/
//现代写法赋值
Swap(s);
return *this;
//这个是不可以的!!
/*delete[]_str;
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
return *this;*/
}
const char* c_str() const
{
return _str;
}
int size() const
{
return _size;
}
char& operator[](int pos)
{
assert(pos <_size);
return _str[pos];
}
//扩容
void resvers(int n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[]_str;
_str = tmp;
_capacity = n;
}
}
void resize(int n, const char ch = '\0')
{
if (n <= _size)
{
_str[n] = '\0';
_size = n;
}
else if(n>_capacity)
{
resvers(n);
}
memset(_str + _size, ch, n - _size);
_size = n;
_str[_size] = '\0';
}
int find(const char ch)
{
for (int i = 0; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return -1;
}
//某个位置插入一个字符或者字符串
string& insert(int pos,const char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
resvers(_capacity==0?4:_capacity*2);
}
size_t end = _size;
while (pos <end)
{
_str[end + 1] = _str[end];
end--;
}
_str[end + 1] = _str[end];
end--;
_str[pos] = ch;
_size++;
return *this;
}
//某个位置插入一个字符串
string& insert(int pos, const char* s)
{
assert(pos <= _size);
int len = strlen(s);
if (_size+len>_capacity)
{
resvers(_size+len);
}
int end = _size+len;
while (pos+len <= end)
{
_str[end] = _str[end - len];
--end;
}
strncpy(_str+pos, s, len);
_size+=len;
return *this;
}
/*string& erase(int pos = 0, int len = npos)
{
assert(pos < _size);
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}*/
//从尾部插入一个字符
void pushback(const char ch)
{
if (_size == _capacity)
{
resvers(_capacity==0?4:_capacity * 2);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
}
//尾部插入一个字符串
void append(const char* s)
{
int len = strlen(s);
if (_size +len>_capacity)
{
resvers(_size + len);
}
strcpy(_str + len, s);
_size += len;
}
string& operator+=(const char* s)
{
append(s);
return *this;
}
~string()
{
delete[]_str;
_str = nullptr;
_size = _capacity = 0;
}
private:
char* _str;
int _size;
int _capacity;
public:
//static const size_t npos;
};
~~~~~~~~~~~~~