对string类的部分常用功能进行了简单的模拟实现:resize,reserve,查找,删除,插入,输入输出流重载等.然后与一步步系统的string接口进行比较测试,完成模拟实现。
常用接口:
函数 | 功能 |
---|---|
构造: | |
string() | 构造空的string类对象,即空字符串 |
string(const char* s) | 用C-string来构造string类对象 |
string(size_t n, char c) | string类对象中包含n个字符c |
string(const string&s) | 拷贝构造函数 |
大小容量相关: | |
length() | 返回字符串有效字符长度 |
size() | 返回字符串有效字符长度 |
capacity() | 返回空间总大小 |
empty() | 检测字符串释放为空串,是返回true,否则返回false |
clear() | 清空有效字符 |
reserve(n) | 为字符串预留n个空间 |
resize(n) | 将有效字符的个数改成n个,多出的空间用0填充 |
resize(n,c) | 将有效字符的个数改成n个,多出的空间用字符c填充 |
访问: | |
operator[] | 返回pos位置的字符,const string类对象调用 |
begin() | 获取第一个字符的迭代器 |
end() | 获取最后一个字符下一个位置的迭代器 |
修改查找操作: | |
push_back() | 在字符串后尾插字符c |
append(str) | 在字符串后追加一个字符串 |
operator+= | 在字符串后追加字符串str |
find (c,pos) | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 |
注意:
- size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()。
- clear()只是将string中有效字符清空,不改变底层空间大小。
- resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容的大小,如果是将元素个数减少,底层空间总大小不变。
- reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。
- 在string尾部追加字符时,s.push_back© / s.append(1, c) / s += 'c’种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
- 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。
1.需要实现的各种函数接口:
namespace bit
{
class string
{
friend ostream& operator<<(ostream& _cout, const bit::string& s);
friend istream& operator>>(istream& _cin, bit::string& s);
public:
typedef char* iterator;
public:
string(const char* str = "");
string(const string& s);
string& operator=(const string &s);
~string();
//
// iterator
iterator begin();
iterator end();
/
// modify
void push_back(char c);
string& operator+=(char c);
void append(const char* str);
string& operator+=(const char* str);
void clear();
void swap(string& s);
const char* c_str()const;
/
// capacity
size_t size()const
size_t capacity()const
bool empty()const
void resize(size_t n, char c = '\0');
void reserve(size_t n);
/
// access
char& operator[](size_t index);
const char& operator[](size_t index)const;
/
//relational operators
bool operator<(const string& s);
bool operator<=(const string& s);
bool operator>(const string& s);
bool operator>=(const string& s);
bool operator==(const string& s);
bool operator!=(const string& s);
// 返回c在string中第一次出现的位置
size_t find (char c, size_t pos = 0) const;
// 返回子串s在string中第一次出现的位置
size_t find (const char* s, size_t pos = 0) const;
// 在pos位置上插入字符c/字符串str,并返回该字符的位置
string& insert(size_t pos, char c);
string& insert(size_t pos, const char* str);
// 删除pos位置上的元素,并返回该元素的下一个位置
string& erase(size_t pos, size_t len);
private:
char* _str;
size_t _capacity;
size_t _size;
}
}
2.实现代码
#include <iostream>
#include <string.h>
#include <string>
#include <assert.h>
using namespace std;
#pragma warning(disable:4996)
namespace bit //为了和系统的string的功能比较,自己加了一个命名空间,防止混淆
{
class string
{
//对该string类的输入输出流做了重载,实现在类的内部(结尾处)
friend ostream& operator<<(ostream& _cout, const bit::string& s);
friend istream& operator>>(istream& _cin, bit::string& s);
public:
typedef char* iterator; //指针充当迭代器
public:
string() : _str(nullptr), _size(0), _capacity(0)
{}
string(const char* str="")
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
string(const string& s):_str(nullptr), _size(0), _capacity(0)
{
string tmp(s._str);
this->swap(tmp);
}
string& operator=(const string& s) {
if (*this != s) {
string tmp(s);
this->swap(tmp);
}
return *this;
}
~string() {
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
public:
iterator begin() {
return _str;
}
iterator end() {
return _str + _size;
}
public:
void push_back(char c) {
if (_size == _capacity) {
resize(_size + 1, c);
}
else{
_str[_size] = c;
_size++;
_str[_size] = '\0';
}
}
string& operator+=(char c) {
push_back(c);
return *this;
}
void append(const char* str) {
_size += strlen(str);
if (_capacity < _size) {
reserve(_size);
}
strcat(_str, str);
}
string& operator+=(const char* str) {
append(str);
return *this;
}
void clear() {
_size = 0;
_str[0] = '\0';
}
void swap(string& s) {
std::swap(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
const char* c_str()const {
return _str;
}
public: capacity
size_t size()const {
return _size;
}
size_t capacity()const {
return _capacity;
}
bool empty()const {
return _size == 0;
}
void resize(size_t n, char c = '\0') {
if (n > _capacity) {
reserve(n);
while (_size < n) {
_str[_size] = c;
_size++;
}
}
_str[n] = '\0';
_size = n;
}
void reserve(size_t n) { //预留空间
if (n > _capacity) {
char *tmp =new char[1.5*n + 1]; //1 为‘\0’的空间
memcpy(tmp, _str,_capacity+1);
delete[] _str;
_str = tmp;
_capacity = 1.5 * n;
}
}
public:
char& operator[](size_t index) {
return _str[index];
}
const char& operator[](size_t index)const {
return _str[index];
}
bool operator<(const string& s) {
return strcmp(_str, s._str)==-1;
}
bool operator<=(const string& s) {
return !(strcmp(_str, s._str) == 1);
}
bool operator>(const string& s) {
return strcmp(_str, s._str) == 1;
}
bool operator>=(const string& s) {
return !(strcmp(_str, s._str) == -1);
}
bool operator==(const string& s) {
return strcmp(_str, s._str) == 0;
}
bool operator!=(const string& s) {
return strcmp(_str, s._str) != 0;
}
返回c在string中第一次出现的位置
size_t find(char c, size_t pos = 0) const {
int idx = pos;
while (idx < _size && _str[idx++] != c);
return idx>=_size ? -1:idx-1; //没找到或者越界返回-1
}
返回子串s在string中第一次出现的位置
size_t find(const char* s, size_t pos = 0) const {
int i = pos; //_str
int j = 0; //s
while (i < _size && j < strlen(s))
{
if (_str[i] == s[j])
{
++i;
++j;
}
else
{
j = 0;
i = i - j + 1;
}
}
if (j = strlen(s))
{
return i - strlen(s);
}
return -1;
}
在pos位置上插入字符c/字符串str,并返回该字符的位置
string& insert(size_t pos, char c) {
if (_capacity == _size) {
reserve(_capacity + 10);//多预留十个空间
}
int count = _size;
while (count > pos) {
_str[count] = _str[count - 1];
count--;
}
_str[pos] = c;
_str[_size + 1] = '\0';
_size++;
return *this;
}
string& insert(size_t pos, const char* str){
int len = strlen(str);
int n = _size + len;
if (n>_capacity ) {
reserve(n+10);//多预留十个空间
}
while (n > pos) {
_str[n] = _str[n - len];
n--;
}
for (int i = pos; i < pos+len ; i++) {
_str[i] = str[i - pos];
}
return *this;
}
删除pos位置上的元素,并返回该元素的下一个位置
string& erase(size_t pos, size_t len) {
char * tmp = new char[_size - len + 1];
memset(tmp, 0, _size - len + 1);
memcpy(tmp, _str, pos);
memcpy(tmp+pos, _str+pos+len, _size-len-pos);
delete[] _str;
_str = tmp;
_size -= len;
return *this;
}
private:
char* _str;
size_t _capacity;
size_t _size;
};
///输入输出流重载
ostream& operator<<(ostream& _cout, const bit::string& s)
{
_cout << s._str;
return _cout;
}
istream& operator>>(istream& _cin, bit::string& s)
{
//预分配100个空间
char* str = (char*)malloc(sizeof(char) * 100);
char* buf = str;
int i = 1;
//预处理:跳过流里面的所有空格和回车
while ((*buf = getchar()) == ' ' || (*buf == '\n'));
for (; ; ++i)
{
if (*buf == '\n') //回车跳出
{
*buf = '\0';
break;
}
else if (*buf == ' ') //空格跳出
{
*buf = '\0';
break;
}
else if (i % 100 == 0) //空间不足
{
i += 100; //追加100个空间
str = (char*)realloc(str, i);
}
else //每次getchar()一个值
{
buf = (str + i);//为了避免realloc返回首地址改变,不使用++buf,而是用str加上偏移.
//每次读取一个字符
*buf = getchar();
}
}
//输入完成,更新s
s._str = str;
s._capacity = s._size = i;
return _cin;
}
};
3.部分测试代码:
int main()
{
//系统string接口
string s1("ddasfghi");
string s2("dbcdefghi");
string s3(s2);
cout << s3 << " capacity:" << s3.capacity() << " size:" << s3.size() << endl;
s3.resize(20);
cout <<s3<< " capacity:" << s3.capacity() << " size:" << s3.size() << endl;
/* s3.resize(2,'x');
cout <<s3<< " capacity:" << s3.capacity() << " size:" << s3.size() << endl;*/
s3.append("dddxxxdpxpxxp");
cout << s3 << " capacity:" << s3.capacity() << " size:" << s3.size() << endl;
cout << s3.find("dp", 0) << endl;
cout << endl;
// 自己实现的 bit::string接口测试
bit::string ss1("ddasfghi");
cin >> ss1 ;
bit::string ss2("dbcdefghi");
bit::string ss3(ss2);
cout << ss1 << endl;
cout<<ss3 << " capacity:" << ss3.capacity() << " size:" << ss3.size() << endl;
ss3.resize(20);
cout << ss3 << " capacity:" << ss3.capacity() << " size:" << ss3.size() << endl;
//ss3.resize(2, 'x');
//cout <<ss3 << " capacity:" <<ss3.capacity()<<" size:"<<ss3.size() << endl;
ss3+="dddxxxdpxpxxp";
cout << ss3 << " capacity:" << ss3.capacity() << " size:" << ss3.size() << endl;
cout << ss3.find("dp") << endl;
return 0;
}
自己实现的reserve与系统string中reserve对底层的空间增长分配不一样,故容量_capacity有差异。
然后发现系统resize()默认填充’\0’后与自己写的resize()有点小差异,细心测试就能发现。不过自己也毕竟只是简单模拟,有些细节一时间不能与系统一样。就这样吧…