文章目录
前言
模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数。
string的底层实现是一个动态顺序表,成员变量如下:
namespace nb//为了与库中区分,用命名空间封装
{
class string
{
public :
//成员函数
private:
char* _str;
size_t _capacity;
size_t _size;
//静态成员变量 --> size_t的最大值
static const size_t npos = -1;
};
}
1. 默认构造函数
// string s;
//string(const char* str = "\0") 错误示范
//string(const char* str = nullptr) 错误示范
string(const char* str = "")
{
size_t len = strlen(str);
_capacity = _size = len;
//_capacity是可以存有效字符的,+1存'\0'
_str = new char[_capacity + 1];
strcpy(_str, str);
//memcpy(_str, str, _size + 1);
}
2. 析构
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
3. 深浅拷贝问题
这里如果使用系统自动生成的拷贝构造会出错:
// 测试
void TestString()
{
string s1("hello world!");
string s2(s1);
}
上述string类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会生成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。
最终导致的问题是,s1、s2共用同一块内存空间,在释放时同一块 空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝
浅拷贝:
也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进行操作时,就会发生访问违规。
可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享
深拷贝:
如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供
4. 拷贝构造
//传统写法:
// s2(s1)
//string(const string& s)
//{
// _size = s._size;//_size和_capacity不包含'\0'
// _capacity = s._capacity;
// _str = new char[_capacity + 1];
// strcpy(_str, s._str);
//}
//现代写法:
//提供swap是因为:std::swap交换两个string对象
//将会发生1次拷贝构造,2次赋值,3次深拷贝代价高
void swap(string& s)
{
//仅交换成员
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
string(const string& s)
:_str(nullptr)//防止交换后tmp._str为随机值,析构出错
, _size(0)
, _capacity(0)
{
string tmp(s._str);//构造
swap(tmp);
}
5. 赋值运算符重载
//传统写法:
//string& operator=(const string& s)
//{
// if (this != &s)//防止自己给自己赋值 --> this没有const修饰
// {
// _size = s._size;
// _capacity = s._capacity;
// char* tmp = new char[_capacity + 1];
// delete[] _str;
// _str = tmp;
// strcpy(_str, s._str);
// }
// return *this;
//}
//现代写法:
string& operator=(string s)//注意不要传引用
{
swap(s);
return *this;
}
6. 迭代器
string的迭代器就是一个原生指针
typedef char* iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
7. reserve和resize
void reserve(size_t n)
{
//只有n > _capacity才扩容
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;//释放原来空间
_capacity = n;
_str = tmp;
}
}
void resize(size_t n, char c = '\0')
{
if (n > _size)
{
reserve(n);
for (size_t i = _size; i < n; ++i)
{
_str[i] = c;
}
_size = n;
_str[_size] = '\0';
}
else
{
_str[n] = '\0';
_size = n;
}
}
8. push_back等接口
- push_back
void push_back(char c)
{
if (_size == _capacity)
{
size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newCapacity);
}
_str[_size] = c;
++_size;
_str[_size] = '\0';
}
- operator+=
string& operator+=(char c)
{
push_back(c);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
- append
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size += len;
}
9. insert和erase
string& insert(size_t pos, const char* str)
{
size_t len = strlen(str);
//判断是否扩容
if (_size + len > _capacity)
{
reserve(_size + len);
}
size_t end = _size + len;
while (end > pos + len - 1)
{
_str[end] = _str[end - len];
--end;
}
strncpy(_str + pos, str, len);
_size += len;
return *this;
}
删除pos位置上的元素,并返回该元素的下一个位置
string& erase(size_t pos, size_t len = npos)
{
assert(pos < _size);
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + len + pos);
_size -= len;
}
return *this;
}
10. 流插入和流提取
std::ostream& operator<<(std::ostream& _cout, const nb::string& s)
{
for (size_t i = 0; i < s.size(); ++i)
{
_cout << s[i];
}
return _cout;
}
std::istream& operator>>(std::istream& _cin, nb::string& s)
{
s.clear();
char buff[128] = { '\0' };
size_t i = 0;
char ch = _cin.get();
while (ch != ' ' && ch != '\n')
{
if (i == 127)
{
// 满了
s += buff;
i = 0;
}
//先输入到buffer中,避免频繁扩容
buff[i++] = ch;
ch = _cin.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
return _cin;
}
整体代码
string.h
#pragma once
#include <iostream>
#include <assert.h>
namespace nb
{
class string
{
public:
typedef char* iterator;
string(const char* str = "")
{
size_t len = strlen(str);
_capacity = _size = len;
_str = new char[_capacity + 1];//_capacity是可以存有效字符的,+1存\0
strcpy(_str, str);
//memcpy(_str, str, _size + 1);
}
//传统写法:
// s2(s1)
//string(const string& s)
//{
// _size = s._size;//_size和_capacity不包含'\0'
// _capacity = s._capacity;
// _str = new char[_capacity + 1];
// strcpy(_str, s._str);
//}
//现代写法:
//提供swap是因为:std::swap交换两个string对象
//将会发生1次拷贝构造,2次赋值,3次深拷贝代价高
void swap(string& s)
{
//仅交换成员
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
string(const string& s)
:_str(nullptr)//防止交换后tmp._str为随机值,析构出错
, _size(0)
, _capacity(0)
{
string tmp(s._str);//构造
swap(tmp);
}
//传统写法:
//string& operator=(const string& s)
//{
// if (this != &s)//防止自己给自己赋值 --> this没有const修饰
// {
// _size = s._size;
// _capacity = s._capacity;
// char* tmp = new char[_capacity + 1];
// delete[] _str;
// _str = tmp;
// strcpy(_str, s._str);
// }
// return *this;
//}
//现代写法:
string& operator=(string s)//注意不要传引用
{
swap(s);
return *this;
}
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
//
// iterator
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
/
// modify
void push_back(char c)
{
if (_size == _capacity)
{
size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newCapacity);
}
_str[_size] = c;
++_size;
_str[_size] = '\0';
}
string& operator+=(char c)
{
push_back(c);
return *this;
}
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size += len;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
void clear()
{
_str[0] = '\0';
_size = 0;
}
const char* c_str()const
{
return _str;
}
/
// capacity
size_t size()const
{
return _size;
}
size_t capacity()const
{
return _capacity;
}
bool empty()const
{
return _size == 0;
}
void resize(size_t n, char c = '\0')
{
if (n > _size)
{
reserve(n);
for (size_t i = _size; i < n; ++i)
{
_str[i] = c;
}
_size = n;
_str[_size] = '\0';
}
else
{
_str[n] = '\0';
_size = n;
}
}
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_capacity = n;
_str = tmp;
}
}
///
access
char& operator[](size_t index)
{
assert(index < _size);//注意断言
return _str[index];
}
//const只读
const char& operator[](size_t index)const
{
assert(index < _size);
return _str[index];
}
///
relational operators
bool operator<(const string& s)
{
return strcmp(_str, s._str) < 0;
}
bool operator<=(const string& s)
{
return strcmp(_str, s._str) <= 0;
}
bool operator>(const string& s)
{
return !(*this <= s);
}
bool operator>=(const string& s)
{
return !(*this < s);
}
bool operator==(const string& s)
{
return strcmp(_str, s._str) == 0;
}
bool operator!=(const string& s)
{
return !(*this == s);
}
返回c在string中第一次出现的位置
size_t find(char c, size_t pos = 0) const
{
assert(pos < _size);
for (size_t i = pos; i < _size; ++i)
{
if (_str[i] == c)
{
return i;
}
}
return npos;
}
返回子串s在string中第一次出现的位置
size_t find(const char* s, size_t pos = 0) const
{
assert(pos < _size);
char* ptr = strstr(_str + pos, s);
if (ptr == NULL)
{
return npos;
}
else
{
return ptr - _str;
}
}
在pos位置上插入字符c/字符串str,并返回该字符的位置
string& insert(size_t pos, char c)
{
assert(pos <= _size);
if (_size == _capacity)
{
size_t newCapacity = _capacity == 0 ? 4 : 2 * _capacity;
reserve(newCapacity);
}
//挪数据
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = c;
++_size;
return *this;
}
string& insert(size_t pos, const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
size_t end = _size + len;
while (end > pos + len - 1)
{
_str[end] = _str[end - len];
--end;
}
strncpy(_str + pos, str, len);
_size += len;
return *this;
}
删除pos位置上的元素,并返回该元素的下一个位置
string& erase(size_t pos, size_t len = npos)
{
assert(pos < _size);
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + len + pos);
_size -= len;
}
return *this;
}
private:
char* _str;
size_t _capacity;
size_t _size;
const static size_t npos = -1;
};
std::ostream& operator<<(std::ostream& _cout, const nb::string& s)
{
for (size_t i = 0; i < s.size(); ++i)
{
_cout << s[i];
}
return _cout;
}
std::istream& operator>>(std::istream& _cin, nb::string& s)
{
s.clear();
char buff[128] = { '\0' };
size_t i = 0;
char ch = _cin.get();
while (ch != ' ' && ch != '\n')
{
if (i == 127)
{
// 满了
s += buff;
i = 0;
}
//先输入到buffer中,避免频繁扩容
buff[i++] = ch;
ch = _cin.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
return _cin;
}
}