提高克隆模式的安全性 如果想在C++中实现克隆> /COD>模式,他可能不确定安全性,因为派生类可能忘记重写它: struct A { virtual A* Clone() const { return new A(*this); } } struct B : A { int value; }; int main() { B b; // oops auto b_clone = b.Clone(); delete b_clone; } 在C++中,如何改进C++中的克隆< /代码>模式?
提出了一个更一般性的问题:提高克隆模式的安全性 如果想在C++中实现克隆> /COD>模式,他可能不确定安全性,因为派生类可能忘记重写它: struct A { virtual A* Clone() const { return new A(*this); } } struct B : A { int value; }; int main() { B b; // oops auto b_clone = b.Clone(); delete b_clone; } 在C++中,如何改进C++中的克隆< /代码>模式?,c++,design-patterns,crtp,cloneable,C++,Design Patterns,Crtp,Cloneable,提出了一个更一般性的问题: 但是,C++中似乎有一个好的解决方案太普遍了——讨论的是可能的方法来执行方法重写。我更感兴趣的是发现一种有用的模式,这可能有助于使用可克隆模式。这是对其中一个答案的详细说明,建议使用typeid进行运行时检查: 使用CRTP,可以得出以下基本想法: 创建类Cloneable,该类管理派生的的克隆,并添加所有需要的运行时检查(似乎即使使用CRTP也不可能进行编译时检查) 然而,这并非小事,还必须通过Cloneable管理继承,如下所述: #include <m
但是,C++中似乎有一个好的解决方案太普遍了——讨论的是可能的方法来执行方法重写。我更感兴趣的是发现一种有用的模式,这可能有助于使用可克隆模式。
这是对其中一个答案的详细说明,建议使用typeid进行运行时检查: 使用CRTP,可以得出以下基本想法: 创建类Cloneable
,该类管理派生的
的克隆,并添加所有需要的运行时检查(似乎即使使用CRTP也不可能进行编译时检查)
然而,这并非小事,还必须通过Cloneable
管理继承,如下所述:
#include <memory>
#include <cassert>
#include <type_traits>
#include <typeinfo>
class CloneableInterface {
public:
virtual std::unique_ptr<CloneableInterface> Clone() const = 0;
};
template <class... inherit_from>
struct InheritFrom : public inherit_from... {
};
template <class Derived, class AnotherBase = void, bool base_is_cloneable = std::is_base_of_v<CloneableInterface, AnotherBase>>
class Cloneable;
// three identical implementations, only the inheritance is different
// "no base is defined" case
template <class Derived>
class Cloneable<Derived, void, false> : public CloneableInterface {
public:
std::unique_ptr<CloneableInterface> Clone() const override {
assert(typeid(*this) == typeid(Derived));
return std::make_unique<Derived>(static_cast<const Derived&>(*this));
}
};
// Base is defined, and already provides CloneableInterface
template <class Derived, class AnotherBase>
class Cloneable<Derived, AnotherBase, true> : public AnotherBase {
...
};
// Base is defined, but has no CloneableInterface
template <class Derived, class AnotherBase>
class Cloneable<Derived, AnotherBase, false> : public AnotherBase, public CloneableInterface {
...
};
#包括
#包括
#包括
#包括
类克隆接口{
公众:
虚拟std::unique_ptr Clone()const=0;
};
模板
结构继承自:公共继承自。。。{
};
模板
类可克隆;
//三个相同的实现,只是继承不同
//“未定义基础”案例
模板
类可克隆:公共可克隆接口{
公众:
std::unique_ptr Clone()常量覆盖{
断言(typeid(*this)=typeid(派生));
return std::make_unique(静态_cast(*this));
}
};
//Base已定义,并已提供CloneableInterface
模板
类可克隆:另一个公共基{
...
};
//Base已定义,但没有CloneableInterface
模板
类Cloneable:public-AnotherBase,public-CloneableInterface{
...
};
用法示例:
class Base : public Cloneable<Base> {
};
// Just some struct to test multiple inheritance
struct Other {
};
struct Derived : Cloneable<Derived, InheritFrom<Base, Other>> {
};
struct OtherBase {
};
struct OtherDerived : Cloneable<OtherDerived, InheritFrom<OtherBase>> {
};
int main() {
// compiles and runs
auto base_ptr = std::make_unique<Base>();
auto derived_ptr = std::make_unique<Derived>();
auto base_clone = base_ptr->Clone();
auto derived_clone = derived_ptr->Clone();
auto otherderived_ptr = std::make_unique<OtherDerived>();
auto otherderived_clone = otherderived_ptr->Clone();
}
类库:公共可克隆{
};
//只是一些测试多重继承的结构
结构其他{
};
派生结构:可克隆{
};
结构OtherBase{
};
结构Other派生:可克隆{
};
int main(){
//编译并运行
auto base_ptr=std::make_unique();
自动派生的_ptr=std::使_唯一();
auto base_clone=base_ptr->clone();
自动派生的克隆=派生的克隆->克隆();
auto-otherderived_ptr=std::make_unique();
auto otherderived_clone=otherderived_ptr->clone();
}
欢迎任何批评和改进建议 C++17及更新版本提供了。理论上,您可以创建一个克隆函数,返回std::any*
:
struct A {
virtual std::any* Clone() const {
return new A(*this);
}
}
struct B : A {
int value;
// I suppose it doesn't have to be virtual here,
// but just in case we want to inherit the cloning capability from B as well
virtual std::any* Clone() const { // Note: you still need to override this function
return new B(*this); // in the lower levels, though
}
};
// Note: I'm still on MSVS2010, so this C++17 code is untested.
// Particularly problematic could be this main
int main() {
B b;
// Here is the clone
auto b_clone = std::any_cast<B*>(b.Clone());
delete b_clone;
}
结构A{
虚拟std::any*Clone()常量{
返回新的A(*本);
}
}
结构B:A{
int值;
//我想这里不必是虚拟的,
//但为了以防万一,我们也想从B继承克隆功能
虚拟std::any*Clone()常量{//注意:您仍然需要重写此函数
返回新的B(*this);//但是在较低的级别
}
};
//注意:我仍然在MSVS2010上,所以这段C++17代码未经测试。
//特别有问题的可能是这一主要问题
int main(){
B B;
//这是克隆人
auto b_clone=std::any_cast(b.clone());
删除b_克隆;
}
同样,这是未经测试的,但在理论上应该是可行的。派生类不重写它与安全无关。基类中缺少虚拟析构函数,并且使用裸指针。@VTT对于这些琐碎的类来说是okNo,这不正常,但却是导致未定义行为的主要原因@datell首先,示例中没有析构函数,即使是编译器生成的析构函数。所以我不相信你,这是一个UBIt就是UB,看。好吧,好吧,我明白你的笑话,把所有的责任都交给那个叫它的人,但这并不是一种模式改进:)也许我误解了你对当前模式的问题。你对当前模式有什么问题?@Chipster问题是你可能会忘记在B中覆盖。我想我误解了。这实际上似乎非常合法,尽管我从未使用过
std::any
@datell I recon。我的理念是,无论子对象是否重写它,它都只能返回a*
,而不是您所期望的B*
。这就解决了这个问题。不过,我想我的解决方案中仍然没有任何东西可以阻止您不重写该函数。可能得考虑一下。