簡體   English   中英

使用C ++中的私有函數覆蓋公共虛函數

[英]Overriding public virtual functions with private functions in C++

是否有任何理由使重寫的C ++虛函數的權限與基類不同? 這樣做有危險嗎?

例如:

class base {
    public:
        virtual int foo(double) = 0;
}

class child : public base {
    private:
        virtual int foo(double);
}

C ++ faq說這是一個壞主意,但沒有說明原因。

我已經在一些代碼中看到了這個習慣用法,我相信作者試圖讓這個類最終,基於一個假設,即不可能覆蓋私有成員函數。 但是, 本文顯示了重寫私有函數的示例。 當然,C ++ faq的另一部分建議不要這樣做。

我的具體問題:

  1. 在派生類和基類中使用不同的虛擬方法權限是否存在任何技術問題?

  2. 有沒有合理的理由這樣做?

你確實得到了令人驚訝的結果,如果你有一個孩子,你不能打電話給foo,但你可以把它扔到基地,然后打電話給foo。

child *c = new child();
c->foo; // compile error (can't access private member)
static_cast<base *>(c)->foo(); // this is fine, but still calls the implementation in child

我想你可以設想一個你不想暴露函數的例子,除非你把它當作基類的一個實例。 但是,這種情況突然出現的事實表明,在可能被重構的線路上的某個地方有一個糟糕的OO設計。

問題是Base類方法是聲明其接口的方式。 從本質上講,它是“你可以對這類對象做的事情。”

在Derived類中,你創建了Base已聲明為public private的東西,你正在拿走一些東西。 現在,即使Derived對象是“is-a”Base對象,你應該能夠對Base類對象做一些你無法對Derived類對象做的事情,打破了Liskov Substitution Prinicple

這會在你的程序中引起“技術”問題嗎? 也許不吧。 但它可能意味着您的類的對象不會以用戶期望它們的行為方式運行。

如果你發現自己處於你想要的狀態(除了在另一個答案中提到的棄用方法),你可能有一個繼承模型,其中繼承不是真正建模“is-a”(例如,斯科特邁爾斯的例子Square繼承自Rectangle,但是你無法改變Square的寬度而不像它的高度那樣,你可能需要重新考慮你的階級關系。

沒有技術問題,但最終會出現公共可用功能取決於您是否具有基指針或派生指針的情況。

在我看來,這將是奇怪和混亂。

如果您使用私有繼承,那么它非常有用 - 即您希望重用基類的(自定義)功能,而不是接口。

它可以做到,偶爾也會帶來好處。 例如,在我們的代碼庫中,我們使用的庫包含一個我們以前使用的具有公共函數的類,但現在不鼓勵使用其他潛在的問題(有更安全的方法可以調用)。 我們碰巧有一個派生自該類的類,我們的許多代碼直接使用它。 因此,我們在派生類中將給定函數設為私有,以幫助每個人記住如果他們可以幫助它就不要使用它。 它並沒有消除使用它的能力,但它會在代碼嘗試編譯時捕獲一些用途,而不是在代碼審查的后期。

私有繼承的一個很好的用例是Listener / Observer事件接口。

私有對象的示例代碼:

class AnimatableListener {
  public:
    virtual void Animate(float delta_time);
};

class BouncyBall : public AnimatableListener {
  public:
    void TossUp() {}
  private:
    void Animate(float delta_time) override { }
};

該對象的某些用戶想要父功能,而有些用戶想要子功能:

class AnimationList {
   public:
     void AnimateAll() {
       for (auto & animatable : animatables) {
         // Uses the parent functionality.
         animatable->Animate();
       }
     }
   private:
     vector<AnimatableListener*> animatables;
};

class Seal {
  public:
    void Dance() {
      // Uses unique functionality.
      ball->TossUp();
    }
  private:
    BouncyBall* ball;
};

這樣, AnimationList可以保存對父項的引用並使用父功能。 雖然Seal持有對孩子的引用並使用獨特的子功能並忽略父母的功能。 在此示例中, Seal不應調用Animate 現在,如上所述,可以通過強制轉換到基礎對象來調用Animate ,但這樣做更困難,通常不應該這樣做。

  1. 沒有技術問題,如果你的意思是技術,因為存在隱藏的運行時成本。
  2. 如果您公開繼承基礎,則不應該這樣做。 如果你通過protected或private繼承,那么這可以幫助防止使用沒有意義的方法,除非你有一個基指針。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM