设计模式之六:原型模式(Prototype)

原型模式:使用原型实例来指定创建对象的种类,并通过拷贝这个对象的值来创建新的对象。

Specify the kind of objects to create using a prototypical instance, and create new objects by copying this prototype.

UML图:

这里写图片描述

主要包括

  1. Prototype:定义了一个包含克隆自身的接口
  2. ConcretePrototype:具体的原型类,实现了克隆自身函数的类
  3. Client:通过一个具体的原型实例来克隆自身。

其它语言针对上面的克隆有专门的函数,但是C++好像没有。针对上面的克隆函数,C++中可以通过拷贝构造函数来实现。而拷贝构造函数又涉及到了浅拷贝和深拷贝的问题。

先看看浅拷贝的克隆模式的C++代码实现:

#include <iostream>
#include <string>

using namespace std;
class Prototype
{
    public:
            Prototype(string str=""):id(str)
            {
            }
            //对C++而言需要返回指针
            virtual Prototype * clone()=0;
            string getId() const
            {
                return id;
            }
    private:
            string id;

};

class ConcretePrototype1:public Prototype
{
    public:
            ConcretePrototype1(string str=""):Prototype(str)
            {

            }
            Prototype * clone()
            {
                std::cout<<"ConcretePrototype1::clone()"<<std::endl;
                return new ConcretePrototype1(*this);
            }
};

class ConcretePrototype2:public Prototype
{
    public:
            ConcretePrototype2(string str=""):Prototype(str)
            {

            }
            Prototype * clone()
            {
                std::cout<<"ConcretePrototype2::clone()"<<std::endl;
                return new ConcretePrototype2(*this);
            }
};

int main()
{
    std::cout<<"原型模式测试"<<std::endl;
    ConcretePrototype1* cp1=new ConcretePrototype1("001");
    ConcretePrototype1* cp11=dynamic_cast<ConcretePrototype1 *>(cp1->clone());

    ConcretePrototype2* cp2=new ConcretePrototype2("002");
    ConcretePrototype2* cp22=dynamic_cast<ConcretePrototype2 *>(cp2->clone());

    delete cp1;
    delete cp11;
    delete cp2;
    delete cp22;
    return 0;
}

执行代码如下:
这里写图片描述
但是当代码中有自定义类型的指针成员变量时需要注意一个浅拷贝和深拷贝的问题,这个问题的实质是C++中的拷贝构造函数只是执行简单的按位复制操作(即浅拷贝),当成员变量中含有指针类型的数据时,它也只是简单的复制一下指针的值,不会复制一份指针所指向的内存(深拷贝)。
考虑下面的代码:
这对代码最后输出对象cp1和cp11更改前后的三项信息:

  1. 成员变量pChar的地址
  2. 成员变量data的地址
  3. 成员变量data所指向内存区域的值name和info

可以发现更改cp11中成员变量data的name字段后cp1中的成员变量data的name字段也发生了变化,这是由于data指向的是同一块内存区域,根据它们的地址信息可以很直观的看出这一点。

#include <iostream>
#include <string>
#include <stdlib.h>
#include <stdio.h>

using namespace std;

class Data
{
        public:
        Data(string n,string i):name(n),info(i)
        {

        }
                string name;
                string info;
};

class Prototype
{
    public:
            Prototype()
            {

            }
            Prototype(char *point):pChar(point)
            {
            }
            //对C++而言需要返回指针
            virtual Prototype * clone()=0;
            char * getPchar() const 
            {
                return pChar;
            }
            void setPchar(char * ch)
            {
                pChar=ch;
            }
            Data * getData() const
            {
                return data;
            }
            void setData(Data *d)
            {
                data=d;
            }
    private:
            char * pChar;
            Data * data;

};

class ConcretePrototype1:public Prototype
{
    public:
            ConcretePrototype1()
            {

            }
            ConcretePrototype1(char * point):Prototype(point)
            {

            }
            Prototype * clone()
            {
                std::cout<<"ConcretePrototype1::clone()"<<std::endl;
                return new ConcretePrototype1(*this);
            }
};


