配套视频课程已更新完毕,大家可通过以下两种方式观看视频讲解:

关注公众号:爱编程的大丙,或者进入大丙课堂学习。


1. 克隆人

在海贼王世界中,杰尔马王国拥有一支强大的科学作战部队 – 杰尔马66军团,其先锋是文斯莫克家族,他们作为雇佣军活跃在世界各地。

这支部队战斗力强悍,没有情感,不畏生死,勇往直前。从某种意义上来讲,他们不能被称之为人,因为他们是科学的结晶,他们都出自文斯莫克·伽治之手。

img

伽治曾和海贼世界中的顶级科学家贝加庞克是同事,一起发现了血统因子,于是才有了现在这么多的杰尔马士兵,对,你没有看错他们都是被克隆出来的。

克隆是一种最直接、最快捷的创建新对象的方式,它不仅隐藏了创建新对象的诸多细节,还保留了源对象的属性信息,保证了这两个对象能够一模一样。

img

血统因子士兵这是一个复杂而又艰辛的过程,一旦研发成功,之后的事情就是基于母体进行复制(克隆)。我猜伽治不仅是一名科学家,可能还是一位架构师,因为他懂设计模式,这种制作克隆人的模式就是原型模式

原型模式就是能够复制已有的对象,而又无需使代码依赖它们所属的类。换种说法,就是通过已有对象克隆出另一个新的对象,并且克隆这个对象不需要使用构造函数。

2. 这是在脱了裤子放屁吗

懂C++的亲们看了上面关于原型模式的描述肯定是满脑子问号。因为在C++中只要定义一个类,这个类就默认自带六大函数,其中一个就是拷贝构造函数,这个函数的作用就是通过一个已有对象克隆出一个新的对象。一个拷贝构造函数就能搞定的事情为啥还要搞出一种设计模式呢?

这是脱了裤子放屁吗?肯定不是,因为这里边还隐藏着一个细节。

  • 对于伽治来说,他可能想通过一代士兵克隆出更优秀的二代士兵
  • 对于程序猿来说,我们可能想要通父类指针或引用把指向的子类对象克隆出来

通过这个描述,就可以从里面挖掘出一个重要的信息:克隆可能会在父类和子类之间进行,并且可能是动态的,很明显通过父类的拷贝构造函数无法实现对子类对象的拷贝,其实这就是一个多态,我们需要给父类提供一个克隆函数并且是一个虚函数

现在逻辑关系已经说明白了,来看一下对应UML类图

3. 量产士兵

根据上面的UML类图,我们就可以把对应的代码写出了,示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
using namespace std;

class GermaSoldier
{
public:
virtual GermaSoldier* clone() = 0;
virtual string whoAmI() = 0;
virtual ~GermaSoldier() {}
};

class Soldier66 : public GermaSoldier
{
public:
GermaSoldier* clone() override
{
return new Soldier66(*this);
}
string whoAmI() override
{
return string("我是杰尔马66的超级士兵!!!");
}
};

class Soldier67 : public GermaSoldier
{
public:
GermaSoldier* clone()
{
return new Soldier67(*this);
}
string whoAmI() override
{
return string("我是杰尔马67的超级士兵!!!");
}
};

int main()
{
GermaSoldier* obj = new Soldier66;
GermaSoldier* soldier = obj->clone();
cout << soldier->whoAmI() << endl;
delete soldier;
delete obj;

obj = new Soldier67;
soldier = obj->clone();
cout << soldier->whoAmI() << endl;
delete soldier;
delete obj;
}

代码中的main()函数对应的就是UML类图中的客户端角色。

  • 第41行通过父类指针克隆了子类Soldier66的对象
  • 第47行通过父类指针克隆了子类Soldier67的对象
  • 在这两个士兵子类的clone()函数体内部是通过当前子类的拷贝构造函数复制出了一个新的子类对象。

程序执行的结果如下:

1
2
我是杰尔马66的超级士兵!!!
我是杰尔马67的超级士兵!!!

通过输出的结果可以看到通过父类指针克隆子类的对象成功了。

image-20220901235029144