// // // // // // // // //
///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(constSomeClass& 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
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<
}
//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<
}
main.cpp
#include"Dessert.h"
#include
enum
{
Deep,
Shallow
};
int main()
{
Dessert* dessert[2];
dessert[Deep] = new DessertWithDepCopy();
dessert[Shallow] = new DessertWithShallowCopy();
std::cout<
dessert[Deep]->outputNum();
dessert[Shallow]->outputNum();
Dessert* newDessert[2];
//Copy construtor.
newDessert[Deep] = dessert[Deep]->copyDessert();
newDessert[Shallow] = dessert[Shallow]->copyDessert();
std::cout<
dessert[Deep]->outputNum();
dessert[Shallow]->outputNum();
return 0;
}
输出结果:
【注意事项】
如上图所示,在调用copyDessert方法之后,浅克隆的数量却没有增加,但这与我们程序的原有设想是相违背的。
因此,在自己编写的类中,要记得对复制构造函数进行override,从而降低Bug发生概率。