// // // // // // // // //
///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发生概率。