【C++】—— string模拟实现
努力学习的小廉 2024-10-01 10:05:09 阅读 56
前言:
学习了string的使用,总感觉了解不是很深厚;自己模拟实现string类来帮助自己理解。
这里只是实现了一部分内容(并没有实现完整的string类)。
先来实现string类里面的成员变量:
namespace HL
{
class string
{
public:
private:
char* _str;
size_t _size;
size_t _capacity;
const static size_t npos;
};
const size_t HL::string::npos = -1;
}
一、string默认成员函数(构造、析构、赋值运算符重载)
1.1、构造函数
1> 默认构造
默认构造函数就是不需要传参的构造函数;这里实现就开辟一个字符的空间存放 '\0'即可(_capacity不包括 '\0' )。
string()
{
_str = new char[1];
_str[0] = '\0';
_size = 0;
_capacity = 0;
}
2> 拷贝构造
拷贝构造,在实现时需要注意:是深拷贝,而不是浅拷贝(值拷贝)。
深拷贝(深拷贝简单来说就是,要开辟一块新的空间,把原空间里的值拷贝到新的空间里)。
string(const string& str)
{
_str = new char[str._capacity + 1];
memcpy(_str, str._str, str._size + 1);
_size = str._size;
_capacity = str._capacity;
}
3> 其他构造
其他构造函数就有很多了,这里就实现以下这几个:
string (const char* s);
<code>string(const char* s)
{
size_t len = strlen(s);
_str = new char[len + 1];
memcpy(_str, s, len + 1);
_size = len;
_capacity = len;
}
string (const char* s, size_t n);
string(const char* s, size_t n)
{
size_t len = strlen(s);
if (n > len)
{
n = len;
}
_str = new char[n + 1];
memcpy(_str, s, n);
_str[n] = '\0';
_size = n;
_capacity = n;
}
string (size_t n, char c);
string(size_t n, char c)
{
_str = new char[n + 1];
for (size_t i = 0; i < n; i++)
{
_str[i] = c;
}
_str[n] = '\0';
_size = n;
_capacity = n;
}
1.2、析构函数
析构函数比较简单,释放开辟的资源即可;
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
1.3、赋值运算符重载
赋值运算符有3个重载,这里就一一实现:
string& operator= (const string& str );
实现这个有很多种方法,
可以释放原空间,再开辟新的空间,将数据拷贝到新的空间中去
<code>string& operator=(const string& str)
{
delete[] _str;
_str = new char[str._capacity];
memcpy(_str, str._str, str._size + 1);
_size = str._size;
_capacity = str._capacity;
return *this;
}
可以调用拷贝构造,构造一个tmp、再将tmp与*this 中的值进行交换(要实现交换函数)
template <typename T>
void Swap(T& x, T& y)
{
T tmp = x;
x = y;
y = tmp;
}
string& operator= (const string& str)
{
string tmp(str);
Swap(_str, tmp._str);
Swap(_size, tmp._size);
Swap(_capacity, tmp._capacity);
return *this;
}
这里如果已经实现string类swap成员函数,就可以直接调用。
string& operator= (const char* s );
/*string& operator= (const char* s)
{
size_t len = strlen(s);
delete[] _str;
_str = new char[len + 1];
memcpy(_str, s, len + 1);
_size = _capacity = len;
return *this;
}*/
string& operator= (const char* s)
{
string tmp(s);
Swap(_str, tmp._str);
Swap(_size, tmp._size);
Swap(_capacity, tmp._capacity);
return *this;
}
string& operator= (char c );
string& operator= (char c)
{
delete[] _str;
_str = new char[2];
_str[0] = c;
_str[1] = '\0';
_size = _capacity = 1;
return *this;
}
二、元素访问与迭代器
2.1、迭代器
迭代器,虽然在string类中使用的不是很多,但在后面的容器中有大用处。
(在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);
}
实现了迭代器之后,范围for这个语法糖就可以使用了(底层就是迭代器)。
2.2、下标访问元素
实现下标访问,就是 [ ]运算符重载。
<code>//下标访问 [ ]
char& operator[] (size_t pos)
{
assert(pos >= _size);
return *(_str + pos);
}
const char& operator[] (size_t pos) const
{
assert(pos >= _size);
return *(_str + _size);
}
at函数和 [ ] 运算符重载原理一样,这里就不重复写了。
三、增删查改
在实现增之前,要先实现一个函数,就是调整空间大小的(扩容来用)。
//扩容
void reserve(size_t n)
{
if (n > _capacity)
{
char* s = new char[n + 1];
memcpy(s, _str, _size);
delete[] _str;
_str = s;
_capacity = n;
}
}
增删这里就实现这些成员函数。
1、push_back 、append 、operator+=
append重载比较多,这里就实现其中的几个。
<code>//扩容
void reserve(size_t n)
{
if (n > _capacity)
{
char* s = new char[n + 1];
memcpy(s, _str, _size);
delete[] _str;
_str = s;
_capacity = n;
}
}
//增
void push_back(char c)
{
if (_size >= _capacity)
{
reserve((_capacity == 0) ? 4 : 2 * _capacity);
}
_str[_size] = c;
_size++;
_str[_size] = '\0';
}
void append(const string& str)
{
size_t n = _size + str._size;
if (n > _capacity)
{
reserve(n);
}
for (int i = 0; i < str._size; i++)
{
_str[_size + i] = str._str[i];
}
_size += str._size;
_str[_size] = '\0';
}
void append(const char* s)
{
size_t len = strlen(s);
int n = _size + len;
if (n > _capacity)
{
reserve(n);
}
for (int i = 0; i < len; i++)
{
_str[_size + i] = s[i];
}
_size = n;
_str[_size] = '\0';
}
void append(size_t n, char c)
{
if (_size + n > _capacity)
{
reserve(_size + n);
}
for (int i = 0; i < n; i++)
{
_str[_size + i] = c;
}
_size += n;
_str[_size] = '\0';
}
string& operator+=(const string& str)
{
this->append(str);
return *this;
}
string& operator+=(const char* s)
{
this->append(s);
return *this;
}
string& operator+=(char c)
{
this->push_back(c);
return *this;
}
2、insert、erase
insert重载也比较多,比较冗余;这里也只实现其中的一部分;
erase这里只实现一个
<code>//insert 、erase
void insert(size_t pos, const string& str)
{
assert(pos >= 0 && pos < _size);
size_t len = str._size;
size_t n = _size + len;
if (n > _capacity)
{
reserve(n);
}
//挪动数据
for (size_t i = n; i >= pos + len; i--)
{
_str[i] = _str[i - len];
}
memcpy(_str + pos, str._str, str._size);
_size += str._size;
_str[_size] = '\0';
}
void insert(size_t pos, const char* s)
{
assert(pos >= 0 && pos < _size);
size_t len = strlen(s);
size_t n = _size + len;
if (n > _capacity)
{
reserve(n);
}
//挪动数据
for (size_t i = n; i >= pos + len; i--)
{
_str[i] = _str[i - len];
}
memcpy(_str + pos, s, len);
_size += len;
_str[_size] = '\0';
}
void insert(size_t pos, size_t n, char c)
{
assert(pos >= 0 && pos < _size);
if (_size + n > _capacity)
{
reserve(_size + n);
}
//挪动数据
for (size_t i = _size+n; i >= pos + n; i--)
{
_str[i] = _str[i - n];
}
for (size_t i = 0; i < n; i++)
{
_str[pos + i] = c;
}
_size += n;
_str[_size] = '\0';
}
void erase(size_t pos, size_t len = npos)
{
assert(pos >= 0 && pos < _size);
if (len == npos)
{
_str[0] = '\0';
_size = 0;
return;
}
for (size_t i = pos; (len + i) < _size; i++)
{
_str[i] = _str[i + len];
}
_size -= len;
_str[_size] = '\0';
}
3、find
//find
size_t find(const string& str, size_t pos = 0)
{
assert(pos >= 0 && pos < _size);
char* tmp = strstr(_str + pos, str._str);
if (tmp == nullptr)
{
return -1;
}
return tmp - _str;
}
size_t find(const char* s, size_t pos = 0)
{
assert(pos >= 0 && pos < _size);
char* tmp = strstr(_str + pos, s);
if (tmp == nullptr)
{
return -1;
}
return tmp - _str;
}
size_t find(char c, size_t pos = 0)
{
assert(pos >= 0 && pos < _size);
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == c)
{
return i;
}
}
return -1;
}
4、swap
swap作为string的成员函数,交换两个string类类型的对象。
//swap
void swap(string& str)
{
Swap(_str, str._str);
Swap(_size, str._size);
Swap(_capacity, str._capacity);
}
这里swap函数内部也可以调用库里面的swap模版(这里我自己写了一个模版Swap)
有了swap函数,上面赋值运算符重载中就可以这样写了:
string& operator= (const string& str)
{
string tmp(str);
swap(tmp);
return *this;
}
四、字符串操作函数
1、c_str
c_str()函数返回string类对象中的字符串;
const char* c_str() const
{
return _str;
}
char* c_str()
{
return _str;
}
2、substr
获得,sting对象中字符串的子串。
<code>//substr
string substr(size_t pos = 0, size_t len = npos) const
{
assert(pos >= 0 && pos < _size);
size_t n = 0;
if (len == npos || pos + len > _size)
{
n = _size - pos;
}
else
{
n = len;
}
string ret;
for (size_t i = 0; i < n; i++)
{
ret += _str[pos + i];
}
return ret;
}
五、其他成员函数
这里max_size是返回容器可以容纳的最大元素的数量,这里就不进行实现了。
reserve在增加元素前已经实现了。(扩容)
<code>//其他成员函数
size_t size() const
{
return _size;
}
size_t length()const
{
return _size;
}
size_t capacity()const
{
return _capacity;
}
void clear()
{
_str[0] = '\0';
_size = 0;
}
bool empty()const
{
return _size == 0;
}
void resize(size_t n)
{
if (n > _capacity)
{
reserve(n);
}
_size = n;
_str[_size] = '\0';
}
void resize(size_t n, char c)
{
if (n > _capacity)
{
reserve(n);
}
for (size_t i = _size; i < n; i++)
{
_str[i] = c;
}
_size = n;
_str[_size] = '\0';
}
六、流插入、流提取
因为成员函数有一个隐藏的this指针,会和istream 和istream 类对象抢占第一个参数的位置,所以我们不能将流插入和流提取写成string类的成员函数。
string中实现了访问元素方成员函数,就可以不将流插入、流提取写成string类的友元函数。
1、流插入运算符重载
std::ostream& operator<<(std::ostream& out, const HL::string& str)
{
//for (int i = 0; i < str.size(); i++)
//{
//out << str[i];
//}
//return out;
for (auto ch : str)
{
out << ch;
}
return out;
}
2、流提取运算符重载
1、 s.clear()清理缓冲区(上次cin流提取的剩余);
2、 创建一个数组,防止多次去开空间(输入到128或者输入结束(‘ ’或者‘\n’)才添加到str中)。
3、下面的代码处理缓冲区的空格。
char ch;
ch = in.get();
while (ch == ' ' || ch == '\n')
{
ch = in.get();
}
4、最后循环里if是遇到空格或者换行结束,将s中输入添加到s中,末尾添加'\0'。
std::istream& operator>>(std::istream& in, HL::string& str)
{
char s[128] = { 0 };
char ch;
ch = in.get();
while (ch == ' ' || ch == '\n')
{
ch = in.get();
}
str.clear();
int i = 0;
while (ch != '\n')
{
s[i] = ch;
i++;
if (i == 127)
{
s[i] = '\0';
str += s;
i = 0;
}
ch = in.get();
}
if (i)
{
str += s;
}
return in;
}
HL :: string 源码
#pragma once
#include<iostream>
#include<cassert>
template <typename T>
void Swap(T& x, T& y)
{
T tmp = x;
x = y;
y = tmp;
}
namespace HL
{
class string
{
friend std::ostream& operator<<(std::ostream& out, const HL::string& str);
friend std::istream& operator>>(std::istream& in, HL::string& str);
public:
//构造函数
/*string()
{
_str = new char[1];
_str[0] = '\0';
_size = 0;
_capacity = 0;
}
string(char ch)
{
_str = new char[1];
_str[0] = ch;
_size = 0;
_capacity = 0;
}*/
string(char ch = '\0')
{
_str = new char[2];
_str[0] = ch;
_str[1] = '\0';
_size = 1;
_capacity = 1;
}
string(const char* s)
{
size_t len = strlen(s);
_str = new char[len + 1];
memcpy(_str, s, len + 1);
_size = len;
_capacity = len;
}
string(const char* s, size_t n)
{
size_t len = strlen(s);
if (n > len)
{
n = len;
}
_str = new char[n + 1];
memcpy(_str, s, n);
_str[n] = '\0';
_size = n;
_capacity = n;
}
string(const string& str)
{
_str = new char[str._capacity + 1];
memcpy(_str, str._str, str._size + 1);
_size = str._size;
_capacity = str._capacity;
}
string(size_t n, char c)
{
_str = new char[n + 1];
for (size_t i = 0; i < n; i++)
{
_str[i] = c;
}
_str[n] = '\0';
_size = n;
_capacity = n;
}
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
/*string& operator=(const string& str)
{
delete[] _str;
_str = new char[str._capacity];
memcpy(_str, str._str, str._size + 1);
_size = str._size;
_capacity = str._capacity;
return *this;
}*/
/*string& operator= (const string& str)
{
string tmp(str);
Swap(_str, tmp._str);
Swap(_size, tmp._size);
Swap(_capacity, tmp._capacity);
return *this;
}*/
string& operator= (const string& str)
{
string tmp(str);
swap(tmp);
return *this;
}
/*string& operator= (const char* s)
{
size_t len = strlen(s);
delete[] _str;
_str = new char[len + 1];
memcpy(_str, s, len + 1);
_size = _capacity = len;
return *this;
}*/
string& operator= (const char* s)
{
string tmp(s);
Swap(_str, tmp._str);
Swap(_size, tmp._size);
Swap(_capacity, tmp._capacity);
return *this;
}
string& operator= (char c)
{
delete[] _str;
_str = new char[2];
_str[0] = c;
_str[1] = '\0';
_size = _capacity = 1;
return *this;
}
//string& operator= (char c)
//{
//string tmp(c);
//Swap(_str, tmp._str);
//Swap(_size, tmp._size);
//Swap(_capacity, tmp._capacity);
//return *this;
//}
//迭代器
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);
}
//下标访问 [ ]
char& operator[] (size_t pos)
{
assert(pos >= 0 && pos < _size);
return *(_str + pos);
}
const char& operator[] (size_t pos) const
{
assert(pos >= 0 && pos < _size);
return *(_str + _size);
}
//扩容
void reserve(size_t n)
{
if (n > _capacity)
{
char* s = new char[n + 1];
memcpy(s, _str, _size);
delete[] _str;
_str = s;
_capacity = n;
}
}
//增
void push_back(char c)
{
if (_size >= _capacity)
{
reserve((_capacity == 0) ? 4 : 2 * _capacity);
}
_str[_size] = c;
_size++;
_str[_size] = '\0';
}
void append(const string& str)
{
size_t n = _size + str._size;
if (n > _capacity)
{
reserve(n);
}
for (int i = 0; i < str._size; i++)
{
_str[_size + i] = str._str[i];
}
_size += str._size;
_str[_size] = '\0';
}
void append(const char* s)
{
size_t len = strlen(s);
size_t n = _size + len;
if (n > _capacity)
{
reserve(n);
}
for (int i = 0; i < len; i++)
{
_str[_size + i] = s[i];
}
_size = n;
_str[_size] = '\0';
}
void append(size_t n, char c)
{
if (_size + n > _capacity)
{
reserve(_size + n);
}
for (int i = 0; i < n; i++)
{
_str[_size + i] = c;
}
_size += n;
_str[_size] = '\0';
}
string& operator+=(const string& str)
{
this->append(str);
return *this;
}
string& operator+=(const char* s)
{
this->append(s);
return *this;
}
string& operator+=(char c)
{
this->push_back(c);
return *this;
}
//insert 、erase
void insert(size_t pos, const string& str)
{
assert(pos >= 0 && pos < _size);
size_t len = str._size;
size_t n = _size + len;
if (n > _capacity)
{
reserve(n);
}
//挪动数据
for (size_t i = n; i >= pos + len; i--)
{
_str[i] = _str[i - len];
}
memcpy(_str + pos, str._str, str._size);
_size += str._size;
_str[_size] = '\0';
}
void insert(size_t pos, const char* s)
{
assert(pos >= 0 && pos < _size);
size_t len = strlen(s);
size_t n = _size + len;
if (n > _capacity)
{
reserve(n);
}
//挪动数据
for (size_t i = n; i >= pos + len; i--)
{
_str[i] = _str[i - len];
}
memcpy(_str + pos, s, len);
_size += len;
_str[_size] = '\0';
}
void insert(size_t pos, size_t n, char c)
{
assert(pos >= 0 && pos < _size);
if (_size + n > _capacity)
{
reserve(_size + n);
}
//挪动数据
for (size_t i = _size+n; i >= pos + n; i--)
{
_str[i] = _str[i - n];
}
for (size_t i = 0; i < n; i++)
{
_str[pos + i] = c;
}
_size += n;
_str[_size] = '\0';
}
void erase(size_t pos, size_t len = npos)
{
assert(pos >= 0 && pos < _size);
if (len == npos)
{
_str[0] = '\0';
_size = 0;
return;
}
for (size_t i = pos; (len + i) < _size; i++)
{
_str[i] = _str[i + len];
}
_size -= len;
_str[_size] = '\0';
}
//find
size_t find(const string& str, size_t pos = 0)
{
assert(pos >= 0 && pos < _size);
char* tmp = strstr(_str + pos, str._str);
if (tmp == nullptr)
{
return -1;
}
return tmp - _str;
}
size_t find(const char* s, size_t pos = 0)
{
assert(pos >= 0 && pos < _size);
char* tmp = strstr(_str + pos, s);
if (tmp == nullptr)
{
return -1;
}
return tmp - _str;
}
size_t find(char c, size_t pos = 0)
{
assert(pos >= 0 && pos < _size);
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == c)
{
return i;
}
}
return -1;
}
//swap
void swap(string& str)
{
Swap(_str, str._str);
Swap(_size, str._size);
Swap(_capacity, str._capacity);
}
//c_str
const char* c_str() const
{
return _str;
}
char* c_str()
{
return _str;
}
//substr
string substr(size_t pos = 0, size_t len = npos) const
{
assert(pos >= 0 && pos < _size);
size_t n = 0;
if (len == npos || pos + len > _size)
{
n = _size - pos;
}
else
{
n = len;
}
string ret;
for (size_t i = 0; i < n; i++)
{
ret += _str[pos + i];
}
return ret;
}
//其他成员函数
size_t size() const
{
return _size;
}
size_t length()const
{
return _size;
}
size_t capacity()const
{
return _capacity;
}
void clear()
{
_str[0] = '\0';
_size = 0;
}
bool empty()const
{
return _size == 0;
}
void resize(size_t n)
{
if (n > _capacity)
{
reserve(n);
}
_size = n;
_str[_size] = '\0';
}
void resize(size_t n, char c)
{
if (n > _capacity)
{
reserve(n);
}
for (size_t i = _size; i < n; i++)
{
_str[i] = c;
}
_size = n;
_str[_size] = '\0';
}
private:
char* _str;
size_t _size;
size_t _capacity;
const static size_t npos;
};
const size_t HL::string::npos = -1;
std::ostream& operator<<(std::ostream& out, const HL::string& str)
{
//for (int i = 0; i < str.size(); i++)
//{
//out << str[i];
//}
//return out;
for (auto ch : str)
{
out << ch;
}
return out;
}
std::istream& operator>>(std::istream& in, HL::string& str)
{
char s[128] = { 0 };
char ch;
ch = in.get();
while (ch == ' ' || ch == '\n')
{
ch = in.get();
}
str.clear();
int i = 0;
while (ch != '\n')
{
s[i] = ch;
i++;
if (i == 127)
{
s[i] = '\0';
str += s;
i = 0;
}
ch = in.get();
}
if (i)
{
str += s;
}
return in;
}
};
上一篇: 【油猴脚本】00017 案例 Tampermonkey油猴脚本, 仅用于学习,不要乱搞。完善添加一列的功能,Jquery爬虫,JavaScript爬虫,HTML+Css+JavaScript编写
下一篇: Java毕业设计:Java江苏南京城市文化展示网站系统毕业设计源代码作品和开题报告
本文标签
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。