C++中如何由一个指向派生类对象的基类指针克隆一个相同的派生类对象

Prototype
解决方案是使用设计模式中的 Prototype (原型模式),典型实现如下:

#include <memory>
#include <typeinfo>
#include <cassert>

class Prototype
{
public:
	virtual std::unique_ptr<Prototype> Clone() const = 0;
	virtual ~Prototype() = default;
protected:
	Prototype() = default;
	
	// Make the polymorphic base class copy and move operations protected to prevent slicing, 
	// and so that only the derived class can invoke them in its own copy and move operations.
	Prototype(const Prototype&) = default;
	Prototype& operator=(const Prototype&) = defalut;
};

class Derived : public Prototype
{
public:
	std::unique_ptr<Prototype> Clone() const override
	{
		return std::make_unique<Derived>(*this);
	}
};

int main()
{
	std::unique_ptr<Prototype> p = std::make_unique<Derived>();
	std::unique_ptr<Prototype> q = p->Clone();

	assert((p != q) && (typeid(*p) == typeid(*q)) && (typeid(*q) == typeid(Derived)));

	return 0;
}

上面的解决方案已经很好了,但存在两个不足:类继承层次结构中进一步派生的类可能会忘记实现 Clone,同时 Clone 的覆写的实现也可能会出错,从而使所得副本与原对象的类型并不一样。
Clone 更完美的实现应该应用 NVI 模式,这种模式将 Clone 的 public 特性和 virtual 特性分开,从而使我们得以插入一个重要的断言,以检查派生类对虚克隆函数的覆写是否有正确实现,详见 virtual Clone - C++ Coding Standards, 典型实现如下:

#include <memory>
#include <typeinfo>
#include <cassert>

class Prototype
{
	public:
	// Note: a non-virtual function
	std::unique_ptr<Prototype> Clone() const
	{
		std::unique_ptr<Prototype> result = DoClone();
		assert(typeid(*result) == typeid(*this) && "Every derived class must correctly override DoClone.");
	}
	virtual ~Prototype() = default;
	
protected:
	Prototype() = default;
	
	// Make the polymorphic base class copy and move operations protected to prevent slicing, 
	// and so that only the derived class can invoke them in its own copy and move operations.
	Prototype(const Prototype&) = default;
	Prototype& operator=(const Prototype&) = defalut;

private:
	virtual std::unique_ptr<Prototype> DoCLone() const = 0;
};

class Derived : public Prototype
{
public:
	Derived1() = default;
	~Derived1() override = default;

protected:
	Derived1(const Derived1&) = default;
	Derived1& operator=(const Derived1&) = default;

private:
	std::unique_ptr<Prototype> DoClone() const override
	{
		// Note: create a temporary object
		return std::unique_ptr<Derived1>(new Derived1(*this));
	}

	std::unique_ptr<Prototype> Clone() const override
	{
		return std::make_unique<Derived1>(new Derived1(*this));
	}
};

// Note: only a final derived class has public copy and move operations
class Derived2 final : public Derived1
{
public:
	Derived2() = default;
	Derived2(const Derived2&) = default;
	Derived2& operator=(const Derived2&) = default;
	~Derived2() override = default;

private:
	std::unique_ptr<Prototype> DoClone() const override
	{
		return std::make_unique<Derived2>(*this);
	}
};

int main()
{
	std::unique_ptr<Prototype> p = std::make_unique<Derived2>();
	std::unique_ptr<Prototype> q = p->Clone();

	assert((p != q) && (typeid(*p) == typeid(*q)) && (typeid(*q) == typeid(Derived2)));

	return 0;
}

polymorphic_value
prototype 的一个典型应用使用来实现 polymorphic_value(值语义多态)

#include <iostream>
#include <vector>
#include <array>
#include <iterator>
#include <memory>
#include <utility>
#include <typeinfo>
#include <cassert>

class Shape
{
public:
	std::unique_ptr<Shape> Clone() const
	{
		std::unique_ptr<Shape> result = DoClone();
		assert(typeid(*result) == typeid(*this) && "Every derived class must correctly override DoClone.");
		return result;
	}

