栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++ 模式设计 原型模式(深拷贝/克隆)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++ 模式设计 原型模式(深拷贝/克隆)

文章目录
  • 1. 理论知识
  • 2. 逻辑模板代码
  • 3. 应用
    • 3.1 应用1: 改写简历
    • 3.2 深浅拷贝的问题 ==*==
    • 3.3 深拷贝的嵌套使用

思考: 原型模式跟装饰器模式的区别?

本文核心:

	WorkExperience* clone(){
	    WorkExperience *w = new WorkExperience();
	    *w = *this; // 深拷贝一个相同的自己, 注意如果this中有指针, 需要在对这个指针类进行clone方法的实现, 这样层层嵌套, 就实现了整体的深拷贝
	    return w; // 将当前的this深拷贝给w, 返回
	}
1. 理论知识

当遇到需要复制的实例时, 我们就用到了原型模式, 比如打印20份自己的简历, 一个自己的简历类, 然后实例化一个简历A后, 在实例化 B=A, 但是很抱歉, 这里的B=A其实B是A的引用, 并不是真实的实例, 所以需要用到克隆技术

原型模式: 用原型实例指定创建对象的种类, 并且通过拷贝这些原型创建新的对象; 其实就是从一个对象在创建另外一个可定制的对象, 并且不需要知道创建的任何细节;

**使用场景: **
1、资源优化场景。
2、类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
3、性能和安全要求的场景。
4、通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
5、一个对象多个修改者的场景。
6、一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
7、在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。

注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的

原型模式的优缺点
原型模式的优点: 底层的二进制实现拷贝,相比起new操作可以节约性能
原型模式的缺点: 构造函数没有调用,牺牲了灵活性

2. 逻辑模板代码

// 原型模式起始就是从一个对象在创建另一个可定制的对象, 而且不需要知道任何创建细节
// 原型类
class Prototype{
private:
    string id;
public:
    Prototype(string id): id(id){}
    Prototype() : id("ndy"){}
    string get(){
        return id;
    }
    virtual Prototype* clone() = 0; // 这个方法是关键
};

// 具体原型类
class ConcretePrototype_1 : public Prototype{
private:

public:
    ConcretePrototype_1(string id): Prototype(id){}
    ConcretePrototype_1() {}
    // 相当于实现了深拷贝
    virtual Prototype *clone(){
        // 先开辟等量空间
        ConcretePrototype_1 *p = new ConcretePrototype_1();
        *p = *this; // 在将当前类深拷贝给上面空间
        return p;
    }
};

