c++ - 克隆指针 C++

标签 c++ pointers polymorphism clone

这个问题是关于复制和指针多态性的。考虑下面的代码。我们有两个类:BaseDerived,它们只是常规对象。然后我们得到类 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 本身不能是模板),这是可能需要的功能。

我的问题是:

  1. 我只有这些选择吗?

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

  3. 有没有什么方法可以创建一个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/

相关文章:

c++ - std::vector<Object> 成员 C++ 的总和

C++ 指针和数据位置 : Data Always Being Placed at Same Memory Location

c - 取消引用双指针时出现问题

c - 删除链表中间的节点

c++ - 使用 boost::gil 从内存中读取 JPEG 图像

c++ - 位于函数中的外部变量?

c++ - 如何在不导致段错误的情况下测试 "dynamic_cast"的结果

C++:多态成员可以保持不变吗?

C++ - 不需要使用基本方法

c++ - 为什么 WSARecvMsg 函数实现为一个函数指针,这个指针是否可以重用?