自学内容网 自学内容网

String模拟实现【C++】【STL】


  STL中有两个属性capacity和size,capacity是真正STL容器的真正内存大小,size是STL容器中数据的大小,实现内存变长这个特性的时候,判断当size大小达到capacity时,进行扩容即可,就像水缸一样,capacity就是水缸能装的最大水容量,size就是水缸中水的多少。capacity很多人会不明白他的必要性,我初学时也不明白,现在想来就是提高了扩容时的一个效率,当有capacity的时候,扩容时还是在之前那块连续的内存上扩容,否则的话,每次扩容可能都要重新找一块足够的内存,然后copy原来数据到新内存空间上。

  为了区别STL的string和自己写的string,我们使用命名空间限定一下:

#pragma once

namespace th
{
class string
{
public:
string(const char* str = "");

string(const string&);
string& operator=(const string&);
~string();
private:
size_t _size;
size_t _capacity;
char* _str;
};
}

  写一个类,必要的构造函数,析构函数,拷贝构造函数,赋值重载函数都已经声明完成,必要这个词用的不严谨,对于类中都是内置类型的属性数据,不用定义构造也可以,此时我们的string是变长的,需要随时扩容的,另外一些属性的初始化也是必要的,因此在这里是必要的。

构造函数

th::string::string(const char* str)
: _size(0)
, _capacity(0)
, _str(nullptr)
{
int size = strlen(str);
if (size != 0)
{
_size = size;
_capacity = _size + 1;
_str = new char(_capacity);
strcpy(_str, str);
}
}

  strlen仅仅能得到字符串中字符的个数,但是不包含末尾的 ‘\0’,_size表示数据的数量,因此不必加上这1个,capacity是申请的内存数量,因此加上这一个。

拷贝构造

  拷贝构造就是一个已经存在的string对象拷贝给一个新的string对象,

th::string::string(const string& str)
{
string temp(str._str);

std::swap(_str, temp._str);
std::swap(_size, temp._size);
std::swap(_capacity, temp._capacity);
}

赋值重载

  赋值重载如下,唯一就是这个定义时函数头的编写需要注意:

th::string& th::string::operator=(string& str)
{
_str = str._str;
_size = str._size;;
_capacity = str._capacity;

return *this;
}

析构函数

  注意点就是_str析构了以后记得置为空,不然就成野指针了。

th::string::~string()
{
delete _str;
_str = nullptr;
_size = 0;
_capacity = 0;
}

  该有的都有了,赶紧测试一下吧:

#include <iostream>
#include "string.h"

int main()
{
th::string str("abcdefg");

std::cout << str << std::endl;
return 0;
}

在这里插入图片描述
  这么多报错!!丸辣!!
  报错在 <<,原来是这个运算符也需要重载才可以使用,为什么嘞,因为我们cout的是自定义类型,里面那么多属性,不知道打印哪一个呀,比如我们的string类里面就有_str, _size, _capacity。这么几个属性是无法确定打印那个的,所以需要赋值重载一下。

<<赋值重载

在这里插入图片描述

  又丸辣!怎么声明都不对!!
  哎哎哎,原来是参数的问题, ==>>==是一个二元运算符,需要从>>的左右传入实参进入重载函数中作为形参,但是将重载函数放入类中作为类的一份子,会自动的往函数中添加一个this指针作为参数,所以此时函数拥有3个参数,这对于一个2元运算符来说是不对滴。因此:
在这里插入图片描述
  把它从类里面拿出来。
在这里插入图片描述
  想使用范围for发现需要begin迭代器函数,那么就定义。迭代器的意义是统一了所有容器的访问方式,并不是所有的容器的内存都是连续的,可以顺序访问,可以随机访问,迭代器完善了这一点。

char* th::string::begin()
{
return _str;
}
char* th::string::end()
{
return _str + _size;
}

在这里插入图片描述
  原因是因为我们的重载函数的第二个参数是被const修饰的,不希望修改str,所以需要const修饰的begin函数,如果将const去除,就不会报错了:
在这里插入图片描述
  当然没有必要,我们两个都提供:

