一、模拟实现string的基本功能列表,以及直接写在头文件中的短小频繁调用的被编译器视为内联函数的函数定义
namespace wjl
{
class string
{
friend ostream& operator<<(ostream& out, const string& s);
friend istream& operator>>(istream& in, string& s);
public:
typedef char* iterator;//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;
}
//短小、频繁调用的函数放在类里面,默认是inline,其他的要声明、定义分离
//这里是默认构造,但是只有使用缺省值构造的才算调用了默认构造
string(const char* str = "")
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);//先拷贝,再判断,遇到\0就停止
}
/*string(const string& s)
{
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}*/
void swap(string& tmp)
{
std::swap(tmp._str, _str);
std::swap(tmp._size, _size);
std::swap(tmp._capacity, _capacity);
}
//引用计数:全称是引用计数的写时拷贝,用于浅拷贝,引用计数是指向这片空间的对象的个数
//一个析构时,引用计数-1,当最后引用计数是零时,将这片区域析构
//但是当需要有一个拷贝对象去写时,有这个对象负责再开辟空间进行拷贝
string(const string& s)
{
string tmp(s._str);//这里使用的前面的构造函数,但这里使用了参数,所以不是使用的默认构造,而是使用的带参构造
swap(tmp);
}
~string()
{
if (_str)
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
}
const char* c_str() const//将std::string转化为C语言风格字符串,即const char*
{
return _str;
}
void clear()//清掉所有数据,不清除空间
{
_str[0] = '\0';
_size = 0;
}
size_t size() const//这里加const是确保对象不会被改变
{
return _size;
}
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);
string& operator=(string tmp);
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 = nullptr;
size_t _size = 0;
size_t _capacity = 0;
//static const可以在声明里初始化,但是static就只能走声明定义分离
static const size_t npos;
};
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);
}
二、以上各功能的具体代码实现
——其中包括一些功能的现代写法,利用构造函数来方便书写代码,如赋值运算符重载
namespace wjl
{
const size_t string::npos = -1;
void string::reserve(size_t n)
{
if (n > _capacity)
{
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 : 2 * _capacity);
}
_str[_size++] = ch;
_str[_size] = '\0';
}
string& string::operator+= (char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : 2 * _capacity);
}
_str[_size++] = ch;
_str[_size] = '\0';
return *this;
}
void string::append(const char* str)
{
size_t len = strlen(str);
if (_size + len >= _capacity)
{
reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
}
strcpy(_str + _size, str);
_size += len;
}
string& string::operator+=(const char* str)
{
append(str);
return *this;
}
void string::insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_size == 0 ? 4 : 2 * _capacity);
}
int end = _size + 1;//或者采取强转的方式
while (end > pos)//C语言在这里留了一个坑——隐式类型转换
{
_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 (len == 0)
{
return;
}
if (_size + len >= _capacity)
{
reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
}
int end = _size + len;
while (end > pos + len - 1)
{
_str[end] = _str[end - len];
end--;
}
for (int i = 0; i < len; i++)
{
_str[pos + i] = str[i];
}
}
void string::erase(size_t pos, size_t len)
{
if (pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
for (size_t i = pos + len; i <= _size; i++)
{
_str[i - len] = _str[i];
}
_size -= len;
}
}
size_t string::find(char ch, size_t pos)
{
for (int i = 0; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
size_t string::find(const char* str, size_t pos)
{
assert(pos < _size);
const char* ptr = strstr(_str + pos, str);//从哪个位置开始找哪个字符串
if (str == nullptr)
{
return npos;
}
else
{
return ptr - _str;
}
}
string string::substr(size_t pos, size_t len)
{
assert(pos < _size);
if (len > _size - pos)
{
len = _size - pos;
}
string sub;
sub.reserve(len);
for (int i = 0; i < len; i++)
{
sub.push_back(_str[i + pos]);
}
return sub;
//这里如果使用简单的传值返回的话,编译器会进行优化,将sub进行浅拷贝
//而sub进行释放后,会使原字符串无效
}
//string& string::operator=(const string& s)
//{
/*if (_str == s._str)
{
return *this;
}
delete[] _str;
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
return *this;*/
//if (this != &s)
//{
// string tmp(s._str);//调用构造,tmp就是我们用来去赋值的对象
// swap(tmp);//而这里是将tmp与我们的this——即被赋值的对象进行交换
//}
//return *this;
//}
string& string::operator=(string tmp)
{
swap(tmp);//这种现代写法核心就是找一个工具人来帮我们干活
return *this;
}
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 strcmp(s1.c_str(), s2.c_str()) <= 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 strcmp(s1.c_str(), s2.c_str()) == 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 strcmp(s1.c_str(), s2.c_str()) > 0;
}
ostream& operator<<(ostream& out, const string& s)
{
for (int i = 0; i < s._size; i++)
{
out << s._str[i];
}
return out;
}
istream& operator>>(istream& in, string& s)
{
s.clear();
const int N = 256;
char buff[N];//这里的buff用完即释放,而且在栈上开辟空间很快
int i = 0;
char ch;
ch = in.get();//cin不能用于char类型,cin获取不到换行或者空格,get将空格和换行符视为分隔符
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == N - 1)
{
buff[i] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
/*istream& operator>>(istream& in, string s)
{
s.clear();
const int N = 256;
char buff[N];
int i = 0;
char ch = in.get();
while (ch != '\n')
{
buff[i++] = ch;
if (i == N - 1)
{
buff[N - 1] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i != 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}*/