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

【2013.1.20】故事的最后,哆啦A梦终于又回到了大雄身边。从此两个人过上了…——ProtoType

2018年10月09日 ⁄ 综合 ⁄ 共 3142字 ⁄ 字号 评论关闭

// // // // // // // // //

///2013.1.20

// // // // // // // // //

还记得小的时候看哆啦A梦,

里面有一话讲到大雄用一个神奇的工具,

好像是一个灯之类的东西,

照一照点心,

点心就会自动复制到无限多(2^n)。

先不管这是不是一件让人羡慕的事情,

但是这个道具,无意间实现了我们今天所要讲的模式——Prototype。

【核心】使用复制(Clone)代替创建(new)对象。

UML图表示:

从表面上看,

与其说是一个模式,

倒不如说是一种复制行为更为贴切。

然而,

这其中,却隐藏着另一个难以察觉的问题。

它涉及到了对象拷贝的两种方式:

——深拷贝与浅拷贝。

在探讨这个两种拷贝方式的区别之前,

首先要提到&这个运算符:

一般情况下,

它的使用方法与指针类似(但不是相等),

&a表示的是a的引用。

一个最简单的使用方法如下所示:

void changeValue(const int& a)
{
	a +=10;
}

int value = 10;
changeValue(value);
printf("%d",value);//输出结果为20

一般函数传递的话,

并不可以直接修改实参的值,

然而我们可以利用传递实参的引用(即其地址)来做到这一点。

那么&这个运算符与Prototype有有什么关系呢?

其实,

在C++中,

即使是我们自定义的类,

也存在着一个看不到的复制构造函数,

它的写法格式如下:

SomeClass(const SomeClass&
C)

正因为存在着这个函数,

我们才能够使用如下的方法进行对象之间的直接赋值创建:

SomeClass a;

SomeClass b(a);

这是非常方便的。

但是,

很遗憾,

默认的复制构造函数只是浅拷贝,

不是深拷贝。

二者大致的区别就是:

浅拷贝如果遇到动态变量(例如A类中聚合B类对象),就不能正确工作了,因为它只是将地址的引用传给了另一个对象,并没有另外开辟一段内存。

深拷贝就是从这方面着想,对内存实实在在地进行了第二份拷贝(而不是耍小聪明只是传个引用)。

具体深拷贝与浅拷贝的区别可以看这篇文章:http://blog.csdn.net/bluescorpio/article/details/4322682


因此,

我们实现ProtoType的时候,

最值得注意的地方就是要创建适用于自己类的复制构造函数,

而不仅仅只是使用C++默认的那个。

具体代码实例:

【大致思路】

虚基类Dessert类的两个派生类分别使用了深克隆与浅克隆的方法,可以在他们同样调用copyDessert之后输出的结果中看出来这两种克隆方法的差异。

Dessert.h

#ifndef _DESSERT_H_
#define _DESSERT_H_
class Dessert
{
public:
	Dessert(){}
	~Dessert(){}

	virtual Dessert* copyDessert() = 0;
	virtual void outputNum() = 0;
};

class DessertWithDepCopy:public Dessert
{
public:
	DessertWithDepCopy();
	DessertWithDepCopy(const DessertWithDepCopy& con);

	~DessertWithDepCopy();

	static int dessertNum;

	Dessert* copyDessert();
	void outputNum();
};

//Didn't define the copy constructor(Use default).
class DessertWithShallowCopy:public Dessert
{
public:
	DessertWithShallowCopy();
	~DessertWithShallowCopy();

	static int dessertNum;

	Dessert* copyDessert();
	void outputNum();
};

#endif

Dessert.cpp

#include "Dessert.h"
#include<iostream>

int DessertWithDepCopy::dessertNum = 0;
int DessertWithShallowCopy::dessertNum = 0;

//DeepCopy class's defination.
DessertWithDepCopy::DessertWithDepCopy()
{
	dessertNum++;
}

DessertWithDepCopy::DessertWithDepCopy(const DessertWithDepCopy& con)
{
	dessertNum++;
}

DessertWithDepCopy::~DessertWithDepCopy()
{
	if(--dessertNum < 0)
		dessertNum = 0;
}

Dessert* DessertWithDepCopy::copyDessert()
{
	return new DessertWithDepCopy(*this);
}

void DessertWithDepCopy::outputNum()
{
	std::cout<<"Dessert(Deep copy) number is: "<<dessertNum<<std::endl;
}


//ShallowCopy class's defination.
DessertWithShallowCopy::DessertWithShallowCopy()
{
	dessertNum++;
}

DessertWithShallowCopy::~DessertWithShallowCopy()
{
	if(--dessertNum < 0)
		dessertNum = 0;
}

Dessert* DessertWithShallowCopy::copyDessert()
{
	return new DessertWithShallowCopy(*this);
}

void DessertWithShallowCopy::outputNum()
{
	std::cout<<"Dessert(Shallow copy) number is: "<<dessertNum<<std::endl;
}

main.cpp

#include"Dessert.h"
#include<iostream>
enum
{
	Deep,
	Shallow
};

int main()
{
	Dessert* dessert[2];
	dessert[Deep] = new DessertWithDepCopy();
	dessert[Shallow] = new DessertWithShallowCopy();

	std::cout<<"\nConstructor"<<std::endl;
	dessert[Deep]->outputNum();
	dessert[Shallow]->outputNum();

	Dessert* newDessert[2];

	//Copy construtor.
	newDessert[Deep] = dessert[Deep]->copyDessert();
	newDessert[Shallow] = dessert[Shallow]->copyDessert();

	std::cout<<"\nCopy Constructor"<<std::endl;
	dessert[Deep]->outputNum();
	dessert[Shallow]->outputNum();

	return 0;
}

输出结果:

【注意事项】

如上图所示,在调用copyDessert方法之后,浅克隆的数量却没有增加,但这与我们程序的原有设想是相违背的。

因此,在自己编写的类中,记得对复制构造函数进行override,从而降低Bug发生概率。

抱歉!评论已关闭.