【C++】string的模拟实现-创新互联
文章目录
前言
2. 析构
3. 深浅拷贝问题
4. 拷贝构造
5. 赋值运算符重载
7. reserve和resize
8. push_back等接口
9. insert和erase
10. 流插入和流提取
整体代码
文章标题:【C++】string的模拟实现-创新互联
URL分享:http://scyanting.com/article/dicgjs.html
- 前言
- 1. 默认构造函数
- 2. 析构
- 3. 深浅拷贝问题
- 4. 拷贝构造
- 5. 赋值运算符重载
- 6. 迭代器
- 7. reserve和resize
- 8. push_back等接口
- 9. insert和erase
- 10. 流插入和流提取
- 整体代码
前言
模拟实现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#include
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;
}
}
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
文章标题:【C++】string的模拟实现-创新互联
URL分享:http://scyanting.com/article/dicgjs.html