通过动手实现String类的构造、拷贝构造、赋值运算符重载以及析构函数
以下分别包括传统写法和现代写法:
传统写法每一步都通过申请空间,资源清理、释放空间;
现代写法理解为进行了偷懒,通过调用构造函数、拷贝构造来实现。
class String
{
public:
String(const char* str = "")
:_size(strlen(str))
,_capacity(_size)
,_data(new char[strlen(str) + 1])
{
strcpy(_data, str);
}
void Swap(String& str)
{
swap(_size, str._size);
swap(_capacity, str._capacity);
swap(_data, str._data);
}
private:
//有效字符个数
size_t _size;
//容量:最大的存放字符的个数
size_t _capacity;
char* _data;
};
拷贝构造函数的传统写法与现代写法:
拷贝构造函数的传统写法:
String类的构造函数需要用到深拷贝,首先要知道什么是深拷贝?深拷贝与浅拷贝的区别是什么?
首先清楚析构函数的作用,先进行资源清理,再进行空间释放。
例如:通过char* pr = new int; 动态申请了一块整形空间,pr指针指向该空间。在使用完成之后要调用析构函数进行空间释放,包括两部分:
资源清理:就是将动态申请的整型空间释放(该空间在堆上)
空间释放:将pr指针的指向置空
浅拷贝:只是将指针指向了该资源,相当于两个指针(拷贝和被拷贝)指向了同一份资源,在进行空间释放的时候会进行二次释放。
深拷贝:拷贝指针通过申请新的空间,并将资源拷贝过来。相当于两个指针分别指向两个资源,这样在进行空间释放时不会出现二次释放问题。
//传统写法:深拷贝:拷贝对象的内容 + 对象的资源
String(const String& str)
:_size(0)
, _capacity(0)
,_data(new char[strlen(str._data) + 1])
{
//拷贝资源
strcpy(_data, str._data);
}
拷贝构造函数的现代写法:
//拷贝构造:现代写法 --> 复用拷贝的逻辑
// 借助构造函数在创建局部对象时,进行空间申请 + 内容拷贝
// 通过交换成员的执行,最终达到拷贝构造的目的
String(const String& str)
:_size(0)
, _capacity(0)
, _data(nullptr)
{
String tmp(str._data);
Swap(tmp);
}
赋值运算符重载的传统写法与现代写法:
赋值运算符重载的传统写法:
//传统写法:赋值运算符重载
String& operator=(const String& str)
{
if (this != &str)
{
//释放原有的资源空间
delete[] _data;
//开新的资源空间
_data = new char[strlen(str._data) + 1];
//拷贝内容
strcpy(_data, str._data);
}
return *this;
}
赋值运算符重载的现代写法1:
//现代写法1:调用构造函数
String& operator=(const String& str)
{
String tmp(str._data);
Swap(tmp);
return *this;
}
赋值运算符重载的现代写法2(主要):
//现代写法2:调用拷贝构造
// 借助拷贝构造完成:进行空间申请 + 内容拷贝
// 借助局部对象的析构函数完成当前对象原有资源的释放
String& operator=(String str)
{
Swap(str);
return *this;
}
现代写法:
class String
{
public:
//String迭代器
//实现:字符指针
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
//第一个元素的位置
return _data;
}
const_iterator begin() const
{
//第一个元素的位置
return _data;
}
iterator end()
{
//最后一个元素的下一个位置
return _data + _size;
}
const_iterator end() const
{
//最后一个元素的下一个位置
return _data + _size;
}
//构造函数
String(const char* str = "")
:_size(strlen(str))
,_capacity(_size)
,_data(new char[strlen(str) + 1])
{
strcpy(_data, str);
}
//拷贝构造:现代写法 --> 复用拷贝的逻辑
// 借助构造函数在创建局部对象时,进行空间申请 + 内容拷贝
// 通过交换成员的执行,最终达到拷贝构造的目的
String(const String& str)
:_size(0)
, _capacity(0)
, _data(nullptr)
{
String tmp(str._data);
Swap(tmp);
}
//析构函数
~String()
{
if (_data)
{
delete[] _data;
_data = nullptr;
_size = _capacity = 0;
}
}
//交换
void Swap(String& str)
{
swap(_size, str._size);
swap(_capacity, str._capacity);
swap(_data, str._data);
}
//扩容
void reserve(size_t n)
{
//只增加容量
if (n > _capacity)
{
//1.开空间
char* tmp = new char[n + 1];
//2.拷贝
strcpy(tmp, _data);
//释放原有空间
delete[] _data;
//更新成员
_data = tmp;
_capacity = n;
}
}
const char* c_str() const
{
return _data;
}
//现代写法2:调用拷贝构造
// 借助拷贝构造完成:进行空间申请 + 内容拷贝
// 借助局部对象的析构函数完成当前对象原有资源的释放
String& operator=(String str)
{
Swap(str);
return *this;
}
size_t size() const
{
return _size;
}
size_t capacity() const
{
return _capacity;
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _data[pos];
}
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _data[pos];
}
void append(const char* str)
{
//1.检查容量
int len = strlen(str);
if (_size + len >= _capacity)
{
reserve(_size + len);
}
//2.插入
memcpy(_data + _size, str, sizeof(char) * len);
//3.更新
_size += len;
_data[_size] = '\0';
}
void pushBack(const char ch)
{
if (_size == _capacity)
{
size_t newC = _capacity == 0 ? 15 : 2 * _capacity;
reserve(newC);
}
_data[_size] = ch;
++_size;
_data[_size] = '\0';
}
//插入一个字符串
String& operator+=(const char* str)
{
append(str);
return *this;
}
//插入一个String对象
String& operator+=(const String& str)
{
append(str._data);
return *this;
}
String& operator+=(const char ch)
{
pushBack(ch);
return *this;
}
void insert(size_t pos, const char& ch)
{
//检查位置的有效性
assert(pos <= _size);
//1.检查容量
if (_size == _capacity)
{
size_t newC = _capacity == 0 ? 15 : 2 * _capacity;
reserve(newC);
}
//2.移动元素:从后向前
size_t end = _size + 1;
while (end > pos)
{
_data[end] = _data[end - 1];
--end;
}
//3.插入
_data[pos] = ch;
//更新
_size++;
}
void insert(size_t pos, const char* ptr)
{
//检查位置的有效性
assert(pos <= _size);
//1.检查容量
int len = strlen(ptr);
if (_size + len > _capacity)
{
reserve(_size + len);
}
//移动元素
size_t end = _size + len;
while (end > pos + len - 1)
{
_data[end] = _data[end - len];
--end;
}
//3.插入
//for (int i = 0; i < len; ++i)
//{
// _data[i + pos] = ptr[i];
//}
memcpy(_data + pos, ptr, sizeof(char) * len);
//更新
_size++;
}
void erase(size_t pos, size_t len = npos)
{
assert(pos < _size);
//从pos开始,删除剩余的所有元素
if (len == npos || pos + len >= _size)
{
_size = pos;
_data[_size] = '\0';
}
else
{
//从前向后,每一个待移动的元素统一向前移动len个位置
size_t start = pos + len;
//[pos + len, size]
while (start <= _size)
{
_data[start - len] = _data[start];
++start;
}
_size -= len;
}
}
size_t find(const char& ch, size_t pos = 0)
{
for (size_t i = pos; i < _size; ++i)
{
if (_data[i] == ch)
return i;
}
//未找到或者位置越界
return npos;
}
size_t find(const char* ptr, size_t pos = 0)
{
assert(pos < _size);
char* ptr = strstr(_data + pos, ptr);
if (ptr)
{
return ptr - _data;
}
return npos;
}
String substr(size_t pos, size_t len = npos)
{
assert(pos < _size);
if (len == npos || pos + len >= _size)
{
String str(_data + pos);
return str;
}
else
{
char* newC = new char[len];
memcpy(newC, _data + pos, sizeof(char) * len);
newC[len] = '\0';
return String(newC);
delete[] newC;
}
}
private:
//有效字符个数
size_t _size;
//容量:最大的存放字符的个数
size_t _capacity;
char* _data;
static const size_t npos;
};
const size_t String::npos = -1;
String operator+(const String& str1, const String& str2)
{
String str = str1;
str += str2;
return str;
}
String operator+(const char* str1, const String& str2)
{
String str(str1);
str += str2;
return str;
}
String operator+(const char ch, const String& str2)
{
String str = str2;
str += ch;
return str;
}
bool operator<(const String& str1, const String& str2)
{
int ret = strcmp(str1.c_str(), str2.c_str());
if (ret < 0)
return true;
return false;
}
bool operator==(const String& str1, const String& str2)
{
int ret = strcmp(str1.c_str(), str2.c_str());
if (ret == 0)
return true;
return false;
}
// > :!( <= )
ostream& operator<<(ostream& out, const String& str)
{
//不是按照c分割的字符串形式输出
for (const auto& ch : str)
{
out << ch;
}
return out;
}
istream& operator>>(istream& in, String& str)
{
char ch;
//while ((ch = cin.get()) != EOF)
while ((ch = getchar()) != EOF)
{
if (ch == ' ' || ch == '\n')
break;
//尾插
str += ch;
}
return cin;
}