huaxiazhihuo 阅读(91) 评论(0)

私有继承小讨论

大家都知道,大C++里面可以私有继承,之后基类的一切,在子类中就成为private的了,不对外开放了。现在流行接口,组合优化继承,所以private继承这玩意,日渐式微,很久以前就很少使用了,嗯,不要说private,就算是大c++,也是江河日下。不过,存在即合理,c++语法里面的任何东西,都有其价值,平时可以用不到,但是关键时刻用一下,确实很方便,当然多数情况下,也可以其他途径来完成,但是,就是没那么舒服。

废话就不说了,直入正题吧。

假设,现在有接口,假设是IUnknown,里面有那三个著名的纯虚函数,QueryInterface, AddRef, Release,好像是这三个哥俩。

然后,有一个类,就叫ClassA,实现了IUnknown接口,其实就是继承IUnknown,也就是说,重写了那三个纯虚函数。此外,ClassA还有一大堆自己的东西,比如public的字段或者成员函数。

现在,有ClassB,想基于ClassA来做一些事情,但是又不想让用户看到ClassA里面那些乱七八糟的玩意,因此,这种情况下,用private似乎很合适。代码如下:

         struct IUnknown

         {

         public:

                            virtual HRESULT QueryInterface(REFIID riid,void** ppvObject) = 0;

                            virtual ULONG AddRef() = 0;

                            virtual ULONG Release() = 0;

         };

         struct ClassA : IUnknown

         {

                   virtual HRESULT QueryInterface(REFIID riid, void** ppvObject) override { ... }

                   virtual ULONG AddRef() override { ... }

                   virtual ULONG Release() override { ... }

                   ...

         };

         struct ClassB : private ClassA

         {

                   ...

         };

这里,内存的使用上非常紧凑,可以说,没有多余的地方。但是,这里的private,不仅仅会private ClassA的一切,就连IUnknown也被private,这有时候就不符合要求了,因为这里意图是,private ClassA,但是又想public IUnknown,也就是说,对外界来说,ClassB不是ClassA,虽然其内部基于ClassA实现,但是,又希望ClassBIUnknown。对此,有几种解决做法,但是都不能让人满意。

方法1、让ClassB再次实现IUnknown接口,如下所示:

         struct ClassB : private ClassA, public IUnknown

         {

                   virtual HRESULT QueryInterface(REFIID riid, void** ppvObject) override { ... }

                   virtual ULONG AddRef() override { ... }

                   virtual ULONG Release() override { ... }

         };

其好处是,ClassB的实例可以无缝用于IUnknown的一切场合,不管是引用或者指针,constconst。但是,代价也是很大的,首先要针对IUnknown的每个虚函数,都要一一手写,再次转发给private的基类,其次,ClassBClassA多了一个虚函数表指针,大小就比原来多了一个指针的大小,这就不是零惩罚了,这是最不该。

方法2,还是保持私有继承,再在ClassB中添加几个函数,用以返回IUnknown,代码如下

         struct ClassB : private ClassA

         {

                   //也可以using ClassA的三个IUnknown里面的函数

                   const IUnknown* GetUnknown()const { return this; }

                   IUnknown* GetUnknown()const { return this; }

         };

避开了方法1的不足,但是就不能无缝用于IUnknown下,每次使用必须调用一下GetUnknown(),对于引用的情况下,还必须加多一个星号*,也是挺不方便的。对了,这里就算添加了类型函数重载,也即是operator IUnknown,编译器也拒绝将ClassB无缝转换成IUnknown

方法3,用包含,不用私有继承。如下:

         struct ClassB

         {

                   ClassA mA;

                   operator const IUnknown&()const { return *this; }

                   operator IUnknown&() { return *this; }

         };

这样子,ClassB的实例可以无缝用于IUnknown引用下的情况。对于指针的话,可以仿造方法2那样子,写两个函数进行调用。貌似综合起来,方法3的整体分数最高。

就个人而言,更偏向于,直接就让ClassB public继承ClassA好了,少了那么多鬼怪,虽然出现很多不必要的函数,其实也没什么不好。

posted on 2017-12-13 15:17 华夏之火 阅读(184) 评论(1)  编辑 收藏 引用 所属分类: c++技术探讨