виртуальный конструктор идиома с умными указателями - PullRequest
6 голосов
/ 15 января 2012

У меня есть иерархия полиморфных классов, скажем, Shape абстрактный базовый класс вместе с его производными классами, например, Rectangle, Circle и т. Д. Вслед за идиомой виртуального конструктора мне было интересно, почему нам нужно, чтобы возвращаемые типы функций виртуального конструктора в производных классах возвращали тот же тип, что и в его родительском класс при использовании умных указателей?
Например, см. Код ниже, где функции-члены clone() и create() должны возвращать smart_pointers в класс Shape. Однако при использовании simple pointers типы возвращаемых данных могут быть того же типа, что и тип производных классов.
Кто-нибудь может объяснить, почему мы должны обрабатывать эти функции упомянутым способом?

class Shape;

typedef std::unique_ptr<Shape> shape_ptr;

class Shape{

    public:

        //typedef std::unique_ptr<Shape> shape_ptr;

        Shape(){};
        virtual ~Shape(){};

        virtual void draw() const = 0;
        virtual float area() const = 0;

        virtual shape_ptr clone() const = 0;
        virtual shape_ptr create() const = 0;
        //virtual Shape*clone() const = 0;
        //virtual Shape*create() const = 0;
};

class Rectangle:public Shape{
    public:

        typedef std::unique_ptr<Rectangle> rectangle_SmartPtr;

        Rectangle(int height=0, int width=0):m_Height(height),m_Width(width){};
        Rectangle(const Rectangle & rect):m_Height(rect.m_Height),m_Width(rect.m_Width){};
        ~Rectangle(){};

        virtual void draw() const;
        virtual float area() const;

        //virtual rectangle_SmartPtr clone() const{ return rectangle_SmartPtr(new Rectangle(*this)); };
        // error C2555: 'Rectangle::clone': overriding virtual function return type differs and is not covariant from 'Shape::clone'
        //virtual rectangle_SmartPtr create() const{ return rectangle_SmartPtr(new Rectangle()); };
        // error C2555: 'Rectangle::create': overriding virtual function return type differs and is not covariant from 'Shape::create'

        virtual shape_ptr clone() const{ return shape_ptr(new Rectangle(*this)); }; //OK
        virtual shape_ptr create() const{ return shape_ptr(new Rectangle()); }; //OK

        //virtual Rectangle* clone() const{ return new Rectangle(*this); }; //OK
        //virtual Rectangle* create() const{ return new Rectangle(); }; //OK

    private:
        int m_Height;
        int m_Width;
};


class Circle:public Shape{
    public:

        typedef std::unique_ptr<Circle> circle_SmartPtr;

        Circle(float radius=0):m_Radius(radius){};
        Circle(const Circle & other):m_Radius(other.m_Radius){};
        ~Circle(){std::cout << "Circle destructor: " << this << std::endl; };

        virtual void draw() const;
        virtual float area() const;

        //virtual circle_SmartPtr clone() const{ return circle_SmartPtr(new Circle(*this)); };
        // error C2555: 'Circle::clone': overriding virtual function return type differs and is not covariant from 'Shape::clone'
        //virtual circle_SmartPtr create() const{ return circle_SmartPtr(new Circle()); }; 
        //  error C2555: 'Circle::create': overriding virtual function return type differs and is not covariant from 'Shape::create'

        virtual shape_ptr clone() const{ return shape_ptr(new Circle(*this)); }; //OK
        virtual shape_ptr create() const{ return shape_ptr(new Circle()); }; //OK

        //virtual Circle* clone() const{ return new Circle(*this); }; //OK
        //virtual Circle* create() const{ return new Circle(); }; //OK

    private:

        float m_Radius;
};

Ответы [ 2 ]

6 голосов
/ 15 января 2012

При использовании необработанных указателей компилятор допускает ковариантные возвращаемые типы, но это невозможно при использовании интеллектуальных указателей, поскольку unique_ptr< Rectangle > не является производным от unique_ptr< Shape >. Два класса совершенно не связаны с точки зрения компилятора.

5 голосов
/ 15 января 2012

Это называется ковариация .

В иерархии классов, когда базовый класс указывает виртуальный метод, который возвращает либо T*, либо T&, тогда производные классы могут возвращать U* или U& соответственно, при условии, что U происходит от T (примечание: и, очевидно, комбинации const и volatile).

Это специальное правило, проверенное компилятором, и оно работает, потому что, если U происходит от T, тогда U* может быть приведен к T*. К сожалению, правило ограничено тем, что оно не работает ни для какого преобразования, и, следовательно, даже если вы обычно можете построить unique_ptr<Shape> из unique_ptr<Rectangle> ... ковариация не работает.

Вот почему в своей концепции Cloneable Boost предписывает возвращать bare тип указателя. Обидно, но единственный способ получить ковариацию.

...