// 客户端
int main(int argc, char const *argv[]){
    Prototype *p1 = new ConcretePrototype_1("nb");
    Prototype *c1 = p1->clone();
    // ConcretePrototype_1 *p1 = new ConcretePrototype_1("nb");
    // ConcretePrototype_1 *c1 = (ConcretePrototype_1 *)p1->clone();
    cout << c1->get() < 
3. 应用 
3.1 应用1: 改写简历 

创建一个简历实例A, 在从这个实例A中修改数据变成实例B

// 简历
class Resume{
private:
    string name, sex, age, timeArea, company;
public:
    Resume(string name) : name(name){}
    // 设置个人信息
    void setPersonalInfo(string sex, string age) {
        this->sex = sex;
        this->age = age;
    }
    // 设置工作经历
    void setWorkExperience(string timeArea, string company){
        this->company = company;
        this->timeArea = timeArea;
    }
    // 显示
    void Display(){
        cout << "个人信息:" << name << " " << age << " " << sex << " " << timeArea << " " << company <setPersonalInfo("男", "25");
    nb->setWorkExperience("2015-2-2", "tencent");

    Resume *b2 = (Resume*) nb->clone();
    b2->setPersonalInfo("女" , "27");

    Resume *b3 = (Resume*) b2->clone();
    b3->setWorkExperience("2020-5-5", "baidu");

    b2->Display();
    nb->Display();
    b3->Display();

    delete nb, b2, b3;
    return 0;
}
3.2 深浅拷贝的问题 *

上面应用遇到一个问题, 当成员变量有指针时, 默认拷贝还是浅拷贝, 比如工作经历如果是一个类, 那么实例指针只会在拷贝的时候拷贝指针, 而不会拷贝实例, 这样拷贝的指针依然指向实例未变, 如果看不懂, 请看下面的例子

// 工作经历类
class WorkExperience{
private:
    string workDate, company;
public:
    void set(string workDate, string company){
        this->workDate = workDate, this->company = company;
    }
    string get_workDate(){return this->workDate;}
    string get_company(){return this->company;}
};

// 简历
class Resume{
private:
    string name, sex, age;
    WorkExperience *work = nullptr;
public:
    Resume(string name) : name(name), work(new WorkExperience()){}
    // 设置个人信息
    void setPersonalInfo(string sex, string age) {
        this->sex = sex;
        this->age = age;
    }
    // 设置工作经历
    void setWorkExperience(string timeArea, string company){
        work->set(timeArea, company);
    }
    // 显示
    void Display(){
        cout << "个人信息:" << name << " " << age <<" " << sex << " " << work->get_workDate() << " " << work->get_company() <setPersonalInfo("男", "25");
    nb->setWorkExperience("2015-2-2", "tencent");

    Resume *b2 = (Resume*) nb->clone();
    b2->setPersonalInfo("女" , "27");

    Resume *b3 = (Resume*) b2->clone();
    b3->setWorkExperience("2020-5-5", "baidu");

    b2->Display();
    nb->Display();
    b3->Display();

    delete nb, b2, b3;
    return 0;
}
个人信息:nb 27 女 2020-5-5 baidu
个人信息:nb 25 男 2020-5-5 baidu
个人信息:nb 27 女 2020-5-5 baidu

就会发现所有的工作经验都变成最后修改的值了, 这是因为浅拷贝, 无论怎么修改都是在修改第一个实例的工作经验

3.3 深拷贝的嵌套使用

在类里面都增加一个clone方法来克隆(深拷贝)对象, 这样就方便了下面使用该类进行深拷贝的操作了

// 工作经历类
class WorkExperience{
private:
    string workDate, company;
public:
    void set(string workDate, string company){
        this->workDate = workDate, this->company = company;
    }
    string get_workDate(){return this->workDate;}
    string get_company(){return this->company;}

    WorkExperience* clone(){ // b. 当调用这个方法时, 深拷贝了一个新work
        WorkExperience *w = new WorkExperience();
        *w = *this;
        return w; // 将当前的this深拷贝给w, 返回
    }
};

// 简历
class Resume{
private:
    string name, sex, age;
    WorkExperience *work = nullptr;
public:
    Resume(string name) : name(name), work(new WorkExperience()){} // a. 第一次创建简历时, 同时创建了工作经历类
    Resume(){}
    ~Resume(){ // 由于每个resume都对应一个work
        if(work != nullptr){
            delete work
        }
    }
    // 设置个人信息
    void setPersonalInfo(string sex, string age) {
        this->sex = sex;
        this->age = age;
    }
    // 设置工作经历
    void setWorkExperience(string timeArea, string company){
        work->set(timeArea, company);
    }
    // 显示
    void Display(){
        cout << "个人信息:" << name << " " << age <<" " << sex << " " << work->get_workDate() << " " << work->get_company() <work还是空的
        *r  =  *this;  // 此时r->work已经是this->work的深拷贝了, 但是里面的work指针还是指向this的work实例
        r->work = work->clone(); // 这样就深拷贝了一个新的work给r, 在返回r, 就是全新的了
        return r;
    }
};

// 这里实现了一个功能为除了name不变, 其他的信息可以改变的不同的简历
// 比如我做了一个简历V1, 然后简历V2是在V1的基础上修改了些信息做的
int main(int argc, char const *argv[])
{
    Resume *nb = new Resume("nb");
    nb->setPersonalInfo("男", "25");
    nb->setWorkExperience("2015-2-2", "tencent");

    Resume *b2 = (Resume*) nb->clone();
    b2->setPersonalInfo("女" , "27");

    Resume *b3 = (Resume*) b2->clone();
    b3->setWorkExperience("2020-5-5", "baidu");

    b2->Display();
    nb->Display();
    b3->Display();

    delete nb, b2, b3;
    return 0;
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/291058.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号