皮姆普成语与继承

我想用pimpl成语来继承。

这是基础公共类及其实现类:

class A
{
    public:
      A(){pAImpl = new AImpl;};
      void foo(){pAImpl->foo();};
    private:
      AImpl* pAImpl;  
};
class AImpl
{
    public:
      void foo(){/*do something*/};
};

我希望能够用它的实现类创建派生的公共类:

class B : public A
{
    public:
      void bar(){pAImpl->bar();};    // Can't do! pAimpl is A's private.
};        

class BImpl : public AImpl
{
    public:
      void bar(){/*do something else*/};
};

但是我不能在B中使用pAimpl,因为它是A的私有。

所以我看到了一些解决方法:

  • 在B中创建BImpl * pBImpl成员,并使用附加的A构造函数A(AImpl *)将其传递给A.
  • 将pAImpl更改为受保护(或添加Get函数),并在B中使用它。
  • B不应该继承自A.在B中创建BImpl * pBImpl成员,并在B中创建foo()和bar(),它将使用pBImpl。
  • 任何其他方式?
  • 我应该选择什么?


    class A
    {
        public:
          A(bool DoNew = true){
            if(DoNew)
              pAImpl = new AImpl;
          };
          void foo(){pAImpl->foo();};
        protected:
          void SetpAImpl(AImpl* pImpl) {pAImpl = pImpl;};
        private:
          AImpl* pAImpl;  
    };
    class AImpl
    {
        public:
          void foo(){/*do something*/};
    };
    
    class B : public A
    {
        public:
          B() : A(false){
              pBImpl = new BImpl;
              SetpAImpl(pBImpl);
          };
          void bar(){pBImpl->bar();};    
        private:
          BImpl* pBImpl;  
    };        
    
    class BImpl : public AImpl
    {
        public:
          void bar(){/*do something else*/};
    };
    

    我认为从纯粹的面向对象理论的角度来看,最好的方式是不让BImpl继承AImpl(这是你在选项3中的意思吗?)。 然而,如果pmppl成员变量是const ,那么让BImpl从AImpl派生(并将所需的impl传递给A的构造函数)也可以。 使用get函数或直接访问派生类中的变量并不重要,除非要在派生类上强制执行const正确性。 让派生类改变pimpl并不是一个好主意 - 它们可以破坏A的所有初始化 - 也不会让基类改变它是一个好主意。 考虑你的例子的这个扩展:

    class A
    {
    protected:
       struct AImpl {void foo(); /*...*/};
       A(AImpl * impl): pimpl(impl) {}
       AImpl * GetImpl() { return pimpl; }
       const AImpl * GetImpl() const { return pimpl; }
    private:
       AImpl * pimpl;
    public:
       void foo() {pImpl->foo();}
    
    
       friend void swap(A&, A&);
    };
    
    void swap(A & a1, A & a2)
    {
       using std::swap;
       swap(a1.pimpl, a2.pimpl);
    }
    
    class B: public A
    {
    protected:
       struct BImpl: public AImpl {void bar();};
    public:
       void bar(){static_cast<BImpl *>(GetImpl())->bar();}
       B(): A(new BImpl()) {}
    
    };
    
    class C: public A
    {
    protected:
       struct CImpl: public AImpl {void baz();};
    public:
       void baz(){static_cast<CImpl *>(GetImpl())->baz();}
       C(): A(new CImpl()) {}
    };
    
    int main()
    {
       B b;
       C c;
       swap(b, c); //calls swap(A&, A&)
       //This is now a bad situation - B.pimpl is a CImpl *, and C.pimpl is a BImpl *!
       //Consider:
       b.bar(); 
       //If BImpl and CImpl weren't derived from AImpl, then this wouldn't happen.
       //You could have b's BImpl being out of sync with its AImpl, though.
    }
    

    尽管您可能没有swap()函数,但您可以很容易想到发生类似的问题,特别是如果A是可分配的,无论是意外还是意图。 这是对Liskov替代性原则的微妙违反。 解决方案是:

  • 施工后不要更换小扁豆成员。 声明它们是AImpl * const pimpl 。 然后,派生的构造函数可以传递适当的类型,派生类的其余部分可以自信地下载。 然而,那么你不能做非抛出交换,赋值或写时复制,因为这些技术要求你可以更改pimpl成员。 然而,如果你有一个继承层次结构,你可能并不打算做这些事情。

  • A和B的私有变量分别与AImpl和BImpl类无关(和哑)。 如果B想对A做些什么,那么使用A的公共或受保护的接口。 这也保留了使用pimpl最常见的原因:能够将AImpl的定义隐藏在派生类无法使用的cpp文件中,所以当A的实现更改时,一半的程序不需要重新编译。


  • 正如stefan.ciobaca所说,如果你真的希望A是可扩展的,你会希望pAImpl得到保护。

    但是,你在B中定义的void bar(){pAImpl->bar();}; 似乎很奇怪,因为barBImpl上的一种方法,而不是AImpl

    至少有三个简单的选择可以避免这个问题:

  • 你的选择(3)。
  • (3)中BImpl扩展AImpl (继承foo的现有实现而不是定义另一个)的变体, BImpl定义barB使用它的私有BImpl* pBImpl来访问两者。
  • 代表团,其中B持有指向AImplBImpl私人指针,并将每个foobar转发给适当的实施者。
  • 链接地址: http://www.djcxy.com/p/60251.html

    上一篇: Pimpl idiom with inheritance

    下一篇: Using properties instead of fields in class methods of the same unit is a bad practice?