C++string容器模拟实现
Posted Suk-god
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++string容器模拟实现相关的知识,希望对你有一定的参考价值。
文章目录
1.浅拷贝问题解决
1.1浅拷贝存在的问题
对于浅拷贝,主要存在于拷贝构造和赋值运算符重载的过程中,下面给出一段代码结合分析
#pragma warning(disable:4996)
#include<iostream>
using namespace std;
class String
public:
String(const char* str = "")
if (nullptr == str)
str = " ";
_str = new char[strlen(str) + 1];
strcpy(_str, str);
~String()
if (_str)
delete[] _str;
_str = nullptr;
private:
char* _str;
;
void StringTest()
String s1("Hello");
String s2(s1);
int main()
StringTest();
return 0;
执行时程序直接崩溃
下面分析一下原因:
上述的崩溃时由于浅拷贝导致的多次释放问题
再来看下面的代码快:
分析:
本次报错是因为执行s2 = s1时,导致内存泄漏以及多次释放的问题,具体看下面的分析
总结一下:
浅拷贝会导致①内存泄露②多次释放同一块空间
这些错误都是极其严重的,我们务必要避免!
接下来就来探讨一下如何解决这些问题:
1.2通过深拷贝的方式解决浅拷贝问题
本质:让每一个对象都拥有一份独立的资源
传统版解决方式
- 解决拷贝构造
- 解决赋值运算符重载
下面给出解决的完整代码
#pragma warning(disable:4996)
#include<iostream>
using namespace std;
class String
public:
String(const char* str = "")
if (nullptr == str)
str = " ";
_str = new char[strlen(str) + 1];
strcpy(_str, str);
String(const String& s)
:_str(new char[strlen(s._str)+1])
strcpy(_str, s._str);
String& operator=(const String& s)
if (this != &s)
char* temp = new char[strlen(s._str) + 1];
strcpy(temp, s._str);
delete[] _str;
_str = temp;
return *this;
~String()
if (_str)
delete[] _str;
_str = nullptr;
private:
char* _str;
;
void StringTest()
String s1("Hello");
String s2("World");
s2 = s1;
//String s2(s1);
int main()
StringTest();
return 0;
现代版解决方式
仔细观察上面的代码,我们发现其实重复的操作很多,比如每次申请新空间,拷贝元素。
现代版的方式就是采用巧妙地代码复用,将繁琐的操作简洁化
具体修改在拷贝构造和赋值运算符重载处
下面我们一一解决
- 拷贝构造的优化
- 赋值运算符重载的优化
以上便是对之前代码的一些优化!
1.3通过写时拷贝解决
理解写时拷贝方法
写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。
引用计数:用来记录资源使用者的个数。
在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,
当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,
如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;
否则就不能释放,因为还有其他对象在使用该资源。
下面使用图解的方式解释一下:
OK,看了上面的解法,有没有感觉有点问题呢?
上面的过程并没有涉及到对象内容修改,假设我现在想将上面s2对象的内容修改为“World”;由于s1 s2共用一块内存空间,所以修改s2的同时s1的内容也被修改了
但是很明显,这不是我的本意,也不符合规矩。那如何解决这个棘手问题呢?
其实上面的这个过程还没有涉及到我们的写时拷贝。
所谓的写时拷贝是在上面的基础上解决修改(写)内容的时候发生的问题
具体步骤如下:
假设我们现在已经处于上面图示的场景,即s1和s2共用同一块内存空间,计数器此时为2
现在我想要将s2的内容改为“World”,需要执行以下步骤:
(1)为s2对象开辟新的空间
(2)将原来的空间的计数器值减减
(3)将s1对象的内容拷贝至为s2新开辟的空间中,并将s2中计数器值设置为1
(4)s2在新空间内进行修改操作
下面通过图示的方式再次演示该过程:
以上便是写时拷贝的粗略思路,具体细节内容等到后续总结,目前处于扫盲状态。
1.4验证不同平台的string类是通过什么方式解决浅拷贝的
验证思路:
(1)实例化一个对象s1,长度大于15;
注意:长度必须大于15,否则无法测试出正确的结果。因为在Windows下,string类中维护着一个空间为16的字符数组,因此小于等于15的字符串直接被存储在数组内,并不会申请空间,也就无法验证。
(2)通过拷贝构造实例化对象s2
(3)打印s1和s2对象的地址,观察地址值是否一样
如果一样 -----> 写时拷贝
如果不一样 ----> 深拷贝
验证代码如下:
void TestCopy()
string s1(20, 'A');
string s2(s1);
printf("&s1 is %p\\n",s1.c_str());
printf("&s2 is %p\\n", s2.c_str());
int main()
TestCopy();
return 0;
1、在Windows平台下的VS2013环境中
结论:vs2013中string是按照深拷贝实现的
2、在Linux平台下
结论:Linux中string是按照写时拷贝实现的
2、string类实现
有了前面的知识做铺垫,这里直接给出代码,模拟实现只是对主要的方法进行模拟,并不是完全实现一个string容器
//mystring.h
#pragma once
#include<iostream>
namespace gyj
class string
friend std::ostream& operator<<(std::ostream& _cout, const gyj::string& s);
public:
typedef char* iterator;
public:
/构造和析构///
string(const char* s = "");
string(const string& str);
string& operator=(const string& str);
~string();
///迭代器相关/
iterator begin();
iterator end();
///容量相关//
size_t size()const;
size_t capacity()const;
bool empty()const;
void resize(size_t n, char c = '\\0');
void reserve(size_t n);
///元素访问相关//
char& operator[](size_t index);
const char& operator[](size_t index)const;
///修改相关的///
void push_back(char c);
string& operator+=(char c);
void append(const char* str);
string& operator+=(const char* str);
void clear();
void swap(string& s);
const char* c_str()const;
/其他//
bool operator<(const string& s);
bool operator<=(const string& s);
bool operator>(const string& s);
bool operator>=(const string& s);
bool operator==(const string& s);
bool operator!=(const string& s);
// 返回c在string中第一次出现的位置
size_t find(char c, size_t pos = 0) const;
// 返回子串s在string中第一次出现的位置
size_t find(const char* s, size_t pos = 0) const;
// 在pos位置上插入字符c/字符串str,并返回该字符的位置
string& insert(size_t pos, char c);
string& insert(size_t pos, const char* str);
// 删除pos位置上的元素,并返回该元素的下一个位置
string& erase(size_t pos, size_t len);
private:
char* _str;
size_t _size;
size_t _capacity;
;
extern void stringTest1();
extern void stringTest2();
接下来是mystring.cpp
#pragma warning(disable:4996)
#include<iostream>
#include"mystring.h"
#include<assert.h>
gyj::string::string(const char* s)
if (nullptr == s)
assert(0);
_size = strlen(s);
_capacity = _size;
_str = new char[_capacity+ 1];
strcpy(_str, s);
gyj::string::string(const string& str)
:_str(nullptr)
, _size(0)
, _capacity(0)
string temp(str._str);
std::swap(_str, temp._str);
_size = str._size;
_capacity = str._capacity;
gyj::string& gyj::string::operator=(const string& str)
if (this != &str)
string temp(str._str);
std::swap(_str, temp._str);
_size = str._size;
_capacity = str._capacity;
return *this;
gyj::string::~string()
if (_str)
delete[] _str;
_str = nullptr;
///迭代器相关/
gyj::string::iterator gyj::string::begin()
return _str;
gyj::string::iterator gyj::string::end()
return _str + _size;
///容量相关//
size_t gyj::string::size()const
return _size;
size_t gyj::string::capacity()const
return _capacity;
bool gyj::string::empty()const
if (0 == _size)
return true;
return false;
void gyj::string::resize(size_t n, char c)
if (n > _size)
if (n > _capacity)
reserve(n);
memset(_str + _size, c, n - _size);
_size = n;
_str[_size] = '\\0';
void gyj::string::reserve(size_t n)
if (n > _capacity)
char* temp = new char[n + 1];
strcpy(temp, _str);
delete[] _str;
_str = temp;
_capacity = n;
///元素访问相关//
char& gyj::string::operator[](size_t index)
assert(index < _size);
return _str[index];
const char& gyj::string::operator[](size_t index)const
assert(index < _size);
return _str[index];
///修改相关的///
void gyj::string::push_back(char c)
if (_size == _capacity)
//扩容
reserve(_capacity * 2);
_str[_size++] = c;
_str[_size] = '\\0';
gyj::string& gyj::string::operator+=(char c)
push_back(c);
return *this;
void gyj::string::append(const char* str)
if (_size+strlen(str) >= _capacity)
//扩容
size_t newcapacity = _capacity * 2 > _size + strlen(str) ? _capacity * 2 : _size + strlen(str) + 1;
reserve(newcapacity);
strcat(_str, str);
_size += strlen(str);
gyj::string& gyj::string::operator+=(const char* str)
append(str);
return *this;
void gyj::string::clear()
_str[0] = '\\0';
_size = 0;
void gyj::string::swap(string& s)
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
const char* gyj::string::c_str()const
return _str;
// <<重载
std::ostream& gyj::operator<<(std::ostream& _cout, const gyj::string& s)
_cout << s._str;
return _cout;
/其他//
bool gyj::string::operator<(const string& s)
int ret = strcmp(_str, s._str);
if (ret < 0)
return true;
return false;
bool gyj::string::operator<=(const string& s)
return !(*this>s);
bool gyj::string::operator>(const string& s)
int ret = strcmp(_str, s._str);
if (ret > 0)以上是关于C++string容器模拟实现的主要内容,如果未能解决你的问题,请参考以下文章
[C/C++]详解STL容器1--string的功能和模拟实现(深浅拷贝问题)
C++初阶:string类string类 | 浅拷贝和深拷贝(传统写法和现代写法) | string类的模拟实现
C++初阶:STL —— stringstring类 | 浅拷贝和深拷贝(传统写法和现代写法) | string类的模拟实现
C++初阶:STL —— stringstring类 | 浅拷贝和深拷贝(传统写法和现代写法) | string类的模拟实现