【问题标题】:virtual constructor idiom with smart pointers带有智能指针的虚拟构造函数习语
【发布时间】:2012-02-10 19:58:22
【问题描述】:

我有一个多态类的层次结构,比如 Shape 抽象基类及其派生类,例如RectangleCircle 等。在Virtual Constructor Idiom 之后,我想知道为什么我们需要派生类中的虚拟构造函数的返回类型在使用智能指针时应该返回与其父类中相同的类型?
例如,参见下面的代码,其中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;
};

【问题讨论】:

  • 我认为这是因为 unique_ptr 和 unique_ptr 之间没有隐式转换,但我不确定,因为我认为您使用的是 C++11,而我不是熟悉。

标签: c++ constructor polymorphism virtual smart-pointers


【解决方案1】:

这称为协方差

在类层次结构中,当基类指定返回T*T&amp; 的虚方法时,则允许派生类分别返回U*U&amp;,前提是@ 987654325@ 派生自 T(注意:显然是 constvolatile 组合)。

这是一个由编译器检查的特殊规则,它之所以有效,是因为如果U 派生自T,那么U* 可以转换为T*。不幸的是,该规则的局限性在于它不适用于任何转换,因此即使您通常可以从 unique_ptr&lt;Rectangle&gt; 构造 unique_ptr&lt;Shape&gt;... 协方差也不起作用。

这就是为什么在它的 Cloneable 概念中,Boost 要求返回一个 bare 指针类型。这是一种耻辱,但却是获得协方差的唯一方法。

【讨论】:

    【解决方案2】:

    当使用原始指针时,编译器允许协变返回类型,但在使用智能指针时这是不可能的,因为unique_ptr&lt; Rectangle &gt; 不是从unique_ptr&lt; Shape &gt; 派生的。从编译器的角度来看,这两个类完全不相关。

    【讨论】:

    • 因为 unique_ptr 不是从 unique_ptr 派生的” 不相关。即使它确实派生了,在 C++ 中协变也不允许重写函数返回从被重写函数的返回类型派生的类。 “从编译器的角度来看,这两个类完全不相关” 不完全是:从“编译器的角度”来看,unique_ptr&lt; Rectangle &gt; 可以转换为 unique_ptr&lt; Shape &gt;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-13
    • 2014-07-06
    • 2017-07-04
    相关资源
    最近更新 更多