这个问题是关于复制和指针多态性的。考虑下面的代码。我们有两个类:Base 和Derived,它们只是常规对象。然后我们得到类 Foo,它有一个指向 Base 的指针作为它的唯一成员。
Foo 的典型用法在 main
中有描述。功能。 Foo::SetMemberX
的输入可能是也可能不是临时对象。
问题是我想要Foo::SetMember
创建传递对象的正确拷贝,并将其地址分配为 Base*
至 Foo::mMember
.
我已经设法想出了 4 种可能的解决方案,但对我来说没有一个看起来很优雅。前三个显示在下面的代码中 Foo::SetMember1
, Foo::SetMember2
, 和 Foo::SetMember3
.第 4 个选项是将内存分配留给用户(例如 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。如果我能以某种方式自动执行此操作,或者使用已实现的模板创建一个 Cloneable 基类(无需每个 Base 派生类都必须显式调用它)Clone
功能,这将是理想的解决方案。
第三种方法 ( Foo::SetMember3
) 的问题是我需要一个用于 Foo 的模板访问器。这可能并不总是可行的,特别是因为在非模板类中不允许虚拟模板方法(Foo 本身不能是模板),这是可能需要的功能。
我的问题是:
我只有这些选择吗?
对于我所缺少的这个问题,是否有更好、更优雅的解决方案?
有没有什么方法可以创建一个Cloneable 基类并从中派生Base,并自动为
DerivedType::Clone()
进行克隆? ?
最佳答案
这里有一个或多或少健壮的 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;
}
关于c++ - 克隆指针 C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11655257/