1

这个问题是关于复制和指针多态性的。考虑下面的代码。我们有两个类:BaseDerived,它们只是常规对象。然后我们有类Foo,它有一个指向Base作为其唯一成员的指针。

函数中描述了Foo的典型用法。main输入Foo::SetMemberX可能是也可能不是临时对象。

问题是我想Foo::SetMember创建传递对象的正确副本,并将其地址分配为 a Base*to Foo::mMember

我设法提出了 4 种可能的解决方案,但对我来说,这些解决方案都不是很优雅。前三个在下面的代码中显示在Foo::SetMember1Foo::SetMember2Foo::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。如果我可以以某种方式自动执行此操作,或者使用已实现的模板函数创建一个基本Cloneable类(每个Base派生类都不必显式调用它)Clone,这将是理想的解决方案。

第三种方法 ( Foo::SetMember3) 的问题是我需要Foo的模板访问器。这可能并不总是可行的,特别是因为在非模板类中不允许使用虚拟模板方法(Foo本身不能是模板),这是可能需要的功能。

我的问题是:

  1. 这些是我唯一的选择吗?

  2. 对于我缺少的这个问题,是否有更好、更优雅的解决方案?

  3. 有没有办法创建一个基本的Cloneable类并从中派生Base,并自动进行克隆DerivedType::Clone()

4

2 回答 2

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;
}
于 2012-07-25T20:49:06.650 回答
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> {};
于 2012-07-25T19:24:53.130 回答