文章介绍:
本文主要是模拟实现String类,可以更好地理解C++中的类与对象、STL等使用。
准备工作:
包括引入头文件,编写类中成员属性以及构造函数、析构函数。
String类的模拟实现可以借助C语言中str系列的函数,数据类型也沿用C语言。
根据STL中String类的实现,可以明确成员属性包括String的长度、容量以及char*表示字符串。
#pragma once
//引入C语言头文件
#include <string.h>
//引入C++头文件
#include <iostream>
using namespace std;
namespace LZ
{
class String
{
public:
//缺省构造
String(const char* str = "")
:_size(strlen(str)),
_capacity(_size),
_str(new char[_capacity + 1])
{
strcpy(_str, str);
}
//无参构造
String()
:_size(1),
_capacity(1)
{
//不能初始化为空指针,否则在打印时会出现崩溃
_str = new char[1];
_str[0] = '\0';
//不能这么写
//_str = "";
}
//析构函数
~String()
{
delete[] _str;
_str[0] = '\0';
_size = 1;
_capacity = 1;
}
private:
size_t _size;
size_t _capacity;
char* _str;
};
}
提供返回成员属性的接口:
这里在参数部分加上const 是为了权限的平移。
//返回字符串
char* c_str()const
{
return _str;
}
//返回字符串长度
size_t size()const
{
return _size;
}
操作符[ ] 的重载:
[ ] 可以认为是解引用,主要用来访问字符串某一位置的字符。
有两种形式。
//可以读写
char& operator[](int pos)
{
assert(pos < _size);
return _str[pos];
}
//只可以读
char& operator[](int pos) const
{
assert(pos < _size);
return _str[pos];
}
迭代器的实现:
在C++中有个很神奇的东西,它可以很方便地访问容器里的数据,它的名字叫迭代器。
在这里,迭代器的实现我只是用了char*。
//迭代器的实现
//可以读写
typedef char* iterator;
//返回字符串的第一个字符
iterator begin()
{
return &(_str[0]);
}
//最后一个字符的下一个字符
iterator end()
{
return &(_str[_size]);
}
//缺省构造
String(const char* str = "")
:_size(strlen(str)),
_capacity(_size),
_str(new char[_capacity + 1])
{
strcpy(_str, str);
}
//迭代器的实现
//只可以读
typedef const char* const_iterator;
const_iterator const_begin()
{
return &(_str[0]);
}
//最后一个字符的下一个字符
const_iterator const_end()
{
return &(_str[_size]);
}
追加字符/字符串:
//在追加字符时判断是否需要扩容
void reserve(int n)
{
if (n > _capacity)
{
_capacity = n;
char* temp = new char[_capacity + 1];
strcpy(temp, _str);
delete[] _str;
_str = temp;
}
}
void push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 1 ? 4 : _capacity * 2);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
}
void append(const char* str)
{
assert(str);
reserve(strlen(str) + _size);
strcpy(_str + _size, str);
_size += strlen(str);
}
String& operator+=(char ch)
{
push_back(ch);
return *this;
}
const String& const operator+=(const char* str)
{
append(str);
return *this;
}
//插入字符/字符串
void insert(size_t pos, const char* str)
{
size_t end = _size;
size_t n = strlen(str);
while (pos <= end && end != string::npos)
{
_str[end + n] = _str[end];
end--;
}
strcpy(_str + pos, str);
_size += n;
}
删除字符/字符串:
如果没有传入要删的字符多少,默认自指定位置开始全删除。
void erase(size_t pos, size_t len = string::npos)
{
assert(pos < _size);
if (pos + len >= _size || len == string::npos)
{
_str[0] = '\0';
}
else
{
size_t end = pos + len;
while(end <= _size)
{
_str[pos++] = _str[end++];
}
_size -= len;
}
}
寻找字符串:
返回子串在字符串的第一个字符的位置。
int find(const char* str)
{
assert(str);
const char* p = strstr(_str, str);
if (p)
{
return p - _str;
}
else
{
return -1;
}
}
修改字符串:
在指定位置小于字符串原有的大小时,那会缩小;
在指定位置大于字符串原有的大小时,先检查是否需要扩容,然后从原有字符串的最后一个字符开始重置为指定字符。
void resize(int pos, char ch = '\0')
{
assert(pos < _size);
if (pos < _size)
{
_size = pos;
_str[_size] = '\0';
}
else
{
reserve(pos);
for (int i = _size; i < pos; i++)
{
_str[i] = ch;
}
_size = pos;
_str[_size] = '\0';
}
}
流输入和流提取的重载:
ostream& operator<<(ostream& out, const String& str)
{
for (int i = 0; i < str._size; i++)
{
out << str[i];
}
out << endl;
return out;
}
istream& operator>>(istream& in, String& str)
{
//不能这么写,不仅因为是私有属性,还是因为不知道str的空间大小多少,不能一下子全输入
//同理字符数组
//in >> s._str;
char ch;
//in >> ch;
//这样写不对,流输入本身就不会提取到空格符和换行符
/*while (ch != ' ' && ch != '\n')
{
str += ch;
in >> ch;
}*/
//可以这么写
//get方法可以获取空格符
/*ch = in.get();
while (ch != ' ' && ch != '\n')
{
str += ch;
ch = in.get();
}*/
//优化方案,由于不清楚输入字符到底有多少,又不想反复调用类中的扩容方法
//因此采用缓冲池的思想,先将一定的字符存储起来,再根据情况提取到字符串中
char buff[64] = { '\0' };
ch = in.get();
int i = 0;
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == 63)
{
buff[i] = '\0';
str += buff;
i = 0;
}
ch = in.get();
}
if (i != 0)
{
buff[i] = '\0';
str += buff;
}
}
比较操作符的重载:
bool operator<(const String& str)
{
bool res = memcmp(_str, str._str, _size < str._size ? _size : str._size);
return res == 0 ? _size < str._size : res < 0;
}
bool operator==(const String& str)
{
return _size == str._size && memcmp(_str, str._str, _size < str._size ? _size : str._size);
}
bool operator!=(const String& str)
{
return !(*this == str);
}
bool operator<=(const String& str)
{
return *this < str || *this == str;
}
bool operator>(const String& str)
{
return !(*this <= str);
}
bool operator>=(const String& str)
{
return !(*this < str);
}
赋值运算符的重载:
void swap(String& str)
{
std::swap(_str, str._str);
std::swap(_size, str._size);
std::swap(_capacity, str._capacity);
}
String& operator=(String& str)
{
//第一种写法
/*if (this != &str)
{
//整个过程str仅仅只是被temp拷贝构造,再通过赋值str深拷贝到temp
//而temp只是局部变量,出作用域就会自动调用析构函数,会成为“废品”,不如做交换
String temp(str);
swap(str);
}*/
//简洁写法
swap(str);
return *this;
}