	virtual void Draw() = 0;

	virtual ~Shape() = default;

protected:
	Shape() = default;

	// Make the polymorphic base class copy and move operations protected to prevent slicing, 
	// and so that only the derived class can invoke them in its own copy and move operations.
	Shape(const Shape&) = default;
	Shape(Shape&&) = default;
	Shape& operator=(const Shape&) = default;
	Shape& operator=(Shape&&) = default;

private:
	virtual std::unique_ptr<Shape> DoClone() const = 0;
};

class Circle : public Shape
{
public:
	void Draw() override
	{
		std::cout << "Draw a circle." << std::endl;
	}

private:
	std::unique_ptr<Shape> DoClone() const override
	{
		return std::make_unique<Circle>(*this);
	}
};

class Square : public Shape
{
public:
	void Draw() override
	{
		std::cout << "Draw a square." << std::endl;
	}

private:
	std::unique_ptr<Shape> DoClone() const override
	{
		return std::make_unique<Square>(*this);
	}
};

// a simple demo implementation of polymorphic_value
class Picture
{
public:
	explicit Picture(std::unique_ptr<Shape> shape) : shape_(std::move(shape)) {  }

	Picture(const Picture& rhs) : shape_(rhs.shape_->Clone()) {  }

	Picture& operator=(const Picture& rhs)
	{
		if (this != &rhs)
		{
			shape_ = rhs.shape_->Clone();
		}
		return *this;
	}

	Picture(Picture&&) = default;
	Picture& operator=(Picture&&) = default;
	~Picture() = default;

	void ChangeShape(std::unique_ptr<Shape> shape)
	{
		shape_ = std::move(shape);
	}

	void Draw()
	{
		shape_->Draw();
	}

private:
	std::unique_ptr<Shape> shape_;
};

int main()
{
	Picture picture1(std::make_unique<Circle>());
	picture1.Draw();
	Picture picture2(std::make_unique<Square>());
	picture2.Draw();

	Picture picture3(picture1); // OK: copyable
	picture3.Draw();
	picture3 = picture2; // OK: copyable
	picture3.Draw();

	Picture picture4(std::move(picture1));
	picture4.Draw();
	picture4 = std::move(picture2);
	picture4.Draw();

	Picture picture5(std::make_unique<Circle>());
	picture5.Draw();
	picture5.ChangeShape(std::make_unique<Square>());
	picture5.Draw();

	std::vector<Picture> pictures1;
	pictures1.emplace_back(std::make_unique<Circle>());
	pictures1.emplace_back(std::make_unique<Square>());
	for (auto& picture : pictures1)
	{
		picture.Draw();
	}

	std::vector<std::unique_ptr<Shape>> shapes1;
	shapes1.push_back(std::make_unique<Circle>());
	shapes1.push_back(std::make_unique<Square>());
	for (auto& shape : shapes1)
	{
		shape->Draw();
	}

	std::vector<Picture> pictures2{ picture3, picture4, picture5 };
	std::vector<Picture> pictures3(pictures1); // OK: copyable
	std::vector<Picture> pictures4(std::move(pictures1));
	pictures3 = pictures2; // OK: copyable
	pictures4 = std::move(pictures2);

	std::vector<std::unique_ptr<Shape>> shapes2;
	shapes2.push_back(std::make_unique<Circle>());
	shapes2.push_back(std::make_unique<Circle>());
	shapes2.push_back(std::make_unique<Circle>());

	auto init = std::to_array<std::unique_ptr<Shape>>({ std::make_unique<Circle>(), std::make_unique<Square>() });
	std::vector<std::unique_ptr<Shape>> shapes3(std::make_move_iterator(init.begin()), std::make_move_iterator(init.end()));

	//std::vector<std::unique_ptr<Shape>> shapes4(shapes1); // Error: noncopyable
	std::vector<std::unique_ptr<Shape>> shapes5(std::move(shapes1));
	//shapes3 = shapes2; // Error: noncopyable
	shapes5 = std::move(shapes2);

	return 0;
}

References

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值