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(值语义多态)
- polymorphic_value 的最新提案见:wg21.link/P0201
- 提案者实现的 polymorphic_value 见:polymorphic_value
- 简单演示实现:Prototype_2 -a simple demo implementation of 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
- 《C++ 沉思录》 第5章 代理类
- virtual Clone - C++ Coding Standards