现在的位置: 首页 > 综合 > 正文

深层复制浅层复制,面试题

2013年07月04日 ⁄ 综合 ⁄ 共 1682字 ⁄ 字号 评论关闭

《程序员面试宝典》第三版,p95-96页邮到面试题,有兴趣的同学可以一起来看看:

#include "stdafx.h"
#include <iostream>
#include <vector>

using namespace std;

class Demo
{
public:
	Demo(const char* a)
	{
		str = new char[32];
		strcpy(str,a);
	}

	~Demo()
	{
		if (str)
			delete [] str;
	}
private:
	char *str;
};

int _tmain(int argc, _TCHAR* argv[])
{
	Demo d("Hello,World!");
	vector<Demo> *p = new vector<Demo>();
	p->push_back(d);
	delete p;
	return 0;
}

问:这段代码有错误,请找出其中出错的原因。

      代码自己定义了一个类,类中有一个变量,有构造函数,析构函数。在主函数中,实例化一个Demo类型的变量d,用Vector存放这个变量d,最后删除vector。执行这段代码之后,程序会出错,无法执行下去。

      要明白这个程序,得先了解当执行vector的push_back(d)函数时,加入到vector中的是原来的d对象呢,还是d对象的副本?

      答案是d对象的副本,当vector中添加元素的时候,是将元素值复制到容器里。就是说容器中存放的是原始元素的副本。被复制的原始值与新容器中的元素各不相关,此后,容器内元素值发生变化时,被复制的原值不会受到影响。

      既然加入到vector中的对象是副本,那么就涉及到d对象的复制,程序中并没有写复制构造函数,但是类会调用默认的复制构造函数(或者称为合成复制构造函数),默认构造函数只是简单的将对象的每个非static成员,依次复制到正创建的对象。Demo类只有一个成员变量char * str,这是一个指针类型的变量,因此会执行this.str = d.str;其结果是两个指针共同指向了同一块儿内存区域。(这就是所谓的浅层复制,不清楚的可以看看)

      因为当执行delete p;时,在销毁vector对象的同时,也会将vector中存储的对象同时销毁掉,d的副本str指向的区域被销毁掉,此时d的str成员就成了悬垂指针。当main执行完毕后,需要将d销毁掉,此时d的str成员是悬垂指针,因此会找不到要销毁的内容,程序就会出错。

     这个问题实质上的问题在于,类的深层复制和浅层复制的区别。当有指针类型成员变量的时,应该采取深层复制。当执行复制操作时,保证每个对象都有自己的独立的内存空间。

程序应该修改如下:

#include "stdafx.h"
#include <iostream>
#include <vector>

using namespace std;

class Demo
{
public:
	Demo(const char* a)
	{
		str = new char[32];
		strcpy(str,a);
	}

	Demo(const Demo& d)
	{
		this->str = new char[32];
		strcpy(str,d.str);
	}
	

	~Demo()
	{
		if (str)
			delete [] str;
	}
private:
	char *str;
};

int _tmain(int argc, _TCHAR* argv[])
{
	Demo d("Hello,World!");
	vector<Demo> *p = new vector<Demo>();
	p->push_back(d);
	delete p;
	return 0;
}

通过这道题,我总结了以下几个知识点:

1. 为了管理具有指针成员的类,必须自己定义三个复制控制成员:复制构造函数,复制操作符,析

    构函数。

2. vector中添加元素的时候,是将元素值复制到容器里。就是说容器中存放的是原始元素的副本。被

    复制的原始值与新容器中的元素各不相关,此后,容器内元素值发生变化时,被复制的原值不会

    受到影响,反之亦然。

3. 当删除vector对象时,会将vector中存储的对象一并删除掉。   

抱歉!评论已关闭.