char* th::string::begin() const
{
return _str;
}
char* th::string::end() const
{
return _str + _size;
}

  于是,重载函数为:

std::ostream& operator>>(std::ostream& out, const th::string& str)
{
for (auto e : str)
{
out << e;
}

return out;

}

测试一下:
在这里插入图片描述
这跟出现scanf是不安全的一个意思,vs的问题,直接加上宏定义:
在这里插入图片描述
再次运行:
在这里插入图片描述
出现了这样的报错,堆溢出的问题,原因不止,调试一下:
从头开始:
在这里插入图片描述
  发现走到return报的错,return之后需要进行全局对象的析构,main函数内对象的析构,于是将端点打到析构函数中:
在这里插入图片描述
  发现delete的时候报错的。delete和new对应,回看了一眼new,发现一个基础错误:
在这里插入图片描述
  丸辣!!char后面怎么会用(),这是初始化啊,改成[]后:
在这里插入图片描述
析构函数也要改:
在这里插入图片描述
在这里插入图片描述
没毛病,运行成功。

测试拷贝构造函数:
在这里插入图片描述
运行成功。

测试赋值重载:
在这里插入图片描述
运行成功。

char& th::string::operator[](int index)
{
assert(index >= 0 && index < _size);
return *(_str + index);
}

插入函数

void th::string::insert(int index, char ch)
{
assert(index >= 0 && index <= _size);

for (int i = _size; i > index; i--)
{
_str[i] = _str[i - 1];
}

_str[index] = ch;
_size++;
}

在这里插入图片描述
测试发现没问题,再次进行插入发现报错:
在这里插入图片描述
原因是当空间不够的时候,没有自动扩容,为此我们先实现一个扩容函数reserve:

reserve

void th::string::reserve(size_t n)
{
if (n > _capacity)
{
char* newStr = new char[n];
strcpy(newStr, _str);
delete[] _str;
_str = newStr;
_capacity = n;

}
}

当n < capacity 时就是缩容了,意义不大,拒绝实现。
在这里插入图片描述

void th::string::insert(int index, char ch)
{
assert(index >= 0 && index <= _size);
if (_size == _capacity)
{
size_t newCapacity = _capacity == 0 ? INITIAL_CAPACITY : _capacity * EXPANSION_FACTOR;
reserve(newCapacity);
}
for (int i = _size; i > index; i--)
{
_str[i] = _str[i - 1];
}

_str[index] = ch;
_size++;
}

测试一下:
在这里插入图片描述
  这个错误还是new出来的内存使用的时候溢出使用了不属于它的内存空间,在delete的时候就会出现这个错误,在代码中的体现就是,当_size = capacity的时候进行扩容,但是此时字符串末尾的’\0’字符进行溢出分配的内存空间了,因此当我们delete的时候就会报错,我们进行改正:

void th::string::insert(int index, char ch)
{
assert(index >= 0 && index <= _size);
if (_size+1 == _capacity)
{
size_t newCapacity = _capacity == 0 ? INITIAL_CAPACITY : _capacity * EXPANSION_FACTOR;
reserve(newCapacity);
}
for (int i = _size-1; i >= index; i--)
{
_str[i+1] = _str[i];
}

_str[index] = ch;
_size++;
}

void th::string::reserve(size_t n)
{
if (n > _capacity)
{
_capacity = n;
char* tmp = new char[_capacity];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
}
}

在这里插入图片描述
此时1万次插入都不会有问题。

append函数

void th::string::append(const char* str)
{
if (_size + 1 + strlen(str) >= _capacity)
{
size_t newCapacity = (_size + 1 + strlen(str)) * EXPANSION_FACTOR;
reserve(newCapacity);
}
strcpy(_str + _size, str);
_size += strlen(str);
}

测试没问题:
在这里插入图片描述

push_back函数

void th::string::push_back(char ch)
{
if (_size + 2 >= _capacity)
{
size_t newCapacity = (_size + 2) * EXPANSION_FACTOR;
reserve(newCapacity);
}
_str[_size] = ch;
_str[_size + 1] = '\0';
_size++;
}

