容器元素的深拷贝和浅拷贝问题

STL容器所提供的都是值(value)寓意,而非引用(reference)寓意,也就是说当我们给容器中插入元素的时候容器内部实施了拷贝动作,将我们要插入的元素再另行拷贝一份放入到容器中,而不是将原数据元素直接放进容器中,也就说我们提供的元素必须能够被拷贝 .

看下面的代码:

//容器元素的深拷贝和浅拷贝问题
#include "pch.h"
#include <iostream>
#include <vector>
#include <cstdlib>
using namespace std;

class CDemo
{
public:
	CDemo() :str(NULL) {}
	~CDemo()
	{
		if (str)
		{
			delete[] str;
		}
		
	}
	char* str;  //指针,容易带来浅拷贝的问题
};

int main()
{
	CDemo d1;
	d1.str = new char[32];
	strcpy_s(d1.str,strlen("trend micro") + 1,"trend micro");

	vector<CDemo>* a1 = new vector<CDemo>;
	a1->push_back(d1);  //只是简单的值拷贝
	delete a1;  //同一块内存析构了两次
	return 0;
}

这个程序在退出的时候会出现问题,重复delete同一片内存,程序崩溃。
将析构函数修改如下,可以更加清楚的看到问题所在:

	~CDemo()
	{
		if (str)
		{
			static int i = 0;
			cout << "&CDemo" << i++ << "=" << (int*)this << ",str = " << (int *)str << endl;
			delete[] str;
		}
		
	}


也就是说,发生了CDemo类的两次析构,并且两次析构str所指向的同一内存地址空间(两次str的值相同)。
问题出在哪里?
有人认为vector对象指针能够自动析构,所以不需要调用delete a1,否则会造成两次析构对象。这种理解是不准确的,任何对象如果是通过new操作符申请了空间,必须显示的调用delete来销毁这个对象,所以delete a1这条语句是没有错误的。
错误在于:
在执行

a1->push_back(d1);  //只是简单的值拷贝

这条语句时,会调用CDemo的拷贝构造函数,虽然CDemo类中没有定义拷贝构造函数,但是编译器会为CDemo类构建一个默认的拷贝构造函数(浅拷贝),正是这里出了问题,a1中的所有CDemo元素的str成员变量没有初始化,只有一个四字节(32位机)指针空间。

a1->push_back(d1);  //只是简单的值拷贝

这句话执行完之后,a1里的CDemo元素与d1是不同的对象,但是a1里的CDemo元素的str与d1.str指向的是同一块内存
局部变量“CDemo d1;”在main函数退出时,自动释放所占内存空间,前面已经调用过delete a1,已经把d1.str释放了,main函数退出时,又要释放掉已经释放掉的d1.str内存空间,所以程序最后崩溃。
解决办法:
给CDemo类添加一个拷贝构造函数即可

	CDemo(const CDemo &cd)
	{
		this->str = new char[strlen(cd.str) + 1];
		strcpy_s(str,strlen(cd.str) + 1,cd.str);
	}

另:最好再重载一个 = 运算符
参考资料
《程序员面试宝典》(第四版)

posted @ 2019-06-26 09:51  尚修能的技术博客  阅读(776)  评论(0编辑  收藏  举报