【c++】string的底层实现
交换函数就近原则,会先找局部的,所以使用类作用域操作符(::);为什么要把_str定义为nullptr,如果不定义那么_str内是随机值,和tmp交换后,出函数tmp生命周期结束,调析构函数清理一块没有权限的空间,不可以;
·
目录
1.类的定义和4个默认成员函数
类的成员变量就写一次,不是一直写冗杂
#pragma once
#include <iostream>
#include <assert.h>
using namespace std;
namespace lj
{
class string
{
public:
private:
size_t _size;
size_t _capacity;
char* _str;
static const size_t npos;
};
- 构造函数
1.我的_capacity是只包括有效字符,不包括'\0';所以和_size是一样的
string(const char* str = "")//构造函数
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];//capacity为容量不包括‘\0’
strcpy(_str, str);
}
- 析构函数
~string()//析构函数
{
delete[] _str;
_str = nullptr;
_capacity = _size = 0;
}
- 拷贝构造函数
- 交换函数就近原则,会先找局部的,所以使用类作用域操作符(::);
- 为什么要把_str定义为nullptr,如果不定义那么_str内是随机值,和tmp交换后,出函数tmp生命周期结束,调析构函数清理一块没有权限的空间,不可以;
//现代写法
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_capacity, s._capacity);
std::swap(_size, s._size);
}
string(const string& s)//拷贝构造函数
:_str(nullptr)//交换后,如果一开始不初始化this,tmp指向随机空间,不属于它不能析构
, _size(0)
, _capacity(0)
{
string tmp(s._str);
swap(tmp);
}
- 赋值运算符重载
- 交换函数同上;
- 看参数是传值传参,那么会调用我们上面所写的拷贝构造深拷贝,然后交换即可,赋值运算符重载是两个被定义了的对象,所以不会出现上面析构函数清理没有权限空间的问题;
void swap(string& s)
{
::swap(_str, s._str);
::swap(_capacity, s._capacity);
::swap(_size, s._size);
}
string& operator=(string s)//赋值运算法重载,传值传参,拷贝构造的深拷贝
{
swap(s);
return *this;
}
2.reserve和resize
- reserve
- 因为我们capacity是包括有效字符,不包括'\0',所以需要多开一个空间
- strncpy也需要拷贝\0\;
void reserve(size_t n)//reserve
{
if (n > _capacity)
{
char* tmp = new char[n + 1];//+1'\0'需要
strncpy(tmp, _str, _size + 1);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
- resize
- val的缺省值是\0
- resize改变容量的同是size也随之改变,所以扩容使用reserve
void resize(size_t n, char val = '\0')//resize
{
if (n < _size)
{
_size = n;
_str[_size] = '\0';
}
else
{
if (n > _capacity)
{
reserve(n);
}
for (size_t i = _size; i < n; ++i)
{
_str[i] = val;
}
_str[n] = '\0';
_size = n;
}
}
3.增:尾插(+=)和任意位置插入(insert)
- 尾插
- push_back(插字符)和append(插字符串)
- capacity可能为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 = _size + strlen(str);
if (len > _capacity)
{
reserve(len);
}
strcpy(_str, str);
}
复用实现:operator+=
string& operator+=(char ch)//+=字符
{
push_back(ch);
return *this;
}
// s1 += "xxxxx"
string& operator+=(const char* str)//+=字符串
{
append(str);
- insert(任意位置插入)
- capacity可能为0,使用三目操作符最佳
- 不要忘了断言和size++;
- insert挪动数组,时间复杂度:O(N),少用
string& insert(size_t pos, char ch)//插入字符
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
char* end = _str+_size;
while (end >= _str + pos)
{
*(end + 1) = *end;
--end;
}
_str[pos] = ch;
_size++;
return *this;
}
string& insert(size_t pos, const char* str)//插入字符串
{
assert(pos <= _size);
size_t len = _size + strlen(str);
if (len > _capacity)
{
reserve(len);
}
char* end = _str + _size;
while (end >= _str + pos)
{
*(end + strlen(str)) = *(end);
end--;
}
strncpy(_str + pos, str, strlen(str));
_size += strlen(str);
return *this;
}
4.删:erase
string& erase(size_t pos, size_t len = npos)
{
assert(pos < _size);
size_t leftLen = _size - pos;
if (len >= leftLen)
{
_str[pos] = '\0';
_size = pos;
}
else
{
char* start = _str + pos + len;
strcpy(_str + pos, start);
_size -= len;
}
return *this;
}
5.查:find,改:直接用下标访问
size_t find(char ch, size_t pos=0)//查字符
{
assert(pos < _size);
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
size_t find(const char* str, size_t pos = 0)//查字符串
{
assert(_size+pos < _size);
char* ret=strstr(_str + pos, str);
if (ret)
{
return ret-_str;
}
else
{
return npos;
}
}
6.运算符重载:比较大小
- 实现一个<或者>,复用就可以了
- 内联函数效率更高
inline bool operator<(const string& s1, const string& s2)
{
return strcmp(s1.c_str(), s2.c_str()) < 0;
}
inline bool operator==(const string& s1, const string& s2)
{
return strcmp(s1.c_str(), s2.c_str()) == 0;
}
inline bool operator<=(const string& s1, const string& s2)
{
return s1 < s2 || s1 == s2;
}
inline bool operator>(const string& s1, const string& s2)
{
return !(s1 <= s2);
}
inline bool operator>=(const string& s1, const string& s2)
{
return !(s1 < s2);
}
inline bool operator!=(const string& s1, const string& s2)
{
return !(s1 == s2);
}
7.重载cout和cin和getline
- 需要写在类外,this指针会强第一个位置,写在外面我们就可以控制
- cin碰到' '和' \t '就会认为一个数据输入完毕,getline则是喷到' \t '才会输入完毕;
- 输入数据需要先清空;
ostream& operator<<(ostream& out, const string& s)
{
for (auto ch : s)
{
out << ch;
}
return out;
}
istream& operator>>(istream& in, string& s)
{
s.clear();
char ch;
ch = in.get();
while (ch != ' ' && ch != '\n')
{
s += ch;
ch = in.get();
}
return in;
}
istream& getlline(istream& in, string& s)
{
s.clear();
char ch;
ch = in.get();
while ( ch != '\n')
{
s += ch;
ch = in.get();
}
return in;
}
点击阅读全文
更多推荐
所有评论(0)