在这里插入图片描述

void th::string::append(const char ch)
{
push_back(ch);
}
th::string& th::string::operator+=(const char ch)
{
push_back(ch);
return *this;
}

earse函数

void th::string::erase(size_t pos, size_t len)
{
assert(pos < _size && pos >= 0);
assert(len >= 0);
if (pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
}

测试:
在这里插入图片描述
自此大部分的函数接口都已经实现完毕
下面是完整代码:

完整代码

string.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cassert>

namespace th
{
const size_t EXPANSION_FACTOR = 2;
const size_t INITIAL_CAPACITY = 8;
class string
{
public:
string(const char* str = "");

string(const string&);
string& operator=(string&);
char* begin() const;
char* end() const;
char* begin();
char* end();
char& operator[](int index);
~string();

void insert(int index, char ch);
void reserve(size_t n);
void append(const char* str);
void append(const char ch);
string& operator+=(const char* str);
string& operator+=(const char ch);
void push_back(char ch);
void erase(size_t pos, size_t len);
private:
size_t _size;
size_t _capacity;
char* _str;
};


std::ostream& operator<<(std::ostream& out, const th::string& str);
}

string.cpp

#include "string.h"


th::string::string(const char* str)
: _size(0)
, _capacity(0)
, _str(nullptr)
{
_size = strlen(str);
_capacity = _size+1;
_str = new char[_capacity];

strcpy(_str, str);
}

th::string::string(const string& str)
{
string temp(str._str);

std::swap(_str, temp._str);
std::swap(_size, temp._size);
std::swap(_capacity, temp._capacity);
}

th::string& th::string::operator=(string& str)
{
_str = str._str;
_size = str._size;;
_capacity = str._capacity;

return *this;
}

th::string::~string()
{
delete[] _str;
_str = nullptr;
_size = 0;
_capacity = 0;
}

char* th::string::begin() const
{
return _str;
}
char* th::string::end() const
{
return _str + _size;
}

char* th::string::begin() 
{
return _str;
}
char* th::string::end()
{
return _str + _size;
}


std::ostream& th::operator<<(std::ostream& out, const th::string& str)
{
for (auto e : str)
{
out << e;
}

return out;

}

char& th::string::operator[](int index)
{
assert(index >= 0 && index <= _size);
return *(_str + index);
}

void th::string::insert(int index, char ch)
{
assert(index >= 0 && index <= _size);
if (_size+1 == _capacity)
{
size_t newCapacity = _capacity == 0 ? INITIAL_CAPACITY : _capacity * EXPANSION_FACTOR;
reserve(newCapacity);
}
for (int i = _size-1; i >= index; i--)
{
_str[i+1] = _str[i];
}

_str[index] = ch;
_size++;
}

void th::string::reserve(size_t n)
{
if (n > _capacity)
{
_capacity = n;
char* tmp = new char[_capacity];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
}
}

void th::string::append(const char* str)
{
if (_size + 1 + strlen(str) >= _capacity)
{
size_t newCapacity = (_size + 1 + strlen(str)) * EXPANSION_FACTOR;
reserve(newCapacity);
}
strcpy(_str + _size, str);
_size += strlen(str);
}

th::string& th::string::operator+=(const char* str)
{
append(str);
return *this;
}

void th::string::push_back(char ch)
{
if (_size + 2 >= _capacity)
{
size_t newCapacity = (_size + 2) * EXPANSION_FACTOR;
reserve(newCapacity);
}
_str[_size] = ch;
_str[_size + 1] = '\0';
_size++;
}

void th::string::append(const char ch)
{
push_back(ch);
}

th::string& th::string::operator+=(const char ch)
{
push_back(ch);
return *this;
}

void th::string::erase(size_t pos, size_t len)
{
assert(pos < _size && pos >= 0);
assert(len >= 0);
if (pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
}

         新人创作不易,你的点赞和关注都是对我莫大的鼓励,再次感谢您的观看。


原文地址:https://blog.csdn.net/tang20030306/article/details/143566481

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!