自学内容网 自学内容网

C++ ——string的模拟实现

目录

前言

浅记

1. reserve(扩容) 

2. push_back(尾插) 

3. iterator(迭代器)

4. append(尾插一个字符串)

5. insert 

5.1 按pos位插入一个字符

5.2 按pos位插入一个字符串

6. erase 

7. find

7.1 查找字符

7.2 查找字符串

8. substr 

 代码汇总

string.h

string.cpp


前言

        C++ —— 关于string类-CSDN博客icon-default.png?t=O83Ahttps://blog.csdn.net/hedhjd/article/details/142023625?spm=1001.2014.3001.5501


浅记

char* _str;字符串存储空间首地址指针
size_t _size; 当前存储的有效数据个数
size_t _capacity;可用容量

*
_str:指向字符串存放的空间的指针
_size:当前存储的有效数据个数 ,指向最后一个字符的下一个位置
_capacity:代表当前可存储的最大容量
nops:此值设置为 -1,无符号整型转换就是42亿,且此值为const和静态参数具有全局效应,某些函数设置其为缺省参数

 


1. reserve(扩容) 

void string::reserve(size_t n)
{
//如果字符串存储的数据大于容量
if (n > _capacity)
{
//cout << "reserve:" << n << endl;
//申请新空间赋给临时变量tmp  + 1是位\0申请的空间
char* tmp = new char[n + 1];
//把数据拷贝到临时变量里
strcpy(tmp, _str);
//释放旧空间
delete[] _str;
//指向新空间
_str = tmp;
//字符串存储的数据等于容量
_capacity = n;
}
}


2. push_back(尾插) 

//尾插
void string::push_back(char ch)
{
//如果当前的有效数据个数和最大容量相等 
if (_size == _capacity)
{
//那么扩容
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
//因为size指向最后一个字符的下一个位置,所以直接插入
_str[_size] = ch;
++_size;
//最后要加一个\0,不然会乱码
_str[_size] = '\0';
}

先判断是否还有剩余空间如果满了就进行扩容操作。扩容后在_size位置放上我们要插入的字符,然后++_size,最后在_size的后一个位置补上'\0',防止乱码


3. iterator(迭代器)

const size_t string::npos = -1;
 
string::iterator string::begin()
{
return _str;
}
 
string::iterator string::end()
{
return _str + _size;
}
 
string::const_iterator string::begin() const
{
return _str;
}
 
string::const_iterator string::end() const
{
return _str + _size;
}

 


4. append(尾插一个字符串)


//尾插一个字符串
//_str:指向字符串存放的空间的指针
//_size:当前存储的有效数据个数, 指向最后一个字符的下一个位置
void string::append(const char* str)
{
//先计算要插入的字符长度,将值赋给临时变量len
size_t len = strlen(str); 
//如果当前存储的有效数据个数 加上 要插入的字符 大于 容量
if (_size + len > _capacity)
{
// 大于2倍,需要多少开多少,小于2倍按2倍扩
reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
}
//将目标地址到\0的位置中间的值拷贝过去
strcpy(_str + _size, str);
//尾插
_size += len;
}

如果尾插后的字符串所需空间大于现有空间就扩容,然后使用strcpy函数将需要尾插的字符串从_str + _size的位置开始拷贝,最后插入len即可


5. insert 

5.1 按pos位插入一个字符

//按pos位插入一个字符
void string::insert(size_t pos, char ch)
{
assert(pos <= _size);

//如果当前的有效数据个数和最大容量相等 
if (_size == _capacity)
{
//那么扩容
reserve(_capacity == 0 ? 4 : _capacity * 2);
}

// 挪动数据
//_size + 1:\0的后一位
//把end指向\0的后一位
size_t end = _size + 1;
//循环将pos之后的数据都向后挪动一位
while (end > pos)
{
_str[end] = _str[end - 1];
--end;
}
//将字符ch放入pos位
_str[pos] = ch;
++_size;
}

先判断是否需要扩容,然后定义一个end指向'\0'的下一个位置,然后将pos之后的数据都向后挪动一位,最后将字符ch放入pos位

5.2 按pos位插入一个字符串

//按pos位插入一个字符串
void string::insert(size_t pos, const char* str)
{
assert(pos <= _size);
//先计算要插入的字符长度,将值赋给临时变量len
size_t len = strlen(str);
//如果当前存储的有效数据个数 加上 要插入的字符 大于 容量
if (_size + len > _capacity)
{
// 大于2倍,需要多少开多少,小于2倍按2倍扩
reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
}
//定义一个end指向_size + len的位置上
size_t end = _size + len;
//通过循环把pos后的len个数据向后挪动len个位置
while (end > pos + len - 1)
{
_str[end] = _str[end - len];
--end;
}

// 这里不能用 strcpy 会把 '\0' 拷贝过来
//使用memcpy函数进行拷贝的原因是memcpy函数不会自动补上'\0'
// 而strcpy函数会在拷贝后自动补上'\0’,
//把str里的len个数据拷贝到_str + pos里
memcpy(_str + pos, str, len);
//插入字符串
_size += len;
}

 先计算要插入的字符长度,将值赋给临时变量len,判断是否需要扩容,然后定义一个end指向_size + len 的位置。通过循环来控制将pos后的len个数据向后挪动len个位置,然后使用memcpy函数把str里的len个数据拷贝到_str + pos里


6. erase 

//从pos位置开始删除长度为的len的字符串
//_str:指向字符串存放的空间的指针
//_size:当前存储的有效数据个数, 指向最后一个字符的下一个位置
void string::erase(size_t pos, size_t len)
{
//判断是否会越界
assert(pos < _size);
//如果要删除的字符  大于等于  当前存储的有效数据个数 减去 当前指向数据的位置
if (len >= _size - pos)
{
//直接将pos位置置为\0
_str[pos] = '\0';
_size = pos;
}
//如果要删除的字符  小于  当前存储的有效数据个数减去当前指向数据的位置
else
{
//将_str + pos + len(要保留的数据)位置的字符串直接拷贝到_str + pos(要删除的数据)位置直接覆盖掉
strcpy(_str + pos, _str + pos + len);
_size -= len;

}
}

 先进行判断len是否大于等于pos后面的元素个数,如果大于等于的话,就将pos位及其之后的元素全部删除,直接将pos位置置为'\0',然后将有效数据个数置为pos

如果len小于pos后面的元素个数的话,就将_str + pos + len位置的字符串直接拷贝到_str + pos位置,直接将要删除的那len个元素覆盖,最后有效数据更新即可


7. find

7.1 查找字符


//查找字符
//查找对应的字符,返回对应的下标
size_t string::find(char ch, size_t pos)
{
assert(pos < _size);

for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}

return npos;
}


