2012-01-15 74 views
6

我有一個多態類的層次結構,說一個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; 
}; 
+0

我認爲這是因爲unique_ptr 和unique_ptr 之間沒有隱式轉換,但我不確定,因爲我認爲您使用的是我不熟悉的C++ 11。 – 2012-01-15 14:50:49

回答

6

使用原始指針時,編譯器允許協變返回類型,但在使用智能指針時,因爲unique_ptr<Rectangle>不從unique_ptr<Shape>派生是不可能的。這兩個類與編譯器的角度完全不相關。

+0

「_since unique_ptr < Rectangle >不是來自unique_ptr < Shape > _」無關。即使它派生了,在C++協方差中也不允許重寫函數返回從重寫函數的返回類型派生的類。 「_這兩個類與編譯器的角度完全不相關_」不完全是:從「編譯器的角度來看」,'unique_ptr < Rectangle >'可轉換爲'unique_ptr < Shape >'。 – curiousguy 2013-05-28 15:31:08

5

這叫做協方差

在類層次結構中,當基類指定返回一個虛擬方法無論是T*T&,則派生類被允許分別返回U*U&,提供UT(注導出:和顯然是constvolatile的組合)。

這是一個特殊的規則,由編譯器檢查,它的工作原理,因爲如果UT派生那麼U*可以轉換爲一個T*。不幸的是,該規則的侷限性在於它不適用於任何轉換,因此即使您通常可以從unique_ptr<Rectangle>構建unique_ptr<Shape> ...協方差也不起作用。

這就是爲什麼在它的Cloneable概念中,Boost強制要求返回一個光禿的指針類型。這是一種恥辱,但是獲得協變的唯一方法。