上一篇博客我们学习了string类的常见应用,接下来我们将堆对它们的底层进行深入挖掘,并进行模拟实现
作者个人gitee主页:楼田莉子 (riko-lou-tian) - Gitee.com
感兴趣的可以自行去查看
目录
[string(const char* str)的模拟实现](#string(const char* str)的模拟实现)
string实现的前置准备
首先我们需要三个文件
string.h:string实现的命名空间、类以及函数声明
string.cpp:string实现的方法
test.cpp:测试文件
string在底层上可以认为是一个字符顺序表。因此在底层上我们也像顺序表那样定义三个变量
cpp
char* _str;
size_t _size;
size_t _capacity;
同时在测试文件test.c需要将模拟实现与原装时间进行对比,但是这样就会与我们系统的string冲突。所以我们需要一个命名空间来包装它
cpp
//防止测试的时候与系统的string冲突,故用Boogiepop命名空间封装
namespace Boogiepop
//命名空间:不吉波普
{
class basic_string
{
public:
private:
char* _str;
size_t _size;
size_t _capacity;
};
};
命名空间可以在多个文件进行书写。多个文件的命名空间一般被认为是同一个命名空间(相当于被合并了)
我们要在三个文件中都用同一个命名空间包装起来。否则会冲突
string的构造/析构实现
string的构造方式常用的有三种:
string()、string(const char*str)、
string()的模拟实现
误区:不要这么初始化

测试:
string.cpp
cpp
#include"string.h"
namespace Boogiepop
//命名空间:不吉波普
{
string::string()
:_str(nullptr), _size(0),_capacity(0)
{
}
string::string(const char* str)
{
}
const char*string:: c_str() const
{
return _str;
}
}
string.h
cpp
#pragma once
//防止测试的时候与系统的string冲突,故用Boogiepop命名空间封装
namespace Boogiepop
//命名空间:不吉波普
{
class string
{
public:
string();
string(const char* str);
//用c_str来打印
//之所以不先用流插入流提取是因为它们相对比较复杂,后面进行学习
const char* c_str() const;
private:
char* _str;
size_t _size;
size_t _capacity;
};
};
test.cpp
cpp
#include<iostream>
using namespace std;
#include"string.h"
namespace Boogiepop
//命名空间:不吉波普
//三个文件的命名空间被认为是同一个命名空间
{
void string_test1()
{
string s1;
cout<<s1.c_str()<<endl;
}
};
int main()
{
Boogiepop::string_test1();
return 0;
}
经过测试,程序崩溃

为什么会崩溃呢?
因为返回的_str是一个const类型的指针。const char*的指针与其他指针不同,其他指针都会按十六进制的地址去打印,但是const char* 不会。const char*更多指向一个串,对指向的空间上的内容,直到'\0'停止。但_str被初始化为nullptr,而且不能被修改,空指针直接的解引用就会报错。
正确的初始化方式为:
cpp
namespace Boogiepop
//命名空间:不吉波普
{
string::string()
:_str(new char[1] {'\0'}), _size(0), _capacity(0)
{
}
string::string(const char* str)
{
}
const char*string:: c_str() const
{
return _str;
}
}
string(const char* str)的模拟实现
cpp
string::string(const char* str)
:_str(new char[strlen(str) + 1])//strlen是不包含'\0'的长度
,_size(strlen(str)), _capacity(strlen(str))
{
strcpy(_str, str);
}
但是这么初始化并不好。因为strlen是在运行时候计算数值,连续调用三个strlen会让程序运行变得很慢。
那么我们是否可以这么改呢?
cpp
string::string(const char* str)
:_size(strlen(str))
,_str(new char[_size + 1])//strlen是不包含'\0'的长度
, _capacity(_size)
{
strcpy(_str, str);
}
也是不可以的。
因为初始化列表是严格按照类中声明的顺序来的,声明的顺序为:
cpp
private:
char* _str;
size_t _size;
size_t _capacity;
我们可不可以将_size顺序调换呢?理论上确实可以解决这个问题。但是这样的程序是耦合的。改变的一个就会影响后面的。项目中应当极力避免的。
如果我们要改可以这样
cpp
string::string(const char* str)
:_size(strlen(str))//strlen是不包含'\0'的长度
{
_str=new char[_size + 1];
_capacity=_size;
strcpy(_str, str);
}
构造的合并
我们也可以将两种构造方式合并。无参的构造和带参数的构造是可以合并的------写全缺省的构造函数
但是不能初始化为nullptr,程序会崩溃。可以初始化为'\0'或者
string.h
cpp
#pragma once
#include<string.h>//包含C语言的头文件
//防止测试的时候与系统的string冲突,故用Boogiepop命名空间封装
namespace Boogiepop
//命名空间:不吉波普
{
class string
{
public:
//string的构造函数
//string();//合并了构造函数
string(const char* str="");
//用c_str来打印
//之所以不先用流插入流提取是因为它们相对比较复杂,后面进行学习
const char* c_str() const;
private:
char* _str;
size_t _size;
size_t _capacity;
};
};
string.cpp
cpp
#include"string.h"
namespace Boogiepop
//命名空间:不吉波普
{
//string的构造
/*string::string()
:_str(new char[1] {'\0'}), _size(0), _capacity(0)
{
}*/
//string的合并
string::string(const char* str)
:_size(strlen(str))//strlen是不包含'\0'的长度
{
_str=new char[_size + 1];
_capacity=_size;
strcpy(_str, str);
}
const char*string:: c_str() const
{
return _str;
}
}
string的拷贝构造函数
string.h
cpp
//拷贝构造函数
string(const string& str);
string.cpp
cpp
//拷贝构造函数
string::string(const string& str)
{
}
现代写法:
cpp
void string::swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
string::string(const string& s)
{
string tmp(s._str);
swap(tmp);
}
赋值重载
声明:
cpp
//赋值重载
string& operator=(const string& s);
//赋值重载
string& operator=(const string& s);
//赋值重载
string& operator=(const string tmp);
方法一
cpp
//赋值重载
//方法一
string& string::operator=(const string& s)
{
if (this != &s)
{
char* tmp = new char[s._capacity + 1];
memcpy(tmp, s._str, s._size + 1);
delete[] _str;
_str = tmp;
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
方法二
cpp
//方法二
string& string::operator=(const string& s)
{
if (this != &s)
{
string tmp(s);
swap(tmp);
}
return *this;
}
方法三
cpp
//方法三
string& string::operator=(string tmp)
{
cout << "string& string::operator=(string tmp)" << endl;
swap(tmp);
return *this;
}
string的析构实现
string.cpp
cpp
//string的析构
string::~string()
{
delete[] _str;//用[]是因为中间可能用多个字符
_str = nullptr;
_size = 0;
_capacity = 0;
}
string函数的实现
size()的模拟实现
cpp
//size()函数
size_t string::size() const
{
return _size;
}
operator[]的模拟实现
cpp
char& string::operator[](size_t i)
//可以写的operator[]()函数
{
assert(i< _size);//断言写的是为真的条件,为假会直接报错
return _str[i];
}
const char& string::operator[](size_t i) const
//只读的operator[]()函数
{
assert(i < _size);//断言写的是为真的条件,为假会直接报错
return _str[i];
}
测试:
cpp
#include"string.h"
namespace Boogiepop
//命名空间:不吉波普
//三个文件的命名空间被认为是同一个命名空间
{
void string_test1()
{
string s1;
cout<<s1.c_str()<<endl;
string s2("hello world");
cout<<s2.c_str()<<endl;
for (size_t i = 0; i < s2.size(); i++)
{
s2[i]++;
cout<<s2[i]<<" ";
}
}
};
int main()
{
Boogiepop::string_test1();
return 0;
}
结果为:

运行正常
迭代器模拟实现
正向迭代器
string.h
cpp
//迭代器
typedef char* iterator;
//正向迭代器
iterator begin();
iterator end();
string.cpp
cpp
//正向迭代器
string::iterator string::begin()
{
return _str;
}
string::iterator string::end()
{
return _str + _size;
}
测试:
cpp
#include"string.h"
namespace Boogiepop
//命名空间:不吉波普
//三个文件的命名空间被认为是同一个命名空间
{
void string_test2()
{
const string s1("the song of the end world ");
string::iterator it1 = s1.begin();
while (it1 != s1.end())
{
cout<<*it1<<" ";
it1++;
}
cout<<endl;
}
};
int main()
{
//Boogiepop::string_test1();
Boogiepop::string_test2();
return 0;
}
这样是不通过的,因为涉及到了权限的放大,所以我们还需要模拟实现const迭代器
const正向迭代器
string.h
cpp
//迭代器
typedef char* iterator;
typedef const char* const_iterator;
//正向迭代器
iterator begin();
iterator end();
//正向const迭代器
const_iterator begin() const;
const_iterator end() const;
string.cpp
cpp
//正向const迭代器
string::const_iterator string::begin() const
{
return _str;
}
string::const_iterator string::end() const
{
return _str + _size;
}
但是const迭代器目前只是简单实现,实际上const迭代器的实现十分复杂。需要后面的知识才能明白
string的插入函数
reserve
cpp
void string::reserve(size_t n)
//扩容函数
{
if (n > _capacity)
{
char* new_str = new char[n + 1];
strcpy(new_str, _str);
delete[] _str;
_str = new_str;
_capacity = n;
}
}
pushback
string.cpp
cpp
void string::push_back(char c)
//尾插函数
{
if (_size >= _capacity)
{
size_t new_capacity =_capacity == 0? 4 : _capacity *2;
reserve(new_capacity);
}
_str[_size] = c;
++_size;
_str[_size] = '\0';
}
test.cpp
cpp
#include"string.h"
namespace Boogiepop
//命名空间:不吉波普
//三个文件的命名空间被认为是同一个命名空间
{
void string_test3()
{
string s1("the song of the end world ");
cout<<s1.c_str()<<endl;
s1.push_back('!');
cout<<s1.c_str()<<endl;
}
};
int main()
{
Boogiepop::string_test3();
return 0;
}
测试成功'

append
cpp
void string::append(const char* str)
{
size_t len = strlen(str);
if (len + _size > _capacity)
{
size_t new_capacity = len + _size <_capacity * 2? _capacity *2: len + _size;
}
strcpy(_str + _size, str);
_size += len;
}
+=运算符重载
string.h
cpp
string& operator+=(char ch);
string& operator+=(const char* str);
string.cpp
cpp
string& string::operator+=(char ch)
{
push_back(ch);
return *this;
}
string& string::operator+=(const char* str)
{
append(str);
return *this;
}
测试:
cpp
#include"string.h"
namespace Boogiepop
//命名空间:不吉波普
//三个文件的命名空间被认为是同一个命名空间
{
void string_test3()
{
string s1("the song of the end world ");
cout<<s1.c_str()<<endl;
s1.push_back('!');
cout<<s1.c_str()<<endl;
s1.append(" ------ singer");
cout<<s1.c_str()<<endl;
s1+= " ------ singer";
cout<<s1.c_str()<<endl;
s1+= '~';
cout<<s1.c_str()<<endl;
}
};
int main()
{
Boogiepop::string_test3();
return 0;
}
结果为:

测试均成功
insert
string.h
cpp
//插入函数
void insert(size_t pos, const char* str);
void insert(size_t pos, char ch);
string.cpp
cpp
//插入函数
void string::insert(size_t pos, char ch)
{
assert(pos<=_size);
if (_size >= _capacity)
{
size_t new_capacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(new_capacity);
}
//挪动数据
//方案一:
/* int end = _size;
while (end >=(int) pos)
{
_str[end + 1] = _str[end];
--end;
}*/
//方案二:
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)
{
assert(pos <= _size);
size_t len = strlen(str);
if (_size+len >= _capacity)
{
size_t new_capacity =2*_capacity>len+_size? 2*_capacity : len + _size;
reserve(new_capacity);
}
//挪动数据
size_t end = _size+len;
while (end >pos+len-1)
{
_str[end] = _str[end-len];
--end;
}
for (size_t i = 0; i < len; i++)
{
_str[pos + i] = _str[i];
}
}
测试:
cpp
#include"string.h"
namespace Boogiepop
//命名空间:不吉波普
//三个文件的命名空间被认为是同一个命名空间
{
void string_test5()
{
string s1("the song of the end world ");
cout << s1 << endl;
s1.insert(0, "d");
cout << s1 << endl;
s1.insert(10, "singer");
cout << s1 << endl;
}
};
int main()
{
Boogiepop::string_test5();
return 0;
}
测试结果为:

成员变量:npos
string.h
cpp
//定义成员变量npos
static const size_t npos = -1;
erase
string.h
cpp
//erase函数
string& erase(size_t pos = 0, size_t len = npos);
string.cpp
cpp
//定义成员变量npos
static const size_t npos = -1;
//erase函数
string& string::erase(size_t pos, size_t len)
{
assert(pos < _size);
// 要删除的数据,大于pos后面的字符个数
// pos后面全删
if (len == npos || len >= (_size - pos))
{
_size = pos;
_str[_size] = '\0';
}
else
{
size_t i = pos + len;
memmove(_str + pos, _str + i, _size + 1 - i);
_size -= len;
}
return *this;
}
test.cpp
cpp
#include"string.h"
namespace Boogiepop
//命名空间:不吉波普
//三个文件的命名空间被认为是同一个命名空间
{
void string_test6()
{
string s1("the song of the end world ");
cout << s1 << endl;
s1.erase(0, 4);
cout << s1 << endl;
}
};
int main()
{
Boogiepop::string_test6();
return 0;
}
测试结果正确

pop_back
string.h
cpp
//pop_back函数
void pop_back()
string.cpp
cpp
//pop_back函数
void string::pop_back()
{
assert(_size > 0);
--_size;
_str[_size] = '\0';
}
test.cpp
cpp
#define _CRT_SECURE_NO_WARNINGS
#include"string.h"
namespace Boogiepop
//命名空间:不吉波普
//三个文件的命名空间被认为是同一个命名空间
{
void string_test6()
{
string s1("the song of the end world ");
cout << s1 << endl;
s1.erase(0, 4);
cout << s1 << endl;
s1.pop_back();
s1.pop_back();
s1.pop_back();
s1.pop_back();
s1.pop_back();
cout << s1 << endl;
}
};
int main()
{
Boogiepop::string_test6();
return 0;
}
测试结果正确

find
string.h
cpp
//查找函数
size_t find(char ch, size_t pos = 0);
size_t find(const char *str, size_t pos = 0 );
string.cpp
cpp
//查找函数
size_t string::find(char ch, size_t pos )
{
for (int i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
}
size_t string::find(const char* str, size_t pos )
{
const char* p = strstr(_str+pos, str);
if (p == nullptr)
{
return npos;
}
else
{
return p - _str;
}
}
test.cpp
cpp
#include"string.h"
namespace Boogiepop
//命名空间:不吉波普
//三个文件的命名空间被认为是同一个命名空间
{
void string_test7()
{
string s1("the song of the end world ");
cout << s1 << endl;
size_t P=s1.find("the");
if (P != string::npos)
{
cout<<"找到了,位置在:"<<P<<endl;
}
else
{
cout<<"没找到"<<endl;
}
size_t U = s1.find("o");
if (U != string::npos)
{
cout << "找到了,位置在: " << U << endl;
}
else
{
cout << "没找到" << endl;
}
}
};
int main()
{
Boogiepop::string_test7();
return 0;
}
测试成功:

substr
string.h
cpp
//substr函数
string substr(size_t pos = 0, size_t len = npos) const;
string.cpp
cpp
//substr函数
string string::substr(size_t pos , size_t len ) const
{
if (len == npos || len > _size - pos)
{
len=_size - pos;
}
string ret;
ret.reserve(len);
for (size_t i = 0;i < len;i++)
{
ret+=_str[pos+i];
}
return ret;
}
test.cpp
cpp
#define _CRT_SECURE_NO_WARNINGS
#include"string.h"
namespace Boogiepop
//命名空间:不吉波普
//三个文件的命名空间被认为是同一个命名空间
{
void string_test8()
{
string s1("the song of the end world ");
cout << s1 << endl;
string s2=s1.substr(0, 4);
cout << s2 << endl;
string s3=s1.substr(10, 6);
cout << s3 << endl;
}
};
int main()
{
Boogiepop::string_test8();
return 0;
}
测试结果:

clear函数
cpp
//清除函数
void string::clear()
{
_size = 0;
_str[0] = '\0';
}
swap函数
cpp
void swap(string& x, string& y)
{
x.swap(y);
}
流插入与流提取
流插入
string.h
cpp
//流插入
ostream& operator<< (ostream& out, const string& str);
//流提取
iostream& operator>> (iostream& in, string& str);
在这里不必要使用友元函数来访问私有,因为访问c_str就足够访问内部的变量了
string.cpp
cpp
//流提取
ostream& operator<< (ostream& out, const string& str)
{
out<<str.c_str();
return out;
}
但是这么写有一个漏洞
cpp
#include"string.h"
namespace Boogiepop
//命名空间:不吉波普
//三个文件的命名空间被认为是同一个命名空间
{
void string_test4()
{
string s1("the song of the end world ");
cout<<s1<<endl;
s1 += '\0';
cout<<s1<<endl;
s1 += '!';
cout<<s1<<endl;
}
};
int main()
{
Boogiepop::string_test4();
return 0;
}
结果为:

不仅打印不出来'\0',而且连后面的'!'都无法打印出来了。
因此改变一下为:
cpp
//流插入
ostream& operator<< (ostream& out, const string& str)
{
for (int i = 0; i < str.size(); i++)
{
out<<str[i];
}
return out;
}
c_str与C语言保持一致,遇到'\0'就停止了
流提取
get()是C++自己的函数,用来从输入流中读取一个字符
string.cpp
cpp
//流提取
istream& operator>>(istream& in, string& s)
{
s.clear();
char buff[128];
int i = 0;
//get()是C++自己的函数,用来从输入流中读取一个字符
char ch = in.get();
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == 127)
{
buff[i] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
getline函数
string.cpp
cpp
istream& getline(istream& in, string& s, char delim)
{
s.clear();
char buff[128];
int i = 0;
char ch = in.get();
while (ch != delim)
{
buff[i++] = ch;
if (i == 127)
{
buff[i] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
比较函数
声明:
cpp
//比较函数
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;
<
cpp
bool string::operator<(const string& s) const
{
size_t i1 = 0, i2 = 0;
while (i1 < _size && i2 < s._size)
{
if (_str[i1] < s._str[i2])
{
return true;
}
else if (_str[i1] > s._str[i2])
{
return false;
}
else
{
++i1;
++i2;
}
}
<=
cpp
bool string::operator<=(const string& s) const
{
return *this < s || *this == s;
}
>
cpp
bool string::operator>(const string& s) const
{
return !(s <= *this);
}
>=
cpp
bool string::operator>=(const string& s) const
{
return !(*this < s);
}
==
cpp
bool string::operator==(const string& s) const
{
size_t i1 = 0, i2 = 0;
while (i1 < _size && i2 < s._size)
{
if (_str[i1] != s[i2])
{
return false;
}
else
{
++i1;
++i2;
}
}
return i1 == _size && i2 == s._size;
}
!=
cpp
bool string::operator!=(const string& s) const
{
return !(*this == s);
}
源代码
string.h
cpp
#pragma once
#include<iostream>
#include<string.h>//包含C语言的头文件
#include<assert.h>
using namespace std;
//防止测试的时候与系统的string冲突,故用Boogiepop命名空间封装
namespace Boogiepop
//命名空间:不吉波普
{
class string
{
public:
//string的构造函数
//string();//合并了构造函数
string(const char* str="");
//用c_str来打印
//之所以不先用流插入流提取是因为它们相对比较复杂,后面进行学习
//c_str()函数
const char* c_str() const;
//拷贝构造函数
string(const string& s);
//string的析构函数
~string();
//赋值重载
string& operator=(const string& s);
//赋值重载
string& operator=(const string& s);
//赋值重载
string& operator=(const string tmp);
//size()函数
size_t size() const;
//operator[]()函数
char& operator[](size_t i);//可以写的operator[]()函数
const char& operator[](size_t i) const;//只读的operator[]()函数
//迭代器
typedef char* iterator;
typedef const char* const_iterator;
//正向迭代器
iterator begin();
iterator end();
//正向const迭代器
const_iterator begin() const;
const_iterator end() const;
//string插入相关的函数
void reserve(size_t n);
void push_back(char c);
void append(const char* str);
string& operator+=(char ch);
string& operator+=(const char* str);
//插入函数
void insert(size_t pos, const char* str);
void insert(size_t pos, char ch);
//定义成员变量npos
static const size_t npos=-1 ;
//erase函数
string& erase(size_t pos = 0, size_t len = npos);
//pop_back函数
void pop_back();
//查找函数
size_t find(char ch, size_t pos = 0);
size_t find(const char *str, size_t pos = 0 );
//substr函数
string substr(size_t pos = 0, size_t len = npos) 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;
bool operator!=(const string& s) const;
//清除函数
void clear();
//swap函数
void swap(string& s);
private:
char* _str;
size_t _size;
size_t _capacity;
};
//流插入
ostream& operator<< (ostream& out, const string& str);
//流提取
istream& operator>> (istream& in, string& str);
//getline函数
istream& getline(istream& in, string& str, char delim='\n');
};
string.cpp
cpp
#define _CRT_SECURE_NO_WARNINGS
#include"string.h"
namespace Boogiepop
//命名空间:不吉波普
{
//string的构造
/*string::string()
:_str(new char[1] {'\0'}), _size(0), _capacity(0)
{
}*/
//string的合并
string::string(const char* str)
:_size(strlen(str))//strlen是不包含'\0'的长度
{
_str=new char[_size + 1];
_capacity=_size;
strcpy(_str, str);
}
void string::swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
//拷贝构造函数
/*string::string(const string& s)
{
_str = new char[s._size + 1];
memcpy(_str,s._str, s._size + 1);
_size = s._size;
_capacity = s._capacity;
}*/
//拷贝构造函数现代写法
string::string(const string& s)
{
string tmp(s._str);
swap(tmp);
}
//string的析构
string::~string()
{
delete[] _str;//用[]是因为中间可能用多个字符
_str = nullptr;
_size = 0;
_capacity = 0;
}
//赋值重载
//方法一
string& string::operator=(const string& s)
{
if (this != &s)
{
char* tmp = new char[s._capacity + 1];
memcpy(tmp, s._str, s._size + 1);
delete[] _str;
_str = tmp;
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
//方法二
string& string::operator=(const string& s)
{
if (this != &s)
{
string tmp(s);
swap(tmp);
}
return *this;
}
//方法三
string& string::operator=(string tmp)
{
cout << "string& string::operator=(string tmp)" << endl;
swap(tmp);
return *this;
}
//c_str函数
const char*string:: c_str() const
{
return _str;
}
//size()函数
size_t string::size() const
{
return _size;
}
//operator[]()函数
char& string::operator[](size_t i)
//可以写的operator[]()函数
{
assert(i< _size);//断言写的是为真的条件,为假会直接报错
return _str[i];
}
const char& string::operator[](size_t i) const
//只读的operator[]()函数
{
assert(i < _size);//断言写的是为真的条件,为假会直接报错
return _str[i];
}
//正向迭代器
string::iterator string::begin()
{
return _str;
}
string::iterator string::end()
{
return _str + _size;
}
//正向const迭代器
string::const_iterator string::begin() const
{
return _str;
}
string::const_iterator string::end() const
{
return _str + _size;
}
//string插入相关的函数
void string::reserve(size_t n)
//扩容函数
{
if (n > _capacity)
{
char* new_str = new char[n + 1];
memcpy(new_str, _str,_size + 1);
delete[] _str;
_str = new_str;
_capacity = n;
}
}
void string::push_back(char c)
//尾插函数
{
if (_size >= _capacity)
{
size_t new_capacity =_capacity == 0? 4 : _capacity *2;
reserve(new_capacity);
}
_str[_size] = c;
++_size;
_str[_size] = '\0';
}
void string::append(const char* str)
{
size_t len = strlen(str);
if (len + _size > _capacity)
{
size_t new_capacity = len + _size <_capacity * 2? _capacity *2: len + _size;
}
memcpy(_str + _size, str+1,len + 1);
_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)
{
assert(pos<=_size);
if (_size >= _capacity)
{
size_t new_capacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(new_capacity);
}
//挪动数据
//方案一:
/* int end = _size;
while (end >=(int) pos)
{
_str[end + 1] = _str[end];
--end;
}*/
//方案二:
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)
{
assert(pos <= _size);
size_t len = strlen(str);
if (_size+len >= _capacity)
{
size_t new_capacity =2*_capacity>len+_size? 2*_capacity : len + _size;
reserve(new_capacity);
}
//挪动数据
size_t end = _size+len;
while (end >pos+len-1)
{
_str[end] = _str[end-len];
--end;
}
for (size_t i = 0; i < len; i++)
{
_str[pos + i] = _str[i];
}
}
//定义成员变量npos
static const size_t npos = -1;
//erase函数
string& string::erase(size_t pos, size_t len)
{
assert(pos < _size);
// 要删除的数据,大于pos后面的字符个数
// pos后面全删
if (len == npos || len >= (_size - pos))
{
_size = pos;
_str[_size] = '\0';
}
else
{
size_t i = pos + len;
memmove(_str + pos, _str + i, _size + 1 - i);
_size -= len;
}
return *this;
}
//pop_back函数
void string::pop_back()
{
assert(_size > 0);
--_size;
_str[_size] = '\0';
}
//查找函数
size_t string::find(char ch, size_t pos )
{
for (int i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
}
size_t string::find(const char* str, size_t pos )
{
const char* p = strstr(_str+pos, str);
if (p == nullptr)
{
return npos;
}
else
{
return p - _str;
}
}
//substr函数
string string::substr(size_t pos , size_t len ) const
{
if (len == npos || len > _size - pos)
{
len=_size - pos;
}
string ret;
ret.reserve(len);
for (size_t i = 0;i < len;i++)
{
ret+=_str[pos+i];
}
return ret;
}
//比较函数
bool string::operator<(const string& s) const
{
size_t i1 = 0, i2 = 0;
while (i1 < _size && i2 < s._size)
{
if (_str[i1] < s[i2])
{
return true;
}
else if (_str[i1] > s[i2])
{
return false;
}
else
{
++i1;
++i2;
}
}
return i2<s._size;
}
bool string::operator<=(const string& s) const
{
return *this < s || *this == s;
}
bool string::operator>(const string& s) const
{
return !(s <= *this);
}
bool string::operator>=(const string& s) const
{
return !(*this < s);
}
bool string::operator==(const string& s) const
{
size_t i1 = 0, i2 = 0;
while (i1 < _size && i2 < s._size)
{
if (_str[i1] != s[i2])
{
return false;
}
else
{
++i1;
++i2;
}
}
return i1 == _size && i2 == s._size;
}
bool string::operator!=(const string& s) const
{
return !(*this == s);
}
//清除函数
void string::clear()
{
_size = 0;
_str[0] = '\0';
}
//swap函数
void swap(string& x, string& y)
{
x.swap(y);
}
//流插入
ostream& operator<< (ostream& out, const string& str)
{
for (int i = 0; i < str.size(); i++)
{
out << str[i];
}
return out;
}
//流提取
istream& operator>>(istream& in, string& s)
{
s.clear();
char buff[128];
int i = 0;
//get()是C++自己的函数,用来从输入流中读取一个字符
char ch = in.get();
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == 127)
{
buff[i] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
//getline函数
istream& getline(istream& in, string& str, char delim )
{
str.clear();
char buff[128];
int i = 0;
char ch = in.get();
while (ch != delim)
{
buff[i++] = ch;
if (i == 127)
{
buff[i] = '\0';
str += buff;
i = 0;
}
ch = in.get();
}
if (i > 0)
{
buff[i] = '\0';
str += buff;
}
return in;
}
}
test.cpp
cpp
#define _CRT_SECURE_NO_WARNINGS
#include"string.h"
namespace Boogiepop
//命名空间:不吉波普
//三个文件的命名空间被认为是同一个命名空间
{
void string_test1()
{
string s1;
cout<<s1.c_str()<<endl;
string s2("hello world");
cout<<s2.c_str()<<endl;
for (size_t i = 0; i < s2.size(); i++)
{
s2[i]++;
cout<<s2[i]<<" ";
}
}
void string_test2()
{
const string s1("the song of the end world ");
string::const_iterator it1 = s1.begin();
while (it1 != s1.end())
{
cout<<*it1<<" ";
it1++;
}
cout<<endl;
}
void string_test3()
{
string s1("the song of the end world ");
cout<<s1.c_str()<<endl;
s1.push_back('!');
cout<<s1.c_str()<<endl;
s1.append(" ------ singer");
cout<<s1.c_str()<<endl;
s1+= " ------ singer";
cout<<s1.c_str()<<endl;
s1+= '~';
cout<<s1.c_str()<<endl;
}
void string_test4()
{
string s1("the song of the end world ");
cout<<s1<<endl;
s1 += '\0';
cout<<s1<<endl;
s1 += '!';
cout<<s1<<endl;
cout<<s1.c_str()<<endl;
s1 += " ------ singer";
cout << s1 << endl;
}
void string_test5()
{
string s1("the song of the end world ");
cout << s1 << endl;
s1.insert(0, "d");
cout << s1 << endl;
s1.insert(10, "singer");
cout << s1 << endl;
}
void string_test6()
{
string s1("the song of the end world ");
cout << s1 << endl;
s1.erase(0, 4);
cout << s1 << endl;
s1.pop_back();
s1.pop_back();
s1.pop_back();
s1.pop_back();
s1.pop_back();
cout << s1 << endl;
}
void string_test7()
{
string s1("the song of the end world ");
cout << s1 << endl;
size_t P=s1.find("the");
if (P != string::npos)
{
cout<<"找到了,位置在:"<<P<<endl;
}
else
{
cout<<"没找到"<<endl;
}
size_t U = s1.find("o");
if (U != string::npos)
{
cout << "找到了,位置在: " << U << endl;
}
else
{
cout << "没找到" << endl;
}
}
void string_test8()
{
string s1("the song of the end world ");
cout << s1 << endl;
string s2=s1.substr(0, 4);
cout << s2 << endl;
string s3=s1.substr(10, 6);
cout << s3 << endl;
}
void string_test9()
{
string s1("the song of the end world "), s2("the song of the end world");
string s3("the song of the end world"), s4("the song of the end worldX");
string s5("the song of the end worldC "), s6("the song of the end world");
cout << (s1 < s2) << endl;
cout << (s3 < s4) << endl;
cout << (s5 < s6) << endl;
}
void string_test10()
{
string s1("the song of the end world "), s2("the story of the end world");
cout << s1 << s2 << endl;
cin>>s1>>s2;
cout << s1 << s2 << endl;
}
};
int main()
{
//Boogiepop::string_test1();
//Boogiepop::string_test2();
//Boogiepop::string_test3();
//Boogiepop::string_test4();
//Boogiepop::string_test5();
//Boogiepop::string_test6();
//Boogiepop::string_test7();
//Boogiepop::string_test8();
//Boogiepop::string_test9();
Boogiepop::string_test10();
return 0;
}
本篇博客到此结束,希望通过string的模拟实现能对string有更深刻的理解。
下一篇我们将继续学习STL中vector的内容进行学习
在这里求一个点赞,谢谢
封面图自取:
