C++: Duplicating a tree of derived elements

2019-08-08 19:48发布

问题:

I have a base class and several derived classes. The base class looks like this:

class Base
{
    int type; //the derived type the object belongs to
    int nOfChildren;
    Base** children; //each child can be any of the derived types
    ...
}

Now I need to duplicate an instance of Base. Because of the recursion, a virtual method Base::duplicate() is needed. It also seems clear what should go in it:

Base temp = new Base();
temp->type = temp;
temp->nOfChildren = nOfChildren;
temp->children = new Base*[nOfChildren];

beyond that, it's not so clear.

Do I allocate each temp->children[i] as a Base object or as a derived object? Do I need a case statement to cater to all possible derived types? Do I need to implement a duplicate() method for each derived type, even those that contain no other information than the Base class? (If a derived class contains more information, then it is clear that I need a separate mechanism. There are several derived classes that contain no further data than the base, although they contain different implementations of a handler() method not shown.)

回答1:

You are right, a virtual method is needed for cloning the polymorphic object. OTOH, you can leverage C++ features to simplify writing it:

class Child : public ICloneable {
public:
    // stuff...
    Child *clone() const { return new Child(*this); }
}

Also, don't put collections of objects into arrays! Use std::vector instead.

class Base
{
    // stuff...
    std::vector<Base*> children;
}

Even better, use a smart pointer to wrap the cloning operation into an object std::vector will be able to manage transparently.

template<typename T>
struct clone_ptr {
    T *object;

    clone_ptr() : object(new T()) {}
    clone_ptr(T *object_) : object(object_) {}
    clone_ptr(clone_ptr<T> const &other) : object(other.object->clone()) {}
    clone_ptr<T> &operator=(clone_ptr<T> other) {
        std::swap(object, other.object);
        return *this;
    }

    ~clone_ptr() { delete object; }
};

That way you can just use a std::vector of clone_ptrs into your Base:

class Base
{
    // stuff...
    std::vector<clone_ptr<Base>> children;
}

Each object will be automagically copied into an object of the same polymorphic type, as long as you implement clone() in each class. The vector will be cloned in the same way other data members are, automatically by the C++ compiler.