目录
本片文章还是主要讲解 string 类中剩余几个函数的模拟实现:
1. swap() 函数的模拟实现
在C++库中就有swap()函数的实现,但是为什么要在string类中也要实现一个swap()函数呢?
我们可以先看一下库中的实现逻辑:
template <class T> void swap ( T& a, T& b )
{
T c(a); a=b; b=c;
}
这里可以看到如果对内置类型,它会调用一次 拷贝构造、两次赋值重载、一次析构,而且都是深拷贝,这样的效率就会大大增加,虽然可以实现swap()的功能,但是一般不推荐使用,所以会在库中自己实现。
因此我们可以自己实现一个 swap() 函数,只要对内置类型进行交换即可:
void swap(string& str)
{
std::swap(_str, str._str);
std::swap(_size, str._size);
std::swap(_capacity, str._capacity);
}
除此之外,为了防止调用算法库的swap(),我们可以写一个全局的swap():
void swap(string& x, string& y)
{
x.swap(y);
}
2. find() 函数的模拟实现
查找字符的模拟实现:
size_t find(char ch, size_t pos = 0) const
{
for (size_t i = pos; i < _size; ++i)
{
if (_str[i] == ch)
{
return i;
}
else
{
return npos;
}
}
}
查找字符串的模拟实现,可以用strstr()函数来完成,查找成功strstr会返回查询到的位置的指针, 查找失败则会返回空,因此要两个指针相减得到下标:
size_t find(const char* ch, size_t pos = 0) const
{
// 字符串匹配
const char* p = strstr(_str, ch);
if (p)
{
return p - _str;
}
else
{
return npos;
}
}
3. substr() 函数的模拟实现
取子串的操作从pos位置开始,取len个,代码如下:
string substr(size_t pos = 0, size_t len = npos)
{
string sub;
if (len >= _size - pos)
{
for (size_t i = pos; i < _size; ++i)
{
sub += _str[i];
}
}
else
{
for (size_t i = pos; i < pos + len; ++i)
{
sub += _str[i];
}
}
return sub;
}
4. operator==()的重载模拟实现
这里就只实现==的重载,其他的重载只需要复用即可。
在库中重载是在全局中实现的,主要是为了实现 const char* 和 string 类型的比较,这里模拟实现就不在全局中实现了:
bool operator==(const string& s)
{
int ret = strcmp(_str, s._str);
return ret == 0;
}
5. << 和 >> 重载的模拟实现
<< 的重载比较简单,注意重载成全局的就可以了:
ostream& operator<<(ostream& _out, const string& str)
{
for (size_t i = 0; i < str.size(); ++i)
{
_out << str[i] << " ";
}
_out << endl;
return _out;
}
cin 读不到' '或者'\n',要用get()获取
istream& operator>>(istream& in, string& s)
{
s.clear();
char ch;
//in >> ch;
ch = in.get();
char buff[128];
size_t i = 0;
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
// [0,126]
if (i == 127)
{
buff[127] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
6. getline() 的重载
和>>类似:
istream& getline(istream& in, string& s)
{
s.clear();
char ch;
//in >> ch;
ch = in.get();
char buff[128];
size_t i = 0;
while (ch != '\n')
{
buff[i++] = ch;
// [0,126]
if (i == 127)
{
buff[127] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
7. 拷贝构造的现代写法
这个是拷贝构造的正常的写法:
- 先动态开辟一块内存,并把str._str中的数据拷贝到其中;
- 让_str指向ch指向的空间
- _size 和 _capacity 分别赋值
string(const string& str)
{
char* ch = new char[str._capacity + 1];
strcpy(ch, str._str);
_str = ch;
_size = str._size;
_capacity = str._capacity;
}
现代写法:
- 新建一个tmp对象用str._str初始化;
- 然后再和this进行交换就可以了。
string(const string& str)
{
string tmp(str._str);
swap(tmp);
}
8. 赋值重载的现代写法
这个是赋值重载的正常的写法:
- 新构建一块空间并把str中的值拷贝到其中;
- 释放掉this->_str指向的空间;
- 让this->str 指向这块空间;
- 给this->_size 和 this->_capacity 赋值
string& operator=(const string& str)
{
if (this != &str)
{
_size = str._size;
_capacity = str._capacity;
char* tmp = new char[_capacity + 1];
delete[] _str;
strcpy(tmp, str._str);
_str = tmp;
}
return *this;
}
现代写法:
- 新建一个tmp对象用str拷贝构造;
- this和tmp进行交换;
- 因为局部变量出作用域会自动调用析构函数,所以这里不需要自己重新写一份delete[] tmp._str 释放掉this原来指向的空间,如果写了delete,记得把tmp._str 制空;
string& operator=(const string& str)
{
string tmp(str);
swap(tmp);
return *this;
}
还有更简单的写法:
因为这里是传值传参,会自动调用拷贝构造,就不需要我们自己手动写一个tmp的拷贝构造了。
string& operator=(string str)
{
swap(str);
return *this;
}