int main()
{
    std::cout<<"原型模式测试"<<std::endl;
    char p1[]="hello";
    char p2[]="world";
    Data data1("Bill","Microsofe");
    ConcretePrototype1* cp1=new ConcretePrototype1(p1);
    cp1->setData(&data1);
    ConcretePrototype1* cp11=dynamic_cast<ConcretePrototype1 *>(cp1->clone());

    std::cout<<"cp1 information:------------------"<<std::endl;
    printf("cp1->pChar address:%p\n",cp1->getPchar());
    printf("cp1->data address:%p\n",cp1->getData());
    std::cout<<"cp1->data->name:"<<cp1->getData()->name<<"    cp1->data->info    "<<cp1->getData()->info<<std::endl;

    std::cout<<"cp11 information:------------------"<<std::endl;
    printf("cp11->pChar address:%p\n",cp11->getPchar());
    printf("cp11->data address:%p\n",cp11->getData());
    std::cout<<"cp11->data->name:"<<cp11->getData()->name<<"    cp11->data->info    "<<cp11->getData()->info<<std::endl;

    cp11->setPchar(p2);
    cp11->getData()->name="change to John"; 

    std::cout<<"cp11 change:----------------"<<std::endl;
    std::cout<<"cp1 information:------------------"<<std::endl;
    printf("cp1->pChar address:%p\n",cp1->getPchar());
    printf("cp1->data address:%p\n",cp1->getData());
    std::cout<<"cp1->data->name:"<<cp1->getData()->name<<"    cp1->data->info    "<<cp1->getData()->info<<std::endl;

    std::cout<<"cp11 information:------------------"<<std::endl;
    printf("cp11->pChar address:%p\n",cp11->getPchar());
    printf("cp11->data address:%p\n",cp11->getData());
    std::cout<<"cp11->data->name:"<<cp11->getData()->name<<"    cp11->data->info    "<<cp11->getData()->info<<std::endl;


    delete cp1;
    delete cp11;
    return 0;
}

输出信息:
这里写图片描述

针对上面的问题需要使用C++中的深拷贝,即自己重新定义拷贝构造函数:

#include <iostream>
#include <string>
#include <stdlib.h>
#include <stdio.h>

using namespace std;

class Data
{
        public:
        Data()
        {

        }
        Data(string n,string i):name(n),info(i)
        {

        }
                string name;
                string info;
};

class Prototype
{
    public:
            Prototype()
            {

            }
            Prototype(char *point):pChar(point)
            {
            }
            Prototype(const Prototype& rhs)
            {
                //下面这条代码是关键,又重新分配了一块内存区域
                data=new Data(*rhs.getData());
                pChar=rhs.getPchar();
            }
            //对C++而言需要返回指针
            virtual Prototype * clone()=0;
            char * getPchar() const 
            {
                return pChar;
            }
            void setPchar(char * ch)
            {
                pChar=ch;
            }
            Data * getData() const
            {
                return data;
            }
            void setData(Data *d)
            {
                data=d;
            }
    private:
            char * pChar;
            Data * data;

};

class ConcretePrototype1:public Prototype
{
    public:
            ConcretePrototype1()
            {

            }
            ConcretePrototype1(char * point):Prototype(point)
            {

            }
            ConcretePrototype1(const ConcretePrototype1 & rhs):Prototype(rhs)
            {

            }
            Prototype * clone()
            {
                std::cout<<"ConcretePrototype1::clone()"<<std::endl;
                return new ConcretePrototype1(*this);
            }
};


int main()
{
    std::cout<<"原型模式测试"<<std::endl;
    char p1[]="hello";
    char p2[]="world";
    Data data1("Bill","Microsofe");
    ConcretePrototype1* cp1=new ConcretePrototype1(p1);
    cp1->setData(&data1);
    ConcretePrototype1* cp11=dynamic_cast<ConcretePrototype1 *>(cp1->clone());

    std::cout<<"cp1 information:------------------"<<std::endl;
    printf("cp1->pChar address:%p\n",cp1->getPchar());
    printf("cp1->data address:%p\n",cp1->getData());
    std::cout<<"cp1->data->name:"<<cp1->getData()->name<<"    cp1->data->info    "<<cp1->getData()->info<<std::endl;

    std::cout<<"cp11 information:------------------"<<std::endl;
    printf("cp11->pChar address:%p\n",cp11->getPchar());
    printf("cp11->data address:%p\n",cp11->getData());
    std::cout<<"cp11->data->name:"<<cp11->getData()->name<<"    cp11->data->info    "<<cp11->getData()->info<<std::endl;

    cp11->setPchar(p2);
    cp11->getData()->name="change to John"; 

    std::cout<<"cp11 change:----------------"<<std::endl;
    std::cout<<"cp1 information:------------------"<<std::endl;
    printf("cp1->pChar address:%p\n",cp1->getPchar());
    printf("cp1->data address:%p\n",cp1->getData());
    std::cout<<"cp1->data->name:"<<cp1->getData()->name<<"    cp1->data->info    "<<cp1->getData()->info<<std::endl;

    std::cout<<"cp11 information:------------------"<<std::endl;
    printf("cp11->pChar address:%p\n",cp11->getPchar());
    printf("cp11->data address:%p\n",cp11->getData());
    std::cout<<"cp11->data->name:"<<cp11->getData()->name<<"    cp11->data->info    "<<cp11->getData()->info<<std::endl;


    delete cp1;
    delete cp11;
    return 0;
}

执行代码如下,可以发现在克隆时cp1和cp11中data成员变量的地址就不一样了,这样更改cp11中的data成员后cp1中的data成员没有发生变化,这就是深拷贝了。
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值