7.2 查找字符串

strstr:在一个字符串中查找另一个字符串 ,返回子串在原串里第一个出现的位置
str1:原串       str2:子串
const char * strstr ( const char * str1, const char * str2 );


size_t string::find(const char* str, size_t pos)
{
assert(pos < _size);
//通过strstr函数来将str作为子串找到它的地址
const char* ptr = strstr(_str + pos, str);
//匹配失败
if (ptr == nullptr)
{
return npos;
}
else//匹配成功
{
//ptr的地址减去_str的地址就是我们要找的字符串的起始位置的下标
return ptr - _str;
}
}

通过strstr函数来将str作为子串找到它的地址,然后sub的地址减去_str的地址就是我们要找的字符串的起始位置的下标


8. substr 

//从pos位置开始截取长度为len的字符再构造一个子串,子串返回
string string::substr(size_t pos, size_t len)
{
assert(pos < _size);

// 如果len大于剩余字符长度
if (len > _size - pos)
{
//把len更新成为一个有效的长度,有多少长度取多少空间
len = _size - pos;
}
//构造一个子串sub
string sub;
//先给sub预留len个空间
sub.reserve(len);
//for循环遍历要截取的字符
for (size_t i = 0; i < len; i++)
{
//将从pos位置开始长度为len的字符尾插到sub中
sub += _str[pos + i];

}

return sub;
}

先判断len是否大于剩余字符的长度,如果len大于剩余字符的长度就把len更新成为一个有效的长度,有多少长度取多少空间,然后再构造一个子串sub,先给sub预留len个空间,然后通过遍历将从pos位置开始长度为len的字符尾插到sub中最后直接返回sub 


 代码汇总

string.h

 

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once

#include<iostream>
#include<string>
#include<assert.h>
using namespace std;

namespace bit
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;

iterator begin()
{
return _str;
}

iterator end()
{
return _str + _size;
}

const_iterator begin() const
{
return _str;
}

const_iterator end() const
{
return _str + _size;
}

/*string()
:_str(new char[1]{'\0'})
,_size(0)
,_capacity(0)
{}*/

// 短小频繁调用的函数,可以直接定义到类里面,默认是inline
string(const char* str = "")
{
_size = strlen(str);
// _capacity不包含\0
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}

// 深拷贝问题
// 
// s2(s1)
string(const string& s)
{
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}

// s2 = s1
// s1 = s1
string& operator=(const string& s)
{
if (this != &s)
{
delete[] _str;

_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}

return *this;
}

