前言:
仅模拟实现string的一部分。实现的目的是:加深对string的理解。
模拟实现的包含以下下内容:
- swap()
- 构造函数
- 拷贝构造函数
- 析构函数
- =
- []
- size()
- capcity()
- 迭代器
- reserve
- resize
- insert
- push_back
- append
- +=
- earse
- find
- npos
- << 和 >>
源码展示:(解析位于注释中)
在.cpp文件中:
#define _CRT_SECURE_NO_WARNINGS 1
#include<string>
#include<iostream>
using namespace std;
#include <assert.h>
#include"my_string.h"
//用于测试
int main()
{
return 0;
}
在.h文件中:(通常来说,应该再开一和cpp文件定义声明分离,在练习这这样定义和声明不分离方便点)
#pragma once
namespace myString
{
class string
{
public:
//实现swap
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capcity, s._capcity);
}
//实现构造函数 1- string s //实现构造函数 2— string s("xxxxx");
string(const char* str = "")
:_size(strlen(str)) //这里的strlen还是用的编译器的string库里的
, _capcity(_size)
{
_str = new char[_capcity + 1]; // + 1 是为了存放/0 ;
strcpy(_str, str);
}
实现构造函数 3- string s1(s2)---浅拷贝
//string(const string& s)
// :_size(strlen(s._str))
// ,_capcity(_size)
//{
// _str = new char[_capcity + 1];
// strcpy(_str, s._str);
//}
//实现深度拷贝——4 string s1(s2) 现代写法
string(const string& s)
:_str(nullptr)
, _size(0)
, _capcity(0)
{
string tmp(s._str);//tmp在出函数时会被析构函数销毁
swap(tmp);
}
//实现析构函数
~string()
{
if (_str != nullptr)
{
delete[] _str;
_str = nullptr;
_size = _capcity = 0;
}
}
//赋值符号 = 重载的实现
string& operator=(string s) //因为此处 string s 为实参拷贝,所以不会改变原s
{
swap(s);
return *this;
}
//实现 c_str
//返回值为const + 指针 代表指针指向的空间不可改变 ; 函数后加 const 指定this指针指向的空间不可被修改
const char* c_str() const
{
return _str;
}
//实现[],可对 _str进行修改的
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
//实现[],不可对 _str进行修改的
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
//实现size()
size_t size() const
{
return _size;
}
size_t capcity()const
{
return _capcity;
}
//实现迭代器
typedef char* iterator;
typedef const char* const_iterator; //const_iterator
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
//实现reserve 功能为保留n个空间
void reserve(size_t n)
{
if ( n <= _capcity)
{
return;
}
else
{
//new 开辟空间一般不会失败
char* tmp = new char[n + 1];
assert(tmp);
strcpy(tmp, _str);
delete[] _str; // 关键的、容易忘记的 !!!!!
_str = tmp; //这样写直接改变this指针中_str,不需要返回值
_capcity = n;
}
}
//实现resize resize功能:n > capcity 开辟到n个空间,并且开辟的空间用字符c初始化
// n < capcity 删除部分数据,保留n个数据,但空间不变。
void resize(size_t n, char c ='\0')
{
if (n <= _capcity)
{
_size = n;
_str[_size] = '\0';
}
else
{
reserve(n);//_capcity已经改变,但_size还没有被改变
for(int i = _size ; i < n ; i ++ )
{
_str[i] = c;
}
_size = n;
_str[_size] = '\0';
}
}
//实现insert1 在pos位置插入一个字符c
string& insert(size_t pos, char c)
{
assert(pos <= _size);
if (_size == _capcity)
{
reserve(_capcity == 0 ? 4 : _capcity * 2);
}
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
end--;
}
_str[pos] = c;
_size++;
return *this;
}
//实现insert2 在pos位置插入字符串c
string& insert(size_t pos, const char* c)
{
assert(pos <= _size);
size_t len = strlen(c);
if (_size + len > _capcity)
{
reserve(_size + _capcity);
}
size_t end = _size + len;
while (end > pos + len - 1)
{
_str[end] = _str[end - len];
--end;
}
strncpy(_str + pos, c, len);
_size = _size + len;
return *this;
}
//实现 push_back() pushback作用是在对象字符串末尾加一个字符
void push_back(char c)
{
if (_size == _capcity)
{
reserve(_capcity == 0 ? 4 : _capcity * 2);
}
_str[_size] = c;
_size++;
_str[_size] = '\0';
}
void append(const char* str)
{
insert(_size, str);
}
//实现对 += 的重载
string& operator+=(char ch) //为何不用 char& ch 呢? 因为大型对象时引用传参效率提升才明显
{
push_back(ch);
return *this;
}
string& operator+=(char* str)
{
append(str);
return *this;
}
//实现earse函数
string& earse(size_t pos, size_t len = npos) //如果不给第二个值那就删除pos之后的所有
{
assert(pos < _size);
if(len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
size_t begin = len + pos;
while (begin <= _size) //因为要把‘\0’也拷过去
{
_str[begin - len] = _str[begin];
begin++;
}
_size -= len;
}
return *this;
}
//实现find函数 ,从字符串的pos位置开始查找,返回第一个所查字符所在位置的下标,如果没有则返回npos
size_t find(char c, size_t pos = 0)
{
while (pos < _size)
{
if (_str[pos] == c)
{
return pos;
}
pos++;
}
return npos;
}
//实现find函数2,查找子窜,返回子窜第一个字符下标;//这个实现的机制是暴力匹配,网络上有高效字符串匹配的方法,这里直接用strstr实现
size_t find(const char* str, size_t pos = 0)
{
const char* p = strstr(_str + pos, str);
if (p == nullptr)
{
return npos;
}
else
{
return p - _str; //下标(相对位置)
}
}
private:
//_size顺序不能变,因为初始化列表实际上是对着private里的成员声明顺序进行初始化的。
char* _str;
size_t _size;
size_t _capcity;
//C++11仅不允许在类声明中初始化static非const类型的数据成员。
const static size_t npos; //即为最大值
};
const size_t string::npos = -1;
//为啥运算符重载在类外定义
ostream& operator<<(ostream& out, const string& s)
{
for (auto ch : s) //依据迭代器实现
{
out << ch;
}
return out;
}
istream& operator>>(istream& in, string& s)
{
char ch;
ch = in.get();
/* get(void) 用来从指定的输入流中提取一个字符(包括空白字符),
函数的返回值就是读入的字符。
若遇到输入流中的文件结束符,则函数值返回文件结束标志EOF*/
char buff[128] = {
'\0' }; //因为一次最多输入128
size_t i = 0;
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == 127)
{
s += buff;
memset(buff, '\0', 128);
/*memset函数 ,内存设置函数 memset
(地址,设置的必须为整形的内容x,改变的字节数n)
用整形内容x替换地址容器中前n个字节的内容*/
i = 0;
}
ch = in.get();
}
s += buff;
return in;
}
}