string模拟实现
定义
char* _str;
size_t _size;
size_t capacity;//只是有效字符
构造函数
string(const char* str)
:_size(strlen(str))
,_capacity(_size)
{
_str = new char[_capacity + 1];//+1的原因是字符串后有\0
strcpy(_str, str);
}
析构函数
~string()
{
delete[]_str;
_str = nullptr;
_size = 0;
_capacity = 0;
}
拷贝构造
如果我们不写拷贝构造,编译器默认的是浅拷贝
pea::string s1("hello world");
pea::string s2(s1);
我们执行代码就会发现崩溃,因为s2和s1指向的是同一个储存空间。当程序调用析构函数时,s2先把那块内存释放掉。此时s1指向的是一块被释放掉的空间,当再次调用析构函数时,再次释放就会报错
指向的是同一个内存地址
string(const string& s)//拷贝构造
:_str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
}
现代写法
用swap会比strcpy快
string(const string& s)//现代写法
:_str(nullptr)//不加这一句会引发崩溃
{
string tmp(s._str);
swap(_str, tmp._str);
}
operater=
string& operator = (const string& s)
{
if (this != &s)//当没有这个条件语句,自己给自己赋值就会出现错误
{
char* tmp = new char[s.capacity + 1];
strcpy(tmp, s._str);
delete[]_str;
_str = tmp;
}
return *this;
}
现代写法
string& operator=(string s)//赋值的现代写法
{
swap(_str, s._str);
return *this;
}
现代写法是函数的复用
基本的string类就已经完成了,接下来就是进行完善
全缺省构造函数
string()
:_str(new char[1])
, _size(0)
, _capacity(0)
{
_str[0] = '\0';
}
错误写法
string()
:_str(nullptr)
, _size(0)
, _capacity(0)
{}
这样实际运行会导致崩溃,但LINUX不会,具体原因比较复杂,了解即可。
可以对构造函数进行改进
原:
string()
:_str(new char[1])
, _size(0)
, _capacity(0)
{
_str[0] = '\0';
}
string(const char* str)//构造函数
:_size(strlen(str))
,_capacity(_size)
{
_str = new char[_capacity + 1];//+1的原因是字符串后有\0
strcpy(_str, str);
}
现:
string(const char* str="")//构造函数,前面提到,不能给nullptr,而是给"",相当于默认\0
:_size(strlen(str))
,_capacity(_size)
{
_str = new char[_capacity + 1];//+1的原因是字符串后有\0
strcpy(_str, str);
}
operator[]
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
迭代器
typedef char* iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
size
size_t size() const//一般不会被修改,所以是const
{
return _size;
}
reserve改变容量函数
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];;
strcpy(tmp, _str);
delete[]_str;
_str = tmp;
_capacity = n;
}
}
本质上是开一块新的空间,把原来的字符串拷贝过来,然后释放原来的空间
pushback
void push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
append
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size += len;
}
operator+=
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
resize
void resize(size_t n, char ch = '\0')
{
if (n <= _size)
{
_size = n;
_str[_size] = '\0';
}
else
{
if (n > _capacity)
{
reserve(n);
}
memset(_str + _size, ch, n - _size);
_size = n;
_str[_size] = '\0';
}
}
resize和reserve的区别:
resize既分配了空间,也创建了对象,可以通过下标访问。当resize的大小
reserve只修改capacity大小,不修改size大小,resize既修改capacity大小,也修改size大小。
find
需要加一个静态成员变量
private:
char* _str;
size_t _size;
size_t _capacity;
static const size_t npos;
insert
string& insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = ch;
++_size;
return *this;
}
erase
string& erase(size_t pos = 0, size_t len = npos)
{
assert(pos < _size);
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return*this;
}
opearator 各种字符串比较
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 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 !(s1 == s2);
}
流输出和流插入
ostream& operator<<(ostream& out, const string& s)
{
for (size_t i = 0; i < s.size(); ++i)
{
out << s[i];
}
return out;
}
istream& operator>>(istream& in, string& s)
{
s.clear();
char ch = in.get();
while (ch != ' ' && ch != '\n')
{
s += ch;
ch = in.get();
}
return in;
}
注意:类内运算符重载时只能有且仅有一个参数;类内其他函数可以有多个参数。,所以我们需要把流输出和流插入和各种字符串比较改成全局函数
最后代码
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#include<string.h>
#include<assert.h>
#include<iostream>
using namespace std;
namespace pea
{
class string
{
public:
typedef char* iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
string(const char* str = "")//构造函数,传统写法
:_size(strlen(str))
, _capacity(_size)
{
_str = new char[_capacity + 1];//+1的原因是字符串后有\0
strcpy(_str, str);
}
//string(const string& s)//拷贝构造,传统写法
// :_size(s._size)
// ,_capacity(s._capacity+1)
//{
// _str=new char[strlen(s._str) + 1];
// strcpy(_str, s._str);
//}
string(const string& s)//现代写法
:_str(nullptr)//不加这一句会引发崩溃
{
string tmp(s._str);
swap(_str, tmp._str);
}
//string& operator = (const string& s)
//{
// if (this != &s)//当没有这个条件语句,自己给自己赋值就会出现错误
// {
// char* tmp = new char[s._capacity + 1];
// strcpy(tmp, s._str);
// delete[]_str;
// _str = tmp;
// _size = s._size;
// _capacity = s._capacity;
// }
// return *this;
//}
string& operator=(string s)//赋值的现代写法
{
swap(_str, s._str);
return *this;
}
~string()
{
delete[]_str;
_str = nullptr;
_size = 0;
_capacity = 0;
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
size_t size() const
{
return _size;
}
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];;
strcpy(tmp, _str);
delete[]_str;
_str = tmp;
_capacity = n;
}
}
void resize(size_t n, char ch = '\0')
{
if (n <= _size)
{
_size = n;
_str[_size] = '\0';
}
else
{
if (n > _capacity)
{
reserve(n);
}
memset(_str + _size, ch, n - _size);
_size = n;
_str[_size] = '\0';
}
}
void push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size += len;
}
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
size_t find(char ch)
{
for (size_t i = 0; i < _size; ++i)
{
if (ch == _str[i])
{
return i;
}
}
return npos;
}
size_t find(const char* s, size_t pos = 0)
{
const char* ptr = strstr(_str + pos, s);
if (ptr == nullptr)
{
return npos;
}
else
{
return ptr - _str;
}
}
string& insert(size_t pos, char* s)
{
assert(pos <= _size);
size_t len = strlen(s);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
size_t end = _size + 1;
while (end > pos + len)
{
_str[end] = _str[end - 1];
--end;
}
strncpy(_str + pos, s, len);
_size += len;
return *this;
}
string& erase(size_t pos = 0, size_t len = npos)
{
assert(pos < _size);
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return*this;
}
private:
char* _str;
size_t _size;
size_t _capacity;
static const size_t npos;
};
}
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 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 !(s1 == s2);
}
ostream& operator<<(ostream& out, const string& s)
{
for (size_t i = 0; i < s.size(); ++i)
{
out << s[i];
}
return out;
}
istream& operator>>(istream& in, string& s)
{
s.clear();
char ch = in.get();
while (ch != ' ' && ch != '\n')
{
s += ch;
ch = in.get();
}
return in;
}