这个问题与复制和指针多态有关。请考虑以下代码。我们有两个类:基础和派生,它们只是常规对象。然后我们有了 Foo 类,它有一个指向 Base 的指针作为其唯一成员。
main
函数中描述了 Foo 的典型用法。 Foo::SetMemberX
的输入可能是也可能不是临时对象。
问题在于我希望Foo::SetMember
创建传递对象的正确副本,并将其地址指定为Base*
到Foo::mMember
。
我已经设法提出了4种可能的解决方案,但对我来说似乎都不是很优雅。前三项显示在Foo::SetMember1
,Foo::SetMember2
和Foo::SetMember3
下面的代码中。第四个选项是将内存分配留给用户(例如foo.SetMember(new Derived())
),这对于明显的内存安全问题不是很理想。 Foo 应该负责内存管理,而不是用户。
#include <iostream>
template <typename tBase, typename tPointer>
void ClonePointer(tBase*& destination, const tPointer* pointer)
{
destination = static_cast<tBase*>(new tPointer(*pointer));
}
// Base can be a virtual class
class Base
{
public:
virtual void Function()
{
std::cout << "Base::Function()" << std::endl;
}
virtual Base* Clone() const = 0;
};
class Derived : public Base
{
public:
virtual void Function()
{
std::cout << "Derived::Function()" << std::endl;
}
virtual Base* Clone() const
{
return new Derived(*this);
}
};
class Foo
{
public:
Foo() : mMember(NULL) { }
~Foo()
{
if (mMember != NULL)
{
delete mMember;
mMember = NULL;
}
}
template <typename T>
void SetMember1(const T& t)
{
if (mMember != NULL)
{
delete mMember;
mMember = NULL;
}
ClonePointer(mMember, &t);
}
void SetMember2(const Base& b)
{
mMember = b.Clone();
}
template <typename T>
void SetMember3(const T& t)
{
if (mMember != NULL)
{
delete mMember;
mMember = NULL;
}
mMember = new T(t);
}
Base& GetMember()
{
return *mMember;
}
private:
Base* mMember;
};
int main(int argc, char** argv)
{
{
Foo f1;
Foo f2;
Foo f3;
// The input may or may not be a tempoary/RValue reference
f1.SetMember1(Derived());
f2.SetMember2(Derived());
f3.SetMember3(Derived());
f1.GetMember().Function();
f2.GetMember().Function();
f3.GetMember().Function();
}
// Output:
// Derived::Function();
// Derived::Function();
// Derived::Function();
system("pause"); // for quick testing
}
第一种方法(Foo::SetMember1
)的问题在于我有一个随机的,免费的模板函数和一个模板访问器(请参阅下面第三种方法的问题)。
第二种方法(Foo::SetMember2
)的问题是每个派生类都必须实现自己的Clone
函数。这对于类用户来说是太多的样板代码,因为会有很多来自 Base 的类。如果我可以以某种方式自动执行此操作,或者使用实现的模板Clone
函数创建基本可克隆类(没有每个 Base - 派生类必须显式调用它) ,这将是理想的解决方案。
第三种方法(Foo::SetMember3
)的问题在于我需要 Foo 的模板访问器。这可能并不总是可行,尤其是因为非模板类中不允许使用虚拟模板方法( Foo 不能是模板本身),这是可能需要的功能。
我的问题是:
这些是我唯一的选择吗?
我缺少一个更好,更优雅的解决方案吗?
有没有办法创建一个基本可克隆类并从中派生 Base ,并且DerivedType::Clone()
会自动进行克隆?
答案 0 :(得分:3)
这是一个或多或少强大的Clonable
类,可以继承到任何深度。它使用CRTP和Alecsandrescu样式的交织继承模式。
#include <iostream>
// set up a little named template parameters rig
template <class X> struct Parent{};
template <class X> struct Self{};
template<class A, class B> struct ParentChild;
// can use ...< Parent<X>, Self<Y> >...
template<class A, class B> struct ParentChild< Parent<A>, Self<B> >
{
typedef A parent_type;
typedef B child_type;
};
// or ...< Self<Y>, Parent<X> >
template<class A, class B> struct ParentChild< Self<B>, Parent<A> >
{
typedef A parent_type;
typedef B child_type;
};
// nothing, really
struct Nada
{
// except the virtual dtor! Everything clonable will inherit from here.
virtual ~Nada() {}
};
// The Clonable template. Accepts two parameters:
// the child class (as in CRTP), and the parent class (one to inherit from)
// In any order.
template <class A, class B = Parent<Nada> > class Clonable :
public ParentChild<A,B>::parent_type
{
public:
// a nice name to refer to in the child class, instead of Clonable<A,B>
typedef Clonable Parent;
// this is our child class
typedef typename ParentChild<A,B>::child_type child_type;
// This is the clone() function returning the cloned object
// Non-virtual, because the compiler has trouble with covariant return
// type here. We have to implemens something similar, by having non-virtual
// that returns the covariant type calling virtual that returns the
// base type, and some cast.
child_type* clone()
{
return static_cast<child_type*>(private_clone());
}
// forward some constructor, C++11 style
template<typename... Args> Clonable(Args&&... args):
ParentChild<A,B>::parent_type(args...) {}
private:
// this is the main virtual clone function
// allocates the new child_type object and copies itself
// with the copy constructor
virtual Nada* private_clone()
{
// we *know* we're the child_type object
child_type* me = static_cast<child_type*>(this);
return new child_type(*me);
};
};
// Test drive and usage example
class Foo : public Clonable < Self<Foo> >
{
public:
Foo (int) { std::cout << "Foo::Foo(int)\n"; }
Foo (double, char) { std::cout << "Foo::Foo(double, char)\n"; }
Foo (const Foo&) { std::cout << "Foo::Foo(Foo&)\n"; }
};
class Bar : public Clonable < Self<Bar>, Parent<Foo> >
{
public:
// cannot say Bar (int i) : Foo(i), unfortunately, because Foo is not
// our immediate parent
// have to use the Parent alias
Bar (int i) : Parent(i)
{ std::cout << "Bar::Bar(int)\n"; }
Bar (double a, char b) : Parent(a, b)
{ std::cout << "Bar::Bar(double, char)\n"; }
Bar (const Bar& b) : Parent(b)
{ std::cout << "Bar::Bar(Bar&)\n"; }
~Bar() { std::cout << "Bar::~Bar()\n"; }
};
int main ()
{
Foo* foo1 = new Bar (123);
Foo* foo2 = foo1->clone(); // this is really a Bar
delete foo1;
delete foo2;
}
答案 1 :(得分:2)
对于第二种方法,您可以使用CRTP,并且不需要在每个派生类中编写克隆方法:
struct Base {
virtual ~Base() {}
virtual Base *clone() const = 0;
};
template <typename Derived>
struct CloneableBase : public Base {
virtual Base *clone() const {
return new Derived(static_cast<Derived const&>(*this));
}
};
struct Derived: CloneableBase<Derived> {};