covariant return type and boost::shared_ptr

covariant return type and boost::shared_ptr

 

covariant return type翻譯爲協變返回值,意義就是在虛函數的返回值上,可以使用子類對象,而不必是基類對象.下面是一個例子:

struct Object

        {

        public:

                virtual ~Object()

                {}

        };

        struct Cloneable: public Object

        {

        public:

                virtual ~Cloneable()

                {}

                   virtual Object* Clone() const = 0;

        };

        struct Foo : public Cloneable

        {

                virtual Foo* Clone() const //注意這裏

                {

                      return new Foo();

                }

        };

我們看到FooClone函數返回的是Foo對象,而不是Object對象.顯然,C++的這個特點,對於設計繼承類層次來說,在某些情況下使用非常方便,因爲你不必被強迫使用dynamic_cast或者static_cast來進行類型轉換了.

boost::shared_ptrboost智能指針中最爲常用的一個,它是內存資源或者其他資源的正確釋放的有力工具. boost::shared_ptr使用在函數返回值當中,也有很好的作用.例如上面的Clone函數的返回值是一個指針,那麼這個指針的生命週期的維護就是一個問題.一般來說,這需要使用者和實現者之間有一個很好的協議,這個協議的嚴格遵守才能保證避免資源的泄露,這個問題也是C++世界中一個非常棘手的問題.引進boost::shared_ptr,可以在很大的程度上解決這個問題,代碼如下:

struct Object

        {

        public:

                virtual ~Object()

                {}

        };

        typedef boost::shared_ptr<Object> ObjectPtr;

 

        struct Cloneable: public Object

        {

        public:

                virtual ~Cloneable()

                {}

                virtual ObjectPtr Clone() const = 0;

        };

        struct Foo;

        typedef boost::shared_ptr<Foo> FooPtr;

        struct Foo : public Cloneable

        {

                virtual FooPtr Clone() const

                {

                        return  boost::shared_ptr<Foo>(new Foo());

                }

        };

資源的釋放由智能指針本身維護,用戶不必再擔心資源的泄露問題.這是一個非常好的方案.

但是,C++的世界不是如此的完美,上面的代碼是不能編譯成功的,原因是:雖然ObjectFoo之間存在繼承關係,但是ObjectPtrFooPtr之間不存在繼承關係,這意味着協變返回值在這裏不起作用.

爲了解決這個問題,boost的新聞組上http://lists.boost.org/boost-users/2003/02/2996.php 給出瞭如下的方案(注意這裏是針對我們的例子修改以後的):

struct Object

        {

        public:

                virtual ~Object()

                {}

        };

        typedef boost::shared_ptr<Object> ObjectPtr;

 

        struct Cloneable: public Object

        {

        public:

                virtual ~Cloneable()

                {}

                virtual ObjectPtr DoClone() const = 0;

        };

        struct Foo;

        typedef boost::shared_ptr<Foo> FooPtr;

        struct Foo : public Cloneable

        {

                virtual ObjectPtr DoClone() const

                {

                        return  boost::shared_ptr<Foo>(new Foo());

                }

FooPtr Clone() const

                {

                        return  boost::dynamic_pointer_cast<X>(this-> DoClone ());

                }

           };

這個方案在一定程度上解決了問題,但是我個人非常不認同這個方案,主要的理由如下:

1.         Cloneable類型是一個接口類,一般來說,接口的抽象程度比較高,使用範圍也很廣.接口一般是用來抽象概念的,上面改變接口函數的做法,在一定的程度上傷害了這個概念.試想,如果你是Cloneable的最初設計者,你會把接口聲明爲DoClone?這種技術妨礙概念設計的做法,是應該竭力避免的.

2.         即便是從純粹技術的角度說,這種做法也存在問題.上面的例子是兩層繼承設計,當然在實際中也可能是三層或者更多的層次,我們就以三層爲例,爲了使用這種技術,還得設定一個新的名稱,難道是DoDoClone?!顯然,這個的技術會隨着層次的加深而越發的不可接受.所以說,這個技術的擴展性比較差.

其實,我認爲更好的方法在boost中已經使用,這個方法是:

1)       上層的接口設計保持不變,仍然是從概念出發,例如Cloneable的接口函數名稱仍然是Clone,返回值類型是ObjectPtr;

2)       下層的接口直接繼承上層的接口,如果是實現類,那麼並不使用協變返回值的技術,仍然是返回ObjectPtr;

3)       提供一個模板化的全局函數,實現向上轉型.

上面例子的做法是:

struct Object

        {

        public:

                virtual ~Object()

                {}

        };

        typedef boost::shared_ptr<Object> ObjectPtr;

 

        struct Cloneable: public Object

        {

        public:

                virtual ~Cloneable()

                {}

                virtual ObjectPtr Clone() const = 0;

        };

        struct Foo;

        typedef boost::shared_ptr<Foo> FooPtr;

        struct Foo : public Cloneable

        {

                virtual ObjectPtr Clone() const

                {

                        return  ObjectPtr <Foo>(new Foo());

                }

        };

template<class X> shared_ptr<X> clone(X const & x)

{

    shared_ptr<X> px = boost::dynamic_pointer_cast<X>(x.Clone());

    assert(px);

    return px;

}

其實這裏也可以使用boost:: static_pointer_cast.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章