C++中string的模拟和实现
目录
1.模拟实现string基本的定义与构造
2.模拟实现string的各种函数
1.扩容reserve()
2.push_back()尾插
3.resize()对元素数量进行修改
4.append()在末端插入字符串
5.find()从pos处开始查找字符,并返回第一次出现的位置
6.insert()在pos位置插入一个字符
7.erase()删除pos上的元素并返回
8.str()、size()、capacity()、empty()获取数据存放的位置,数量,容器的容量以及判空
3.迭代器
1.begin()
2.end()
4.各种运算符的重载
1.由于大于小于等判断运算符基本可以复用,这里只写出>=、<=、==
2.[]运算符
3.<<,>>流插入流提取运算符
1.模拟实现string基本的定义与构造
c++中string是STL容器的一种,其数据类型为字符类型,所以,我们在定义中需要用char* _str来存放数据,用size_t来定义两个变量分别为_size、_capacity,其中_size为存放的数据数量,_capacity来表示我们能够存放多少数据,也就是容量;
class string
{
pubilc:
:
:
:
pravite:
char* _str; //为了区分成员变量,⼀般习惯上成员变量
size_t _size; // 会加⼀个特殊标识,如_ 或者 m开头
size_t _capacity;
};
string的定义可以有多种,例如
string str1;
string str2("efgh");
string str3(str2);//拷贝构造
所以,我们也得实现其多种构造函数,以及析构函数
class string
{
pubilc:
string()
{
_str=nullptr;
_size=0;
_capacity=0;
}
string(const char* str)
:_size(strlen(str))
{
_capacity = _size;
_str = new char[_size + 1];
strcpy(_str, str);
}
string(const string& str)
{
_str = new char[str._capacity + 1];
_capacity = str._capacity;
_size = str._size;
}
~string()
{
delete []_str;
_size=0;
_capacity=0;
_str=nullptr;
}
pravite:
char* _str;
size_t _size;
size_t _capacity;
};
2.模拟实现string的各种函数
我们已经对string 有了基本的了解,现在我们来实现string的各种函数的模拟实现
1.扩容reserve()
void string::reserve(size_t n) //n 为需扩容到n
{
if (n > _capacity)
{
char* tmp = new char[n+1];
strcpy(tmp, _str);
delete[]_str;
_str = tmp;
_capacity = n;
}
}
2.push_back()尾插
尾插就是在数据最末端,也就是_str[_size]处插入一个字符
void string::push_back(char c)
{
if (_size + 1 > _capacity) //尾插前需检查_capacity是否等于_size
{
reserve(_capacity == 0 ? 4 : _capacity * 2); //也就是是否“满了”
}
_str[_size] = c; //如果满了则扩容,一般为了不浪费空间
_size++;
_str[_size] = '\0'; //选择扩容至原来的2倍
}
3.resize()对元素数量进行修改
当n小于_size时,我们会进行尾删,删到只剩n个元素,并且把元素个数进行修改
当n大于_size且小于_capacity时,我们补全元素到n位,多余的这些元素用c来替代,然后修改_size
当n大于_capacity时,我们先进行扩容,再把元素补全到n位,然后修改_size
void string::resize(size_t n, char c )
{
if (n > _size) //当n大于_size时小于_capacity时,
{
reserve(n); //reserve是不会进行扩容的
for (size_t i = _size; i < n; i++)
{
_str[i] = c;
}
_size = n;
_str[_size] = '\0';
}
else
{
_str[n] = '\0'; //小于_size时,只需直接将n处改为\0
_size = n;
}
}
4.append()在末端插入字符串
void string::append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity) //“满了”则扩容
{
size_t newcapacity = 2 * _capacity;
if (_size + len > 2 * _capacity)
{
newcapacity = _size + len;
}
reserve(newcapacity);
}
strcpy(_str + _size, str);
_size += len;
}
5.find()从pos处开始查找字符,并返回第一次出现的位置
size_t string::find(char c, size_t pos=0) const
{
assert(pos < _size);
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == c)
{
return i;
}
}
return npos; //由于我们需要返回size_t类型的数据,当查找不到时无法返
} //回位置,所以我们需要在成员变量里定义一个npos,并给出
//缺省值-1
6.insert()在pos位置插入一个字符
string::insert(size_t pos, char c)
{
assert(pos <= _size);
if (_size + 1 > _capacity) //“满了”扩容
{
size_t newcapacity = 2 * _capacity;
reserve(newcapacity);
}
size_t end = _size + 1;
while (end > pos - 1)
{
_str[end] = _str[end - 1];
end--;
}
_str[pos - 1] = c;
}
7.erase()删除pos上的元素并返回
string& string::erase(size_t pos, size_t len)
{
if (len >= _size-pos)
{
_str[pos] = '\0';
_size = pos;
}
else
{
size_t end = pos + len;
while (end <= _size)
{
_str[end - len] = _str[end];
end++;
}
_size -= len;
}
return *this;
}
8.str()、size()、capacity()、empty()获取数据存放的位置,数量,容器的容量以及判空
const char* str()const
{
return _str;
}
size_t size()const
{
return _size;
}
size_t capacity()const
{
return _capacity;
}
bool empty()const
{
return _size == 0;
}
3.迭代器
1.begin()
iterator begin()
{
return _str;
}
2.end()
iterator end()
{
return _str + _size;
}
4.各种运算符的重载
1.由于大于小于等判断运算符基本可以复用,这里只写出>=、<=、==
bool string::operator<=(const string& s)
{
return strcmp(_str, s._str) <= 0;
}
bool string::operator>=(const string& s)
{
return strcmp(_str, s._str) >= 0;
}
bool string::operator==(const string& s)
{
return strcmp(_str, s._str) == 0;
}
2.[]运算符
char& string::operator[](size_t index)
{
assert(index < _size);
return _str[index];
}
3.<<,>>流插入流提取运算符
在开始前,由于对于已经有元素的string对象,不能直接流提取,我们可以先调用一个clear函数再进行流提取
并且流插入和流提取函数只能写在类的外面或者使用友元函数写在类的里面,写在类外面的原因是写在类里面this指针会默认抢占第一个参数的位置,而第一个参数是out
clear()
void clear()
{
_str[_size] = '\0';
_size = 0;
}
s不能加上const,因为s需要被读写
ostream& bit::operator<<(ostream& _cout, bit:: string& s)
{
for ( auto a : s )
{
cout << a;
}
return cout;
}
流提取时,为了代码的效率,防止大量扩容,我们需要设置一个buff数组来减少扩容的次数
在一些编译器里,buff数组是确实存在的,可以通过监视窗口看见
istream& bit :: operator>>(istream& _cin, bit::string& s)
{
s.clear();
const int N = 1024;
char buff[N];
int i = 0;
char ch = cin.get();
while (ch != ' ' && ch != '\0')
{
buff[i++] += ch;
if (i == N - 1)
{
buff[i] = '\0';
s += buff;
i = 0;
}
ch = cin.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
return cin;
}