~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}

const char* c_str() const
{
return _str;
}

//将字符串的内容清空
void clear()
{
_str[0] = '\0';
_size = 0;
}

size_t size() const
{
return _size;
}

size_t capacity() const
{
return _capacity;
}

char& operator[](size_t pos)
{
assert(pos < _size);

return _str[pos];
}

const char& operator[](size_t pos) const
{
assert(pos < _size);

return _str[pos];
}

void reserve(size_t n);
void push_back(char ch);
void append(const char* str);
string& operator+=(char ch);
string& operator+=(const char* str);

void insert(size_t pos, char ch);
void insert(size_t pos, const char* str);
void erase(size_t pos, size_t len = npos);

size_t find(char ch, size_t pos = 0);
size_t find(const char* str, size_t pos = 0);
string substr(size_t pos = 0, size_t len = npos);
private: //设置私有,不允许随便访问底层数据

char* _str;//字符串存储空间首地址指针
size_t _size; //当前存储的有效数据个数
size_t _capacity;//可用容量

//static const size_t npos = -1;
static const size_t npos;
/*_str:指向字符串存放的空间的指针
_size:当前存储的有效数据个数 ,指向最后一个字符的下一个位置
_capacity:代表当前可存储的最大容量
nops:此值设置为 -1,无符号整型转换就是42亿,且此值为const和静态参数具有全局效应,某些函数设置其为缺省参数*/
};


bool operator<(const string& s1, const string& s2);
bool operator<=(const string& s1, const string& s2);
bool operator>(const string& s1, const string& s2);
bool operator>=(const string& s1, const string& s2);
bool operator==(const string& s1, const string& s2);
bool operator!=(const string& s1, const string& s2);

ostream& operator<<(ostream& out, const string& s);
istream& operator>>(istream& in, string& s);
}


string.cpp

 

#include"string.h"


//char* _str;字符串存储空间首地址指针
//size_t _size; 当前存储的有效数据个数
//size_t _capacity;可用容量
//static const size_t npos = -1;
/*_str:指向字符串存放的空间的指针
_size:当前存储的有效数据个数 ,指向最后一个字符的下一个位置
_capacity:代表当前可存储的最大容量
nops:此值设置为 -1,无符号整型转换就是42亿,且此值为const和静态参数具有全局效应,某些函数设置其为缺省参数*/


