string类的模拟
目录
- string类的模拟
- 功能介绍
- 各功能的实现
- 类的构造函数,拷贝构造函数,析构函数
- 迭代器的实现
- string的内部容量访问成员函数
- string的修改成员函数
- string类的相关联函数
- string类的输入输出友元
- 汇总
- string功能的实现汇总
- 测试代码
功能介绍
namespace bit
{
class string
{
private:
char* _str;
size_t _capacity;
size_t _size;
public:
typedef char* iterator;
typedef const char*const_iterator;
static const int npos;
public:
string(){}
~string(){}
//各个成员函数的实现
}
}
const int bit::string::npos = -1;
实现思路:
- 迭代器的实现{typefef 一个指针,模拟指针,这里用的指针}
- 先实现构造函数{注意初始化的动态内存开辟}
这里还要注意如何实现缺省值(赋予nullptr? 赋予空)
,拷贝构造函数{开辟字符串长度大小的空间,strcpy内存复制} - c_str(){返回数组内容}
- 运算符重载 拷贝 {设计现代写法,利用临时变量在此函数调用其他已经实现的构造函数,与临时变量交换,实现拷贝。第二种普遍想法,开同等大小的空间(注意实际多出1来存‘/0’),内存复制函数}
- 析构函数{释放空间,成员变量归零}
- 扩容函数reserve{若空间不足,new,思想和栈,链表开空间一样,临时变量来做缓冲}
- 调整字符串大小函数resize{若参数的长度小于当前string的长度,便将[n]位置的数据赋予‘/0’
修改:
- 尾插push_pack{判断扩容,尾部插入字符}
- append{扩容,复制拷贝字符串}
- 插入函数insert(字符串,字符){扩容,对pos位置的字符往后排,在pos位置插入字符,或者插入字符串(strncpy,不会复制空字符)}
- 擦除erase{在pos位置删除之后的字符串}
- 交换swap{交换函数模板,各成员变量都交换}
- 查找find(字符,字符串)
判断pos位置的合理性,循环比较相等
字符串需要用到库函数strstr - 获取从某个位置开始的字符串substr{临时变量string来储存pos之后的字符串,注意的是,谨慎判断pos与_size和npos之间的关系大小
- 清除clear{_str[0]置空字符,大小变为零}
- [ ]
各功能的实现
类的构造函数,拷贝构造函数,析构函数
其中涉及到库函数,内存拷贝的4个库函数
-
void * memcpy ( void * destination, const void * source, size_t num );
返回值是destination,此函数是复制内存块,将字节数的值从源指向的位置直接复制到目标指向的内存块,不检查是否source是否有终止符号 -
void * memmove ( void * destination, const void * source, size_t num );
返回值是destination,此函数是移动内存块。将 num 字节的值从源指向的位置复制到目标指向的内存块。复制就像使用中间缓冲区一样进行,从而允许目标和源重叠 -
char * strcpy ( char * destination, const char * source );
返回值是destination,此函数的复制包括结尾的‘/0’ -
char * strncpy ( char * destination, const char * source, size_t num ); 返回值是
destination,此函数是复制字符串,复制n个字符至des -
构造,拷贝构造
//不建议的写法
string():_str(new char[1]),_capacity(0),_size(0)
{
_str[0] = '/0';
}
string(const char* s):_capacity(strlen(s))
{
_str = new char[_capacity + 1];
_size = _capacity;
strcpy(_str, s);
}
string(const char* str = ""):_capacity(strlen(str))//缺省值不给字符,也就是空字符串
{
_str = new char[_capacity + 1];//多开一个空间给‘/0’
_size = _capacity;
strcpy(_str, str);
}
string(const string& s)
{
_str = new char[s._capacity + 1];
_capacity = s._capacity;
_size = s._size;
strcpy(_str, s._str);
}
string& operator=(const string& s)
{
_str = new char[s._capacity + 1];
_capacity = s._capacity;
_size = s._size;
strcpy(_str, s._str);
return *this;
}
- 现代写法:在实现了一个构造函数,在此基础上调用构造函数来实现另一个构造函数
string& operator=(const string& s)
{
//_str = new char[s._capacity + 1];
//_capacity = s._capacity;
//_size = s._size;
//strcpy(_str, s._str);
//return *this;
string tem(s);
swap(tem);//这里交换要注意一开始给成员变量初始值
return *this;
}
注意:交换后临时开辟的变量若对成员变量不初始化,临时变量就会变成野指针,在析构可能出现异常
- 析构函数
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
迭代器的实现
string的迭代器这里用的是指针实现,迭代器用指针是比较容易实现的,只需返回指针即可
// iterator
typedef char* iterator;
typedef const char*const_iterator;
const_iterator begin()const
{
return _str;
}
const_iterator end()const
{
return _str + _size;
}
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
实现了迭代器,那么for循环也可以使用,其实for循环的本质也是用的迭代器的原理,表面是任意变量的拷贝,底层是迭代器的转换
void test()
{
bit::string s2("abcdefg");
bit::string::iterator begin = s2.begin();
while (begin != s2.end())
{
(*begin)++;
cout << *begin;
begin++;
}
cout << endl;
for (auto ch : s2)
{
cout << ch;
}
}
string的内部容量访问成员函数
- string大小容量的实现
// capacity
size_t size()const
{
return _size;
}
size_t capacity()const
{
return _capacity;
}
bool empty()const
{
return _size == 0;
}
- 在实现resize和reserve成员函数,应该充分了解他们的底层逻辑,resize只增容不缩容,若参数大于当前容量,扩容并且以参数char c来填充扩容后的内容。
reserve是用来为其他成员函数扩容的,不缩容,所有扩容函数构造函数都会多开一个空间来留个‘\0’这个空字符
void resize(size_t n, char c = '\0')
{
if (n <= _capacity)
{
_str[n] = '\0';
_size = n;
}
else {
reserve(n);
for (size_t i = _size;i < n; i++)
{
_str[i] = c;
}
_str[n] = '\0';
_size = n;
}
}
void reserve(size_t n)
{
if (n > _capacity)
{
char* tem = new char[n + 1];//多开一个空间
strcpy(tem, _str);
delete[] _str;
_str = tem;
_capacity = n;
}
}
string的修改成员函数
string的修改成员函数
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
- 实现插入,增加的原理是一样的:
先判断是否扩容,然后插入字符,或者strcpy插入字符串
void push_back(char c)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 * _capacity);
}
_str[_size++] = c;
_str[_size] = '\0';
}
string& operator+=(char c)
{
push_back(c);
return *this;
}
void append(const char* str)
{
int len = strlen(str);
if(_size+len>_capacity||_size >_capacity - len)//最大值溢出问题
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size = _size + len;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
string& operator+=(const string s)
{
reserve(_size + s._size);
strcpy(_str + _size, s._str);
_size = _size + s._size;
return *this;
}
- 清除函数和交换函数也是很容易实现的
清除成员函数不需要考虑释放空间,只需要将第一个有效字符设置为’\0’即可
而交换函数可以利用库函数的模板来实现,将各个成员变量进行交换
void clear()
{
_str[0] = '\0';
_size = 0;
}
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_capacity, s._capacity);
std::swap(_size, s._size);
}
在swap中应用中还是不太普遍,需要s1.swap(s2) ,看起来还是不够使用,也不像库函数那样,所以,可以再重载一个全局函数,这里注意是在类域外,全局处重载函数
void swap(string& s1, string& s2)
{
s1.swap(s2);
}
- 返回字符串函数,返回成员变量_str(开辟空间的地址)即可
const char* c_str()const
{
return _str;
}
string类的相关联函数
- find成员函数的实现
// 返回c在string中第一次出现的位置
size_t find(char c, size_t pos = 0) const
{
//pos合理
assert(pos < _size);
for (size_t i = pos;i < _size;i++)
{
if (_str[i] == c)
return i;
}
return npos;
}
find的字符串的实现
这里用到了库函数strstr
const char * strstr ( const char * str1, const char * str2 );
此函数查找子字符串,若匹配了,返回指针,不匹配返回空指针
// 返回子串s在string中第一次出现的位置
size_t find(const char* s, size_t pos = 0) const
{
assert(pos < _size);
char* ch = strstr(_str, s);
if (ch)
{
//返回俩个指针的差值,就是当前的位置
return ch - _str;
}
else
{
return npos;
}
}
insert插入函数的实现
在pos位置上插入字符c/字符串str,并返回该字符的位置
string& insert(size_t pos, char c)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 * _capacity);
}
for (size_t i = _size+1;i > pos;i--)
{
_str[i] = _str[i-1];
}
_str[pos] = c;
_size++;
return* this;
}
string& insert(size_t pos, const char* str)
{
assert(pos <= _size);
//和append实现思路一样
size_t len = strlen(str);
if (_size > _capacity - len || _size+len >_capacity )//最大值溢出的问题
{
reserve(_size + len);
}
for (size_t i = _size + len;i > pos + len -1 ;i--)
{
_str[i] = _str[i - len];
}
strncpy(_str + pos, str,len);
_size = _size + len;
return *this;
}
erase擦除函数,将pos位置及后面的总计len个字符擦除
// 删除pos位置上的元素,并返回该元素的下一个位置
string& erase(size_t pos, size_t len=npos)
{
assert(pos < _size);//不用取等,最后一个_size位置是不能擦除的,'\0'
if (len == npos || len > _size - pos-1)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}
string比较函数的实现可以借助库函数的strcmp
int strcmp ( const char * str1, const char * str2 );
此函数将俩个字符串进行比较,相等返回0 ,str1小于str2返回一个负数(反之)
string类的输入输出友元
注意:此类函数的实现都是基于类域的外面,也就是全局
输出流cout
输入流cin
输入流的实现思路有很多种优化。
很普遍的实现是将输入的字符一个一个塞进string的实例中,一边塞一边扩容 ,获取输入字符可以借助库函数get()
//istream& operator>>(istream& _cin, bit::string& s)
//{
// //清空当前的内容
// s.clear();
// char ch;
// ch = _cin.get();
// s.reserve(64);
// while (ch != ' ' && ch != '\n')//当遇到enter和空格就停止获取
// {
// s.push_back(ch);
// ch = _cin.get();
// }
// return _cin;
//}
优化:用一个大小固定的临时数组来存储当前输入字符,当输入结束或者数组已满,就赋值给string,将数组的有效字符重置,继续下一轮(如果还有输入字符)
istream& operator>>(istream& _cin, bit::string& s)
{
s.clear();
char ch;
ch = _cin.get();
char c[128];
size_t i = 0;
while (ch != ' ' && ch != '\n')
{
c[i++] = ch;
//当数组满时
if (i == 127)
{
c[127] = '\0';//注意在这个位置赋予结束字符,否则会出现随机字符
s += c;
i = 0;
}
ch = _cin.get();
}
if (i > 0)
{
c[i] = '\0';//注意先给结束字符,否则会将上一次循环的已无效字符或随机字符,+=进入string
s += c;
}
return _cin;
}
汇总
string功能的实现汇总
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<assert.h>
using namespace std;
namespace bit
{
class string
{
private:
char* _str = nullptr;
size_t _capacity = 0;
size_t _size = 0;
friend ostream& operator<<(ostream& _cout, const bit::string& s);
friend istream& operator>>(istream& _cin, bit::string& s);
public:
typedef char* iterator;
typedef const char*const_iterator;
static const int npos;
public:
//string() :_str(new char[1]), _capacity(0), _size(0)
//{
// _str[0] = '/0';
//}
//string(const char* s) :_capacity(strlen(s))
//{
// _str = new char[_capacity + 1];
// _size = _capacity;
// strcpy(_str, s);
//}
string(const char* str = ""):_capacity(strlen(str))//缺省值不给字符,也就是空字符串
{
_str = new char[_capacity + 1];//多开一个空间给‘/0’
_size = _capacity;
strcpy(_str, str);
}
string(const string& s)
{
_str = new char[s._capacity + 1];
_capacity = s._capacity;
_size = s._size;
strcpy(_str, s._str);
}
string& operator=(const string& s)
{
//_str = new char[s._capacity + 1];
//_capacity = s._capacity;
//_size = s._size;
//strcpy(_str, s._str);
//return *this;
string tem(s);
swap(tem);//这里交换要注意一开始给成员变量初始值
return *this;
}
//俩种现代写法,在完成其他成员函数的基础上
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
// iterator
const_iterator begin()const
{
return _str;
}
const_iterator end()const
{
return _str + _size;
}
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
// modify
void push_back(char c)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 * _capacity);
}
_str[_size++] = c;
_str[_size] = '\0';
}
string& operator+=(char c)
{
push_back(c);
return *this;
}
void append(const char* str)
{
size_t len = strlen(str);
if (_size >_capacity - len||_size+len>_capacity)//最大值溢出问题
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size = _size + len;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
string& operator+=(const string s)
{
reserve(_size + s._size);
strcpy(_str + _size, s._str);
_size = _size + s._size;
return *this;
}
void clear()
{
_str[0] = '\0';
_size = 0;
}
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_capacity, s._capacity);
std::swap(_size, s._size);
}
const char* c_str()const
{
return _str;
}
// 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)
{
_str[n] = '\0';
_size = n;
}
else {
reserve(n);
for (size_t i = _size;i < n; i++)
{
_str[i] = c;
}
_str[n] = '\0';
_size = n;
}
}
void reserve(size_t n)
{
if (n > _capacity)
{
char* tem = new char[n + 1];
strcpy(tem, _str);
delete[] _str;
_str = tem;
_capacity = n;
}
}
access
char& operator[](size_t index)
{
return *(_str + index);
}
const char& operator[](size_t index)const
{
return *(_str + index);
}
//relational operators
bool operator<(const string& s)
{
if (strcmp(c_str(), s.c_str()) < 0)
{
return true;
}
return false;
}
bool operator<=(const string& s)
{
return *this == s || *this < s;
}
bool operator>(const string& s)
{
return !(*this <= s);
}
bool operator>=(const string& s)
{
return !(*this < s);
}
bool operator==(const string& s)
{
if (strcmp(c_str(), s.c_str()) == 0)
return true;
return false;
}
bool operator!=(const string& s)
{
return !(*this == s);
}
// 返回c在string中第一次出现的位置
size_t find(char c, size_t pos = 0) const
{
//pos合理
assert(pos < _size);
for (size_t i = pos;i < _size;i++)
{
if (_str[i] == c)
return i;
}
return npos;
}
// 返回子串s在string中第一次出现的位置
size_t find(const char* s, size_t pos = 0) const
{
assert(pos < _size);
char* ch = strstr(_str, s);
if (ch)
{
//返回俩个指针的差值,就是当前的位置
return ch - _str;
}
else
{
return npos;
}
}
在pos位置上插入字符c/字符串str,并返回该字符的位置
string& insert(size_t pos, char c)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 * _capacity);
}
for (size_t i = _size+1;i > pos;i--)
{
_str[i] = _str[i-1];
}
_str[pos] = c;
_size++;
return* this;
}
string& insert(size_t pos, const char* str)
{
assert(pos <= _size);
//和append实现思路一样
size_t len = strlen(str);
if (_size > _capacity - len || _size+len >_capacity )//最大值溢出的问题
{
reserve(_size + len);
}
for (size_t i = _size + len;i > pos + len -1 ;i--)
{
_str[i] = _str[i - len];
}
strncpy(_str + pos, str,len);
_size = _size + len;
return *this;
}
// 删除pos位置上的元素,并返回该元素的下一个位置
string& erase(size_t pos, size_t len=npos)
{
assert(pos < _size);//不用最后一个pos是不能擦除的,‘\0'
if (len == npos || len > _size - pos-1)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}
};
const int bit::string::npos = -1;
void swap(string& s1, string& s2)
{
s1.swap(s2);
}
ostream& operator<<(ostream& _cout, const bit::string& s)
{
for (auto ch : s)
{
_cout << ch;
}
return _cout;
}
//istream& operator>>(istream& _cin, bit::string& s)
//{
// //清空当前的内容
// s.clear();
// char ch;
// ch = _cin.get();
// s.reserve(64);
// while (ch != ' ' && ch != '\n')//当遇到enter和空格就停止获取
// {
// s.push_back(ch);
// ch = _cin.get();
// }
// return _cin;
//}
istream& operator>>(istream& _cin, bit::string& s)
{
s.clear();
char ch;
ch = _cin.get();
char c[128];
size_t i = 0;
while (ch != ' ' && ch != '\n')
{
c[i++] = ch;
//当数组满时
if (i == 127)
{
c[127] = '\0';//注意在这个位置赋予结束字符,否则会出现随机字符
s += c;
i = 0;
}
ch = _cin.get();
}
if (i > 0)
{
c[i] = '\0';//注意先给结束字符,否则会将上一次循环的已无效字符或随机字符,+=进入string
s += c;
}
return _cin;
}
}
测试代码
#include"imitate_string.h"
void test1()
{
//bit::string s1;
bit::string s2("abcdefg");
bit::string::iterator begin = s2.begin();
while (begin != s2.end())
{
(*begin)++;
cout << *begin;
begin++;
}
cout << endl;
for (auto ch : s2)
{
cout << ch;
}
//cout << s2[2] << endl;
}
void test2()
{
bit::string s1("abcdefg");
//s1.resize(4);
//s1.resize(5, 'i');
//cout << s1.c_str() << endl;
//bit::string s2;
//s2.resize(10, '1');
//s1.push_back('2');
s1.append("you donot know .... ,just nothing");
cout << s1.c_str() << endl;
}
void test3()
{
bit::string s1;
bit::string s2("ijustlovebutitwillbenothing");
bit::string s3 = s2;
s3 += s2;
s2 += "itjustnothing OK";
cout << s3.c_str() << endl;
cout << s2.c_str() << endl;
s1.swap(s2);
cout << s1.c_str() << endl;
s1.clear();
cout << s1.c_str() << endl;
}
void test4()
{
bit::string s1("abadfadf");
//bit::string s2("it just be ok,i think i only gave up");
//swap(s1, s2);
//bit::string s3 = s2;
//cout << s1.c_str() << endl << s2.c_str() <<s3.c_str()<< endl;
}
void test5()
{
bit::string s1("abadfadf");
bit::string s2("ac");
size_t i = s1.find('b');
//s1.insert(i, '5');
//s2.insert(0,"12345");
s1.erase(i, 2);
s2.erase(0);
s2 = s1;
s2.erase(3);
cout << s1.c_str() << endl;
cout << s2.c_str();
}
void test6()
{
bit::string s1("qwert");
bit::string s2("qwert");
bit::string s3("qert");
//cout << (s1 < s2) << endl;//0
//cout << (s1 >= s2) << endl;//1
//cout << (s3 <= s2) << endl;//1
cout << s1 << endl;
cin >> s2;
cout << s2;
bit::swap(s2, s1);
cout << s1;
}
int main()
{
//using namespace bit;
//bit::string s1();
//bit::string s2("abcdefg");
//test5();
//test6();
bit::string s1("abka");
cin >> s1;
//s1.append("ab");
cout << s1;
return 0;
}