带有智能指针的虚拟构造函数习惯用法

virtual constructor idiom with smart pointers

本文关键字:构造函数 习惯 惯用法 虚拟 智能 指针      更新时间:2023-10-16

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

这被称为协方差

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

这是一个由编译器检查的特殊规则,它之所以有效,是因为如果UT派生,则U*可以强制转换为T*。不幸的是,该规则受到限制,因为它不适用于任何转换,因此即使您通常可以从unique_ptr<Rectangle>构造unique_ptr<Shape>。。。协方差不起作用。

这就是为什么在其可克隆概念中,Boost要求返回指针类型。这很遗憾,但这是获得协方差的唯一方法。

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