STL之string类的模拟实现
一、四个默认成员函数
构造函数和析构函数
class string
{
public:
string(const char* str = "")
:_str(new char[strlen(str)+1])
{
strcpy(_str, str);
}
~string()
{
delete[] _str;
_str = nullptr;
}
拷贝构造和运算符重载
传统写法:
string(const string& s)
:_str(new char[strlen(s._str)+1])
{
strcpy(_str, s._str);
}
const char* c_str()
{
return _str;
}
string& operator=(const string& s)
{
if (this != &s)
{
delete[] _str;
_str = new char[strlen(s._str) + 1];
strcpy(_str, s._str);
}
return *this;
现代写法:
string(const string& s)
:_str(nullptr)
{
string tmp(s._str);
swap(_str, s._str);
}
string& operator=(string s)
{
swap(_str, s._str);
return *this;
}
二、string类的增删查改
在之前的代码下要添加内存和容量
class string
{
public:
string(const char* str = "")
{
_str = (new char[strlen(str) + 1]);
_size = strlen(str);
_capacity = _size;
strcpy(_str, str);
}
~string()
{
delete[] _str;
_str = nullptr;
_size = 0;
_capacity = 0;
}
string(const string& s)
:_str(nullptr)
, _size(0)
, _capacity(0)
{
string tmp(s._str);
swap(_str, tmp._str);
swap(_size, tmp._size);
swap(_capacity, tmp._capacity);
}
string& operator=(string s)
{
swap(_str, s._str);
swap(_size, s._size);
swap(_capacity, s._size);
return *this;
}
private:
char* _str;
size_t _size;
size_t _capacity;
};
关于const的描述
我们定义的类的成员函数中,常常有一些成员函数不改变类的数据成员,也就是说,这些函数是"只读"函数,而有一些函数要修改类数据成员的值。如果把不改变数据成员的函数都加上const关键字进行标识,显然,可提高程序的可读性。其实,它还能提高程序的可靠性,已定义成const的成员函数,一旦企图修改数据成员的值,则编译器按错误处理, const成员函数和const对象 实际上,const成员函数还有另外一项作用,即常量对象相关。对于内置的数据类型,我们可以定义它们的常量,用户自定义的类也一样,可以定义它们的常量对象。
1、非静态成员函数后面加const(加到非成员函数或静态成员后面会产生编译错误)
2、表示成员函数隐含传入的this指针为const指针,决定了在该成员函数中, 任意修改它所在的类的成员的操作都是不允许的(因为隐含了对this指针的const引用)
3、唯一的例外是对于mutable修饰的成员。加了const的成员函数可以被非const对象和const对象调用,但不加const的成员函数只能被非const对象调用。
前面使用const 表示返回值为const
后面加 const表示函数不可以修改class的成员
遍历
我们这里的第一种遍历需要const的帮助
[]+for
//读
const char& operator[](size_t i) const
{
return _str[i];
}
//可读可写
char& operator[](size_t i)
{
return _str[i];
}
size_t size() const
{
return _size;
}
void f(const string& s)
{
for (size_t i = 0; i < s.size(); ++i)
{
cout << s[i] << endl;
}
}
const对象不能调用非const的成员函数,所以size要加const。
如果是返回引用,成员函数就要提供两个,一个const一个是非const对象,保证可读可写。
迭代器
string的迭代器原理就是指针
typedef char* iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
void test_string3()
{
string s1("hello world");
string::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
++it;
}
return 0;
}
三、增删查改
增:
string& insert(size_t pos, char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
char* end = _str + _size;
while (end >= _str + pos)
{
*(end + len) = *end;
--end;
}
strncpy(_str + pos, str, len);
_size += len;
return *this;
}
void push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity * 2);
}
_str[_size] = ch;
_str[_size + 1] = '\0';
++_size;
}
void append(const char* str)
{
size_t len = _size + strlen(str);
if (len > _capacity)
{
reserve(len);
}
strcpy(_str + _size, str);
_size = len;
}
删:
string& erase(size_t pos, size_t end)
{
size_t leftLen = _size - pos;
//1、剩余字符长度小于要删的长度
//2、剩余字符长度大于要删的长度
if (len >= leftLen)
{
_str[pos] = '\0';
_size = pos;
}
else
{
const char* start = _str + pos+len;
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
}
查:
查找字符:
size_t find(char ch, size_t pos = 0)
{
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)
{
const char* ret = strstr(_str, str);
if (ret)
{
return ret - _str;
}
else
{
return npos;
}
}
总结
string类是一个管理字符数组的类,要求这个字符数组结尾用’\0’
1.拷贝构造和赋值重载实现深拷贝
2.增删查改的相关接口(跟顺序表类似)
3.重载了一些常见运算符