目录
如果对于不涉及资源管理的自定义类型的操作(Date):
对涉及资源管理类操作(String):
一、插入四个元素(代码正常编译没有任何资源泄露问题)
二、当插入第五个元素时(开始扩容,使用memcpy进行字节拷贝(资源泄露+浅拷贝)代码崩溃)
如果对于不涉及资源管理的自定义类型的操作(Date):
以下为我的push_back()函数与 reserve()扩容函数 以及Date类的部分代码
在进行扩容的时候会使用memcpy()对内容按字节进行拷贝
/// 内容修改函数
void push_back(const T& val)
{
if (finish == endofstorage)
{
size_t newcap = capacity() == 0 ? 4 : capacity() * 1.5;
reserve(newcap);
}
*finish = val;
finish++;
}
void reserve(size_t newcapacity)
{
size_t oldcap = capacity();
if (newcapacity > oldcap)
{
// 开辟新空间
T* temp = new T[newcapacity];
if (start)
{
//拷贝元素
memcpy(temp, start, sizeof(T) * size());
/*for (size_t i = 0; i < size(); ++i)
{
temp[i] = start[i];
}*/
// 释放旧空间
delete[] start;
}
// 提前保存元素个数 方便更新finish
size_t sz = size();
start = temp;
finish = start + sz;
endofstorage = start + newcapacity;
}
}
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{
cout << "Date " << endl;
}
~Date()
{
cout << "~Date" << endl;
}
private:
int _year;
int _month;
int _day;
};
分析:
对比start的内容与内容对应的地址,不难发现,start对应的元素是正常尾插的,start中后面四个元素的地址是及其混乱的,这是因为Date类浅拷贝的问题,他们都使用的是同一块内存空间。如果加入了资源管理,析构的时候就会因为同一资源的多次释放发生代码崩溃。
对涉及资源管理类操作(String):
String类及其代码测试
class String
{
public:
String(const char* str = "")
{
if (str == nullptr)
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;
};
reserve()函数:
void reserve(size_t newcapacity)
{
size_t oldcap = capacity();
if (newcapacity > oldcap)
{
// 开辟新空间
T* temp = new T[newcapacity];
if (start)
{
//拷贝元素
memcpy(temp, start, sizeof(T) * size());
// 释放旧空间
delete[] start;
}
// 提前保存元素个数 方便更新finish
size_t sz = size();
start = temp;
finish = start + sz;
endofstorage = start + newcapacity;
}
}
void push_back(const T& val)
{
if (finish == endofstorage)
{
size_t newcap = capacity() == 0 ? 4 : capacity() * 1.5;
reserve(newcap);
}
*finish = val;
finish++;
}
当我的vector为空时第一次扩容会进行4个空间的扩容,那么在我push_back()四个元素想要进行第五个元素的push_back()时,就会进行下一次的扩容 ,扩容到4*1.5=6的空间,那么就会进入reserve(6)函数体内且传入参数为6.
因此这里的测试分为俩类:
一、插入四个元素(代码正常编译没有任何资源泄露问题)
梳理过程:
① 首先进行初始化v操作,v的start = finish = endofstorage = nullptr;
② 当插入第一个“111”时:因为没有空间,所以需要进行扩容,开辟4个String类型的空间,并且初值都为 “” 空字符串。
③ 然后分别构造出“1111” “2222” “3333” "4444",进行尾插
④ 正常执行没有问题
二、当插入第五个元素时(开始扩容,使用memcpy进行字节拷贝(资源泄露+浅拷贝)代码崩溃)
好 ,我们开始演示第二种情况,当空间容量已经无法继续放入元素了,这时候就需要进行扩容操作
① 准备插入第五个元素 “5555”
当我的vector为空时第一次扩容会进行4个空间的扩容,那么在我push_back()四个元素想要进行第五个元素的push_back()时,就会进行下一次的扩容 ,扩容到4*1.5=6的空间,那么就会进入reserve(6)函数体内且传入参数为6.
② 进入memcpy按字节扩容,可以发现temp中之前的空串 “” 全部被start中的四个元素一起覆盖掉了,造成了我们遇到的第一个问题(资源泄露)
③ 那么也就是说直接将原来四个元素的空间一起复制到新空间temp中去,再将endofstorage进行更新,即如下图示;那么temp与start目前是具有相同的元素,且元素都指向的是同一块空间,那么,进行下一步操作 释放旧空间,delete[] start就会将temp与start共同的空间给释放掉,那么在temp中的元素就成为了野指针了,继而用start指向temp使用新空间,第二个问题(浅拷贝问题)
④ 进行插入“5555”元素
⑤ 进行资源释放时,程序崩溃。
解决:
更换掉memcpy(),使用如下代码,采用赋值运算符重载,因为赋值运算符重载是采用深拷贝方式来进行的。
//拷贝元素
// memcpy(temp, start, sizeof(T) * size());
for (size_t i = 0; i < size(); ++i)
{
temp[i] = start[i];
}