namespace bit
{
const size_t string::npos = -1;
//扩容
void string::reserve(size_t n)
{
//如果字符串存储的数据大于容量
if (n > _capacity)
{
//cout << "reserve:" << n << endl;
//申请新空间赋给临时变量tmp  + 1是位\0申请的空间
char* tmp = new char[n + 1];
//把数据拷贝到临时变量里
strcpy(tmp, _str);
//释放旧空间
delete[] _str;
//指向新空间
_str = tmp;
//字符串存储的数据等于容量
_capacity = n;
}
}

//尾插
void string::push_back(char ch)
{
//如果当前的有效数据个数和最大容量相等 
if (_size == _capacity)
{
//那么扩容
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
//因为size指向最后一个字符的下一个位置,所以直接插入
_str[_size] = ch;
++_size;
//最后要加一个\0,不然会乱码
_str[_size] = '\0';
}

//尾插一个字符串
//_str:指向字符串存放的空间的指针
//_size:当前存储的有效数据个数, 指向最后一个字符的下一个位置
void string::append(const char* str)
{
//先计算要插入的字符长度,将值赋给临时变量len
size_t len = strlen(str);
//如果当前存储的有效数据个数 加上 要插入的字符 大于 容量
if (_size + len > _capacity)
{
// 大于2倍,需要多少开多少,小于2倍按2倍扩
reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
}
//将目标地址到\0的位置中间的值拷贝过去
strcpy(_str + _size, str);
//尾插
_size += len;
}

//尾插一个字符
string& string:: operator+=(char ch)
{
push_back(ch);
return *this;
}

//尾插一个字符串
string& string::operator+=(const char* str)
{
append(str);
return *this;
}

//按pos位插入一个字符
void string::insert(size_t pos, char ch)
{
assert(pos <= _size);

//如果当前的有效数据个数和最大容量相等 
if (_size == _capacity)
{
//那么扩容
reserve(_capacity == 0 ? 4 : _capacity * 2);
}

// 挪动数据
//_size + 1:\0的后一位
//把end指向\0的后一位
size_t end = _size + 1;
//循环将pos之后的数据都向后挪动一位
while (end > pos)
{
_str[end] = _str[end - 1];
--end;
}
//将字符ch放入pos位
_str[pos] = ch;
++_size;
}

//按pos位插入一个字符串
void string::insert(size_t pos, const char* str)
{
assert(pos <= _size);
//先计算要插入的字符长度,将值赋给临时变量len
size_t len = strlen(str);
//如果当前存储的有效数据个数 加上 要插入的字符 大于 容量
if (_size + len > _capacity)
{
// 大于2倍,需要多少开多少,小于2倍按2倍扩
reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
}
//定义一个end指向_size + len的位置上
size_t end = _size + len;
//通过循环把pos后的len个数据向后挪动len个位置
while (end > pos + len - 1)
{
_str[end] = _str[end - len];
--end;
}

// 这里不能用 strcpy 会把 '\0' 拷贝过来
//使用memcpy函数进行拷贝的原因是memcpy函数不会自动补上'\0'
// 而strcpy函数会在拷贝后自动补上'\0’,
//把str里的len个数据拷贝到_str + pos里
memcpy(_str + pos, str, len);
//插入字符串
_size += len;
}

//从pos位置开始删除长度为的len的字符串
//_str:指向字符串存放的空间的指针
//_size:当前存储的有效数据个数, 指向最后一个字符的下一个位置
void string::erase(size_t pos, size_t len)
{
//判断是否会越界
assert(pos < _size);
//如果要删除的字符  大于等于  当前存储的有效数据个数 减去 当前指向数据的位置
if (len >= _size - pos)
{
//直接将pos位置置为\0
_str[pos] = '\0';
_size = pos;
}
//如果要删除的字符  小于  当前存储的有效数据个数减去当前指向数据的位置
else
{
//将_str + pos + len(要保留的数据)位置的字符串直接拷贝到_str + pos(要删除的数据)位置直接覆盖掉
strcpy(_str + pos, _str + pos + len);
_size -= len;

}
}

//查找字符
//查找对应的字符,返回对应的下标
size_t string::find(char ch, size_t pos)
{
assert(pos < _size);

for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}

return npos;
}

//查找字符串
//strstr:在一个字符串中查找另一个字符串 
// 返回子串在原串里第一个出现的位置
//str1:原串       str2:子串
//const char * strstr ( const char * str1, const char * str2 );
size_t string::find(const char* str, size_t pos)
{
assert(pos < _size);
//通过strstr函数来将str作为子串找到它的地址
const char* ptr = strstr(_str + pos, str);
//匹配失败
if (ptr == nullptr)
{
return npos;
}
else//匹配成功
{
//ptr的地址减去_str的地址就是我们要找的字符串的起始位置的下标
return ptr - _str;
}
}

//从pos位置开始截取长度为len的字符再构造一个子串,子串返回
string string::substr(size_t pos, size_t len)
{
assert(pos < _size);

// 如果len大于剩余字符长度
if (len > _size - pos)
{
//把len更新成为一个有效的长度,有多少长度取多少空间
len = _size - pos;
}
//构造一个子串sub
string sub;
//先给sub预留len个空间
sub.reserve(len);
//for循环遍历要截取的字符
for (size_t i = 0; i < len; i++)
{
//将从pos位置开始长度为len的字符尾插到sub中
sub += _str[pos + i];

}

return sub;
}


//string比较大小按照ascii码比
//strcmp:如果第一个数大于/小于/等于第二个数,那么返回>0/<0/0
//小于
bool operator<(const string& s1, const string& s2)
{
return strcmp(s1.c_str(), s2.c_str()) < 0;
}

//小于等于
bool operator<=(const string& s1, const string& s2)
{
return s1 < s2 || s1 == s2;
}

//大于
bool operator>(const string& s1, const string& s2)
{
return !(s1 <= s2);
}

//大于等于
bool operator>=(const string& s1, const string& s2)
{
return !(s1 < s2);
}

//等于
bool operator==(const string& s1, const string& s2)
{
return strcmp(s1.c_str(), s2.c_str()) == 0;
}

//不等于
bool operator!=(const string& s1, const string& s2)
{
return !(s1 == s2);
}



//流插入
ostream& operator<<(ostream& out, const string& s)
{
for (auto ch : s)
{
out << ch;
}

return out;
}



//流提取
istream& operator>>(istream& in, string& s)
{
s.clear();

const int N = 256;
char buff[N];
int i = 0;

char ch;
//in >> ch;
ch = in.get();
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == N - 1)
{
buff[i] = '\0';
s += buff;

i = 0;
}

//in >> ch;
ch = in.get();
}

if (i > 0)
{
buff[i] = '\0';
s += buff;
}

return in;
}


感谢观看~


原文地址:https://blog.csdn.net/hedhjd/article/details/142100023

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!