String
String是STl中的六大组件之一,里面可以存放字符串,在c语言中同样可以用数组来存放字符串,但是c语言中无法直接使用函数对字符串进行操作,所以使用String类可以避免这些,而这里也符合了c++封装的思想。 在String类中增加了许多接口,专门对字符串进行操作
类成员
String类主要的成员有三个,一个指针,一个有效字符长度,一个容量大小 指针指向的空间用来存放字符串
private:
char* _str;
size_t _size;
size_t _capacity;
构造函数和拷贝构造函数
string(char* str = "")
:_size(strlen(str))
, _capacity(_size)
{
_str = new char[_capacity + 1];
strcpy(_str, str);
}
~string() {
delete[] _str;
_capacity = 0;
_str = nullptr;
}
void swap(string& s) {
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
string(const string& s)
:_str(nullptr)
{
string tmp(s._str);
swap(tmp);
}
string& operator = (string str) {
swap(str);
return *this;
}
我们需要注意的是,在string类中,在创建,拷贝,传返回值等都是会深拷贝,所以这时候引用(&)就显得尤为重要,它可以避免我们进行深拷贝,去创建空间,而是直接使用
迭代器
typedef char* iterator;
typedef const char* const_iterator;
iterator begin() {
return _str;
}
iterator end() {
return _str + _size;
}
const_iterator begin() const{
return _str;
}
const_iterator end() const{
return _str + _size;
}
在string类中,迭代器本身其实是指针,但是,在别的容器里迭代器本不是指针,至于是什么,在list容器中我会进行解释,迭代器其实还有很多操作,比如+,+=,>>,<<等等,他将我们大多的运算符全都重载一遍,使我们用起来和数组类似,但在这里我没有展示
函数功能
基本功能有尾插,扩容,插入字符串,插入字符,删除字符,开空间
void reserve(size_t n) {
if (n > _capacity) {
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
};
void push_back(char c) {
if (_size >= _capacity) {
reserve(_capacity * 2);
}
_str[_size] = c;
_size++;
_str[_size] = '\0';
}
void append(const char* c) {
size_t len = strlen(c);
if (_size + len >= _capacity) {
reserve(_size + len);
}
strcpy(_str + _size, c);
_size += len;
}
char& operator[] (size_t num) {
return *(_str + num);
}
void resize(size_t n, char ch = '\0'){
if (n <= _size) {
for (size_t i = 0; i < n; i++)
*(_str + i) = ch;
_size = n;
_str[_size] = '\0';
}
else {
if (n > _capacity) {
reserve(n);
}
for (size_t i = _size; i < n; i++)
*(_str + i) = ch;
_size = n;
_str[_size] = '\0';
}
}
size_t size() const{
return _size;
}
size_t capacity() const {
return _capacity;
}
string& insert(size_t pos, char ch) {
assert(pos <= _size);
if (_size >= _capacity) {
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(_size);
}
size_t newnode = _size + 1;
while (newnode > pos) {
_str[newnode] = _str[newnode - 1];
--newnode;
}
_str[pos] = ch;
_size++;
return *this;
}
string& insert(size_t pos, const char* str) {
assert(pos <= _size);
size_t len = strlen(str);
if (_size+len >= _capacity) {
reserve(_size+len);
}
size_t newnode = _size + len;
while (newnode >= pos+len) {
_str[newnode] = _str[newnode-len];
newnode--;
}
for (size_t i = 0; i < len; ++i)
{
_str[pos + i] = str[i];
}
_size += len;
return *this;
}
string& erase(size_t pos, size_t len = npos) {
assert(pos < _size);
if (pos + len >= _size || len == pos) {
_str[0] = '\n';
_size = npos;
}
else {
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}
在VS19里面,空间的扩容基本上是以1.5倍的方式进行的,但在别的环境中又不一样,比如在linux下是以2倍的方式进行扩容
完整代码
class string {
public:
typedef char* iterator;
typedef const char* const_iterator;
iterator begin() {
return _str;
}
iterator end() {
return _str + _size;
}
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
string(const char* str = "")
:_size(strlen(str))
, _capacity(_size)
{
_str = new char[_capacity + 1];
strcpy(_str, str);
}
~string() {
delete[] _str;
_capacity = 0;
_str = nullptr;
}
void swap(string& s) {
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
string(const string& s)
:_str(nullptr)
{
string tmp(s._str);
swap(tmp);
}
string& operator = (string str) {
swap(str);
return *this;
}
void reserve(size_t n) {
if (n > _capacity) {
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
};
void push_back(char c) {
if (_size >= _capacity) {
reserve(_capacity * 2);
}
_str[_size] = c;
_size++;
_str[_size] = '\0';
}
void append(const char* c) {
size_t len = strlen(c);
if (_size + len >= _capacity) {
reserve(_size + len);
}
strcpy(_str + _size, c);
_size += len;
}
char& operator[] (size_t num) {
return *(_str + num);
}
void resize(size_t n, char ch = '\0')
{
if (n <= _size) {
for (size_t i = 0; i < n; i++)
*(_str + i) = ch;
_size = n;
_str[_size] = '\0';
}
else {
if (n > _capacity) {
reserve(n);
}
for (size_t i = _size; i < n; i++)
*(_str + i) = ch;
_size = n;
_str[_size] = '\0';
}
}
size_t size() const{
return _size;
}
size_t capacity() const {
return _capacity;
}
string& insert(size_t pos, char ch) {
assert(pos <= _size);
if (_size >= _capacity) {
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(_size);
}
size_t newnode = _size + 1;
while (newnode > pos) {
_str[newnode] = _str[newnode - 1];
--newnode;
}
_str[pos] = ch;
_size++;
return *this;
}
string& insert(size_t pos, const char* str) {
assert(pos <= _size);
size_t len = strlen(str);
if (_size+len >= _capacity) {
reserve(_size+len);
}
size_t newnode = _size + len;
while (newnode >= pos+len) {
_str[newnode] = _str[newnode-len];
newnode--;
}
for (size_t i = 0; i < len; ++i)
{
_str[pos + i] = str[i];
}
_size += len;
return *this;
}
string& erase(size_t pos, size_t len = npos) {
assert(pos < _size);
if (pos + len >= _size || len == pos) {
_str[0] = '\n';
_size = npos;
}
else {
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}
void clear() {
_str[0] = '\0';
_size = 0;
}
private:
char* _str;
size_t _size;
size_t _capacity;
static const size_t npos;
};
const size_t string::npos = -1;
总结
string容器的底层逻辑其实非常简单,他利用数组存储字符串,但外加各种接口来方便使用,并且重载运算符,将重载后的运算符功能和本身功能一样,由于string对象都是进行深拷贝,所以我们有时候可以利用引用,来减少开空间的负担
|