用C++的复制构造函数实现原型模式

原型模式(Prototype Pattern)

定义[^1]

Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式结构图

通过仿照C#中的Cloneable()接口进行实现[^2]

ICloneable.h

class ICloneable
{
public:
	virtual ~ICloneable() {}

	virtual ICloneable* clone() const = 0;

};

Resume.h

#pragma once
#include "ICloneable.h"

#include <string>

class Resume :
    public ICloneable
{
public:
    Resume();
    Resume(const std::string& name);
    virtual ~Resume();

    void setPersonalInfo(const std::string& sex, const std::string& age);

    void setWorkExperience(const std::string& company, const std::string& timeArea);

    void display() const;

    virtual ICloneable* clone() const override;

    std::string getName() const;

    std::string getSex() const;

    std::string getAge() const;

    std::string getCompany(const std::string& timeArea) const;

    std::string getTimeArea(const std::string& company) const;

private:
    std::string mName = std::string("");
    std::string mSex = std::string("");
    std::string mAge = std::string("");
    std::string mTimeArea = std::string("");
    std::string mCompany = std::string("");

};

Resume.cpp

#include "Resume.h"

#include <iostream>

Resume::Resume()
{
}

Resume::Resume(const std::string& name) : mName(name)
{
}

Resume::~Resume()
{
}

void Resume::setPersonalInfo(const std::string& sex, const std::string& age)
{
	mSex = sex;
	mAge = age;
}

void Resume::setWorkExperience(const std::string& company, const std::string& timeArea)
{
	mCompany = company;
	mTimeArea = timeArea;
}

void Resume::display() const
{
	std::cout << getName() << " " << getSex() << " " << getAge() << std::endl;
	std::cout << "工作经历:" << getCompany(mTimeArea) << " " << getTimeArea(mCompany) << std::endl;
}

ICloneable* Resume::clone() const
{
	return static_cast<ICloneable *>(new Resume(*this));
}

std::string Resume::getName() const
{
	return mName;
}

std::string Resume::getSex() const
{
	return mSex;
}

std::string Resume::getAge() const
{
	return mAge;
}

std::string Resume::getCompany(const std::string& timeArea) const
{
	if (timeArea == mTimeArea)
		return mCompany;
	else
		return std::string("");
}

std::string Resume::getTimeArea(const std::string& company) const
{
	if (company == mCompany)
		return mTimeArea;
	else
		return std::string("");
}

其实这里,并不能看出什么问题;
我们只知道覆写clone()中有 new Resume(*this) 这段代码,它对应了复制构造函数的标准固定形式 Resume(const Resume&)
但是,我们并没有发现这个构造函数的声明和定义,它目前是由编译器默认生成的(同时,还包括赋值运算符…)。

接下来,我们新增一个 WorkExperience
WorkExperience.h

#pragma once
#include <string>

class WorkExperience
{
public:
	void setCompany(const std::string& company);
	std::string getCompany() const;

	void setWorkData(const std::string& workData);
	std::string getWorkData() const;

private:
	std::string mWorkData = std::string("");
	std::string mCompany = std::string("");

};

Resume 类中的两个成员变量做一个替换

#include "WorkExperience.h"
class ResumeS :
    public ICloneable
{
public:
    ResumeS();
    ResumeS(const std::string& name);
    virtual ~ResumeS();
    
    virtual ICloneable* clone() const override;

private:
    std::string mName = std::string("");
    std::string mSex = std::string("");
    std::string mAge = std::string("");
    WorkExperience* mWorkExp = nullptr;
};

此时,你如果运行下面的代码,将会让你眼前一亮

    Resume* rs1 = new Resume(std::string("码农A"));
    rs1->setPersonalInfo(std::string("男"), std::string("31"));
    rs1->setWorkExperience(std::string("XX公司"), std::string("1998-2000"));

    Resume* rs2 = dynamic_cast<Resume*>(rs1->clone());
    rs2->setWorkExperience(std::string("YY公司"), std::string("1998-2006"));

    Resume* rs3 = dynamic_cast<Resume*>(rs1->clone());
    rs3->setWorkExperience(std::string("ZZ公司"), std::string("1998-2003"));

    rs1->display();
    rs2->display();
    rs3->display();

    ResumeS* rss1 = new ResumeS(std::string("码农A"));
    rss1->setPersonalInfo(std::string("男"), std::string("31"));
    rss1->setWorkExperience(std::string("XX公司"), std::string("1998-2000"));

    ResumeS* rss2 = dynamic_cast<ResumeS*>(rss1->clone());
    rss2->setWorkExperience(std::string("YY公司"), std::string("1998-2006"));

    ResumeS* rss3 = dynamic_cast<ResumeS*>(rss1->clone());
    rss3->setWorkExperience(std::string("ZZ公司"), std::string("1998-2003"));

    rss1->display();
    rss2->display();
    rss3->display();

在这里插入图片描述
码农A在不同的时间段有三段不同的工作经历,而码农B却只有三份一模一样的工作经历。
这里,需要抛出两个概念:浅拷贝 vs. 深拷贝[^3]
C++编译器默认生成的复制构造函数只进行浅拷贝,即只拷贝成员变量的值。
*那这个时候,该怎么办呢? *

一、让WorkExperience继承抽象基类ICloneable

#include "ICloneable.h"
class WorkExperienceT : public ICloneable
{
public:
	virtual ~WorkExperienceT();

	virtual ICloneable* clone() const override;
};

ICloneable* WorkExperienceT::clone() const
{
	return static_cast<ICloneable*>(new WorkExperienceT(*this));
}

同时,在Resume类中新增一个私有的构造函数

class ResumeT :
    public ICloneable
{
private:
    ResumeT(const std::string& name, WorkExperienceT& we);
}
ResumeT::ResumeT(const std::string& name, WorkExperienceT& we) : mName(name)
{
	mWorkExp = dynamic_cast<WorkExperienceT*>(we.clone());
}

ICloneable* ResumeT::clone() const
{
	ResumeT* newRst = new ResumeT(getName(), *mWorkExp);
	newRst->setPersonalInfo(getSex(), getAge());
	return static_cast<ICloneable*>(newRst);
}

二、自定义复制构造函数

ResumeT(const ResumeT& rst);
ResumeT::ResumeT(const ResumeT& rst)
{
	if (this == (&rst))
		return;

	if (!(this->mWorkExp))
	{
		this->mWorkExp = new WorkExperienceT();
	}
	this->mWorkExp->setCompany(rst.getCompany());
	this->mWorkExp->setWorkData(rst.getTimeArea());
	this->mName = rst.getName();
	this->mSex = rst.getSex();
	this->mAge = rst.getAge();
}

ICloneable* ResumeT::clone() const
{
	return static_cast<ICloneable*>(new ResumeT(*this));
}

关于自定义构造函数可能还不完全,还需要自定义赋值运算符…

关于扩展,还可以用智能指针unique_ptr/make_unique的方式,仿照C#的Cloneable()接口来实现原型模式…

另外,也可以从多线程的数据竞争的问题角度来考量实现原型模式…

参考资料
[^1]: https://book.douban.com/subject/25843319/
[^2]: https://book.douban.com/subject/2334288/
[^3]:https://blog.csdn.net/m0_59068776/article/details/133916375

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值