Clonable类层次结构和unique_ptr

时间:2016-06-13 11:10:30

标签: c++ inheritance clone abstract-class unique-ptr

我遇到了克隆抽象类和唯一指针的问题。假设我有以下可克隆的抽象基类

class Base
{ 
    public:
        virtual void doSomething()=0;
        virtual std::unique_ptr<Base> clone() const=0;
}

和派生的抽象类,它提供了一个额外的方法

class Derived : public Base
{ 
    public:
        virtual void doSomething()=0;
        virtual void doSomethingMore()=0;
        virtual std::unique_ptr<Base> clone() const=0;
}

这允许定义通过组合存储Base层次结构的多态对象的新类。例如

class Composed
{ 
    public:
        Composed(Base const &base_) : basePtr(base_.clone()) {}

    private:
        std::unique_ptr<Base> basePtr;
}

通过这种方式,我应该能够在Composed中存储Derived类型的对象,而不会切换Derived添加wrt Base的方法。但是,我想定义另一个存储从Derived继承的多态对象的对象,将其视为Derived类型的对象。使用与上面相同的结构

class ComposedDerived
{ 
    public:
        ComposedDerived(Derived const &derived_) : derivedPtr(derived_.clone()) {}

    private:
        std::unique_ptr<Derived> derivedPtr;
}

显然,我收到编译错误,因为Derived的克隆方法返回std::unique_ptr<Base>。另一方面,如果我更改Derived的定义如下

class Derived : public Base
{ 
    public:
        virtual void doSomething()=0;
        virtual void doSomethingMore()=0;
        virtual std::unique_ptr<Derived> clone() const=0;
}

在这种情况下,编译器会出现以下错误:invalid covariant return type for ‘virtual std::unique_ptr<Derived> Derived::clone() const

编译器是否有办法理解std::unique_ptr<Derived>实际上可以通过多态性用作std::unique_ptr<Base>而不是争论派生类clone方法的返回类型?

2 个答案:

答案 0 :(得分:2)

clone()方法使用NVI(非虚拟接口惯用法),如下所示:

class Base
{ 
    public:
        virtual void doSomething()=0;

        std::unique_ptr<Base> clone() const {
            return cloneImpl();
        }

    private:
        virtual std::unique_ptr<Base> cloneImpl() const=0;
};

class Derived : public Base
{ 
    public:
        virtual void doSomething()=0;
        virtual void doSomethingMore()=0;

        std::unique_ptr<Derived> clone() const {
            return std::unique_ptr<Derived>(static_cast<Derived*>(cloneImpl().release()));
        }
};

您甚至可以在子类中添加“覆盖”clone()方法的更多安全性和便利性,如下所示:

class Base
{ 
    public:
        virtual void doSomething()=0;

        std::unique_ptr<Base> clone() const {
            return checkedClone<Base>();
        }

    protected:
        template<class T>
        std::unique_ptr<T> checkedClone() const {
            auto p = cloneImpl();
            assert(typeid(*p) == typeid(*this) && "subclass doesn't properly override cloneImpl()");
            assert(nullptr != dynamic_cast<T*>(p.get()));
            return std::unique_ptr<T>(static_cast<T*>(p.release()));
        }

    private:
        virtual std::unique_ptr<Base> cloneImpl() const=0;
};

class Derived : public Base
{ 
    public:
        virtual void doSomething()=0;
        virtual void doSomethingMore()=0;

        std::unique_ptr<Derived> clone() const {
            return checkedClone<Derived>();
        }
};

答案 1 :(得分:1)

如何利用协变回报:

class Base { 
public:
    std::unique_ptr<Base> clone() const {
        return std::unique_ptr<Base>(cloneImpl());
    }
    virtual ~Base();
private:
    virtual Base* cloneImpl() const;
};

class Derived : public Base { 
public:
    std::unique_ptr<Derived> clone() const {
        return std::unique_ptr<Derived>(cloneImpl());
    }
    ~Derived() override;
private:
    // Covariant return:
    Derived* cloneImpl() const override;
};