我想让函数
clone()
实际上是可覆盖的,因为我需要它在我的应用程序中是多态的。
class Component
{
virtual Component *cloneImpl() const = 0;
public:
std::unique_ptr<Component> clone() const
{
return std::unique_ptr<Component>(this->cloneImpl());
}
};
截至目前,我设法将
clone()
函数隐藏在一些派生组件中,但这会导致严重的运行时问题。有没有可能让它以一种允许我这样做的方式工作? :
class DerivedComponent : public Component
{
DerivedComponent* cloneImpl() const
{
return new DerivedComponent();
}
public:
std::unique_ptr<DerivedComponent> clone() const override
{
return std::unique_ptr<DerivedComponent>(this->cloneImpl());
}
};
我在考虑使用模板,但不幸的是那并没有引导我到任何地方。
我希望它是真正的多态,而不仅仅是隐藏基类的
clone()
方法。
我担心我想做的事是不可能的,但我决定在这里问一下是值得的,只是为了确保我不会浪费更多时间思考它。
这个怎么样?只是猜测你在追求什么。它不是很清楚你到底想要什么..
template <typename T>
class Component {
virtual T *cloneImpl() const = 0;
public:
virtual ~Component() = default;
std::unique_ptr<T> clone() const {
return std::unique_ptr<T>(this->cloneImpl());
}
};
class DerivedComponent : public Component<DerivedComponent> {
private:
DerivedComponent* cloneImpl() const {
return new DerivedComponent();
}
public:
~DerivedComponent() = default;
};
我可能会按照这条总的思路编写代码:
#include <iostream>
#include <memory>
class Component
{
virtual Component *cloneImpl() const = 0;
public:
virtual void test() const = 0;
template <class T> friend std::unique_ptr<T> clone(T const &cmp) {
return std::unique_ptr<T>(cmp.cloneImpl());
}
template <class T> friend std::unique_ptr<T> clone(std::unique_ptr<T> const &cmp) {
return std::unique_ptr<T>(cmp->cloneImpl());
}
};
class Derived : public Component
{
Derived* cloneImpl() const override
{
return new Derived();
}
template <class T> friend std::unique_ptr<T> clone(std::unique_ptr<T> const &);
template <class T> friend std::unique_ptr<T> clone(T const &);
public:
virtual void test() const { std::cout << "Derived component\n"; }
};
class D2 : public Component {
D2 *cloneImpl() const override {
return new D2();
}
template <class T> friend std::unique_ptr<T> clone(std::unique_ptr<T> const &);
template <class T> friend std::unique_ptr<T> clone(T const &);
public:
virtual void test() const { std::cout << "D2\n"; }
virtual void test2() const { std::cout << "D2 unique item\n"; }
};
int main() {
Derived d;
D2 d2;
// test cloning a derived object:
auto ret1 = clone(d);
ret1->test();
// make sure clone of derived gives a unique_ptr<derived>
auto ret2 = clone(d2);
ret2->test();
ret2->test2();
// test cloning when we start with a unique_ptr<T>
auto ret3 = clone(ret2);
ret3->test2();
}
我应该在这里指出一些可能不是很明显的事情:
clone
是班级的朋友。由于 friend
ship 不是继承的,每个派生类都必须重新声明 clone
作为它的朋友。
尽管它的定义在
Component
的类定义中,但 clone
不是成员函数——因为它是 friend
,所以它不能是成员,所以它被“注入”到周围命名空间(在本例中为全局命名空间)。
虽然它是一个全局函数,但在全局命名空间中没有clone
的
声明。这意味着
clone
只能通过参数依赖查找找到。例如,如果我们添加一些不相关的类:
class Unrelated {
Unrelated *cloneImpl() { return new Unrelated; }
};
// ...
Unrelated u;
auto ret3 = clone(u);
...代码不会编译(即使
Unrelated
)提供了 cloneImpl
需要的 clone
,所以在其他情况下,它可以工作)。如果你想让它工作,我很确定将 friend
声明添加到 Unrelated
会让它工作。
我添加了另一个派生类,它定义了另一个未在基类中定义的成员函数,因此我们可以证明,当我们克隆派生对象时,我们得到一个实际的
unique_ptr<Derived>
,可用于调用派生-无需任何转换或其他转换的特定功能。
克隆通常意味着您正在通过指针(或在本例中为
unique_ptr
s)处理相当多的事情——所以我包含了重载以将输入作为对象或 unique_ptr
(都返回一个unique_ptr
给克隆)。
使用 C++20,我将定义一个隐式 CRTP 使用推导这个:
#include <comcepts>
struct Component
{
virtual ~Component() = 0; //must be virtual
virtual unique_ptr<Component> dynamic_clone() const = 0;
auto static_clone(this auto const& self) {
return std::make_unique
<std::remove_cvref_t
<decltype(self)>>(self);
}
};
template<std::derived_from<Component> derived>
struct Concrete : derived {
using derived::derived;
~Concrete() override = default;
unique_ptr<Component> dynamic_clone() const override
{ return {this->static_clone()}; };
};
auto derived_ptr = std::make_unique<Concrete<DerivedComponent>>();
static_clone
成员作用于其对象的静态类型,而dynamic_clone
成员根据其对象的动态类型创建一个新副本。因为整个层次结构被dynamic_clone
抽象,所以只能创建Concrete
个实例。