C++入门系列:
【C++】从C丝滑入门C++——初始C++
【C++】类和对象(一)
【C++】类和对象(二)
【C++】类和对象(三)
【C++】动态内存管理以及模板
加粗样式>—
感兴趣的同学点击支持一下!
初识STL
我们首先要知道什么是 STL :STL 是C++标准库的重要组成部分,里面包含了我们常用的数据结构和算法的框架。使用 STL 我们可以非常方便的进行数据结构方面的代码编写。
STL 由几大组件构成:
这里的组件笔者会通过学习为大家一一讲解,我们有一个概念即可。
sting类的模拟实现
我们首先学习的结构就是字符类—— string 。它内部包含了字符数组的增删查改、遍历、反转以及交换。这些功能我们都可以使用系列内置函数来进行实现。在本文笔者通过模拟实现一个 string 类的方法来带大家理解并使用 sting 类。
我们先看看其内部的成员函数以及构造函数:
namespace mystd
{
class string
{
public:
string(const char* str = "");
string(const string& s);
string& operator=(const string& s);
~string();
private:
char* _str;
size_t _capacity;
size_t _size;
};
}
其实 string 类就是一个存储字符数组的顺序表,成员变量由指向数组空间的 _str 指针,空间容量 _capacity 以及字符个数 _size 构成。所以其构造函数与顺序表的初始化也类似,大家想了解顺序表的内容可以看笔者数据结构部分的文章——【数据结构】顺序表
namespace mystd
{
string::string(const char* str)
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
string::string(const string& s)
{
_size = s._size;
_capacity = s._capacity;
_str = new char[_capacity + 1];
strcpy(_str, s._str);
}
string& string::operator=(const string& s)
{
if (this != &s)
{
_size = s._size;
_capacity = s._capacity;
delete[] _str;
_str = new char[_capacity + 1];
strcpy(_str, s._str);
}
return *this;
}
string::~string()
{
delete[] _str;
_size = _capacity = 0;
}
}
构造函数非常简单,即开辟动态内存,初始化 _capacity 与 _size 的大小。而这里需要注意的是拷贝构造函数与赋值重载函数的编写——我们需要对 string 类进行深拷贝,而不能简单的浅拷贝,浅拷贝在拷贝后容易造成内存的重复释放!!! 所以我们对深拷贝的内容单独开辟了一处内存空间,然后再进行值拷贝。
这样我们基础的框架以及写完,但是一个数据结构免不了增删查改,所以我们要对代码进行一一补充。首先要对这个对象进行增加字符串,我们免不了对字符数组的扩容问题,所以我们首先编写扩容函数 reserve。
在c语言中我们要对动态内存进行扩容,我们可以使用 realloc 函数。那么在 C++ 中有相应的函数或者关键字吗?遗憾的是没有。那我们用 realloc 进行扩容吗,我们之前提到在 C++ 编写代码时,我们最好使用配套的内存开辟与释放的方法。所以我们仍然使用 new 进行扩容。
我们首先 new 一整块我们需要的空间大小,然后我们可以对之前使用的空间进行释放,再让 _str 指针指向新开辟的内存空间区域。这样我们就完成了对内存的扩容。
namespace mystd
{
if (n > _capacity)
{
_capacity = n;
char* tmp = new char[_capacity + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
}
}
可以对类对象进行扩容后,我们就可以轻松完成 string 类的插入工作:
namespace mystd
{
void string::push_back(char c)
{
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
_capacity = newcapacity;
}
_str[_size++] = c;
_str[_size] = 0;
}
string& string::operator+=(char c)
{
push_back(c);
return *this;
}
void string::append(const char* s)
{
size_t len = strlen(s);
if ((_size + len) > _capacity)
{
_capacity = _size + len;
reserve(_capacity);
}
strcpy(_str + _size, s);
_size += len;
_str[_size] = 0;
}
string& string::operator+=(const char* s)
{
append(s);
return *this;
}
}
插入无非就是首先检查内存空间大小是否够用,若不够用的话我们就进行扩容,再进行插入。本次实现了 += 的操作符重载的方式,也可进行对字符与字符串的扩容。
string str("123");
str += 4; //扩容一个字符
str += "abcd"; //扩容一个字符串
删除也很简单,对一个顺序表我们需要删除只需改变 _size 的值即可。对一个字符类我们还需多实现一步对 _size 位置的数组置为0,这样的话我们打印字符串时到 \0 结尾也能适用此字符类。
namespace mystd
{
string& string::erase(size_t pos, size_t len)
{
assert(pos <= _size);
if (len == npos || pos + len >= _size)
{
_str[pos] = 0;
_size = pos;
}
else
{
size_t end = pos + len;
while (_str[end])
{
_str[end - len] = _str[end];
end++;
}
_size = _size - len;
_str[_size] = 0;
}
return *this;
}
}
那么我们如何改变 string 类的字符内容呢,我们实现了一个[]的操作符重载函数,返回对应的引用内容,这样我们就可以对 string 类的内容查看与更改。
namespace mystd
{
char& string::operator[](size_t index)
{
return *(_str + index);
}
}
使用[]的操作符重载的好处是我们可以想操作数组一样操作这个对象,大大提高了代码的可读性:
string str("123");
str[1] = a; //将2改为a字符
剩下的内容思路与其上类似,大家可以自己将下列代码实现一下:
笔者的实现内容大家可以翻阅笔者的gittee查看:https://gitee.com/zwz04/linux_learning
namespace mystd
{
class string
{
public:
friend std::ostream& operator<<(std::ostream& out, const string& s);
friend std::istream& operator>>(std::istream& in, const string& s);
string(const char* str = "");
string(const string& s);
string& operator=(const string s);
~string();
void push_back(char c);
string& operator+=(char c);
void append(const char* s);
string& operator+=(const char* s);
void swap(string& s);
const char* c_str() const;
size_t size() const;
size_t capacity() const;
bool empty() const;
void resize(size_t n, char c = '\0');
void reserve(size_t n);
char& operator[](size_t index);
const char& operator[](size_t index) const;
size_t find(char c, size_t pos = 0) const;
size_t find(const char* s, size_t pos = 0) const;
string& insert(size_t pos, char c);
string& insert(size_t pos, const char* s);
string& erase(size_t pos, size_t len = npos);
string substr(size_t pos, size_t len = npos) const;
bool operator==(const string& s) const;
bool operator!=(const string& s) const;
bool operator>=(const string& s) const;
bool operator<=(const string& s) const;
bool operator>(const string& s) const;
bool operator<(const string& s) const;
static size_t npos;
private:
char* _str;
size_t _capacity;
size_t _size;
};
void test1();
}