String模拟实现【C++】【STL】
- 构造函数
- 拷贝构造
- 赋值重载
- 析构函数
- <<赋值重载
- 插入函数
- reserve
- append函数
- push_back函数
- earse函数
- 完整代码
- string.h
- string.cpp
STL中有两个属性capacity和size,capacity是真正STL容器的真正内存大小,size是STL容器中数据的大小,实现内存变长这个特性的时候,判断当size大小达到capacity时,进行扩容即可,就像水缸一样,capacity就是水缸能装的最大水容量,size就是水缸中水的多少。capacity很多人会不明白他的必要性,我初学时也不明白,现在想来就是提高了扩容时的一个效率,当有capacity的时候,扩容时还是在之前那块连续的内存上扩容,否则的话,每次扩容可能都要重新找一块足够的内存,然后copy原来数据到新内存空间上。
为了区别STL的string和自己写的string,我们使用命名空间限定一下:
#pragma oncenamespace 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;}
}
新人创作不易,你的点赞和关注都是对我莫大的鼓励,再次感谢您的观看。