문제

재정의 된 C ++ 가상 기능에 대한 권한을 기본 클래스와 다르게 할 이유가 있습니까? 그렇게 할 위험이 있습니까?

예를 들어:

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

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

그만큼 C ++ FAQ 나쁜 생각이라고 말하지만 이유는 말하지 않습니다.

나는 일부 코드 에서이 관용구를 보았고, 저자는 개인 회원 기능을 무시할 수 없다는 가정을 바탕으로 클래스를 최종적으로 만들려고했다고 생각합니다. 하지만, 이 기사 개인 기능을 무시하는 예를 보여줍니다. 물론 C ++ FAQ의 또 다른 부분 그렇게하는 것을 권장합니다.

내 구체적인 질문 :

  1. 파생 클래스 대 기본 클래스에서 가상 방법에 대해 다른 권한을 사용하는 데 기술적 인 문제가 있습니까?

  2. 그렇게 할 정당한 이유가 있습니까?

도움이 되었습니까?

해결책

문제는 기본 클래스 방법이 인터페이스를 선언하는 방법이라는 것입니다. 본질적으로 "이것은 당신 이이 수업의 목표에 할 수있는 일입니다."라고 말합니다.

파생 수업에서 기지가 공공 개인으로 선언 한 무언가를 만들면 무언가를 빼앗아갑니다. 이제 파생 된 객체 "is-a"기본 객체, 기본 클래스 객체에 할 수 있어야 할 일이 있어도 파생 된 클래스 객체에 할 수없고 Liskov 대체 프린 니플

이로 인해 프로그램에서 "기술적 인"문제가 발생합니까? 아마. 그러나 아마도 수업의 대상이 사용자가 행동하기를 기대하는 방식으로 행동하지 않는다는 것을 의미 할 것입니다.

이것이 당신이 원하는 상황에서 자신을 발견한다면 (다른 답변에서 언급 된 더 이상 사용되지 않은 방법의 경우를 제외하고) 상속이 실제로 "IS-a"를 모델링하지 않는 상속 모델이있을 가능성이 있습니다. 예를 들어 Scott Myers의 사각형 사각형은 사각형에서 상속되지만 사각형과 마찬가지로 높이와 무관하게 정사각형의 너비를 변경할 수는 없으며 등급 관계를 재고해야 할 수도 있습니다.

다른 팁

아이가 있다면 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 디자인을 제안 할 것입니다.

기술적 인 문제는 없지만 공개적으로 사용 가능한 기능이 기본 또는 파생 포인터가 있는지 여부에 따라 달라집니다.

내 의견으로는 이상하고 혼란 스러울 것입니다.

개인 상속을 사용하는 경우 매우 유용 할 수 있습니다. 즉, 기본 클래스의 (사용자 정의 된) 기능을 재사용하려고하지만 인터페이스는 아닙니다.

이를 수행 할 수 있으며 가끔 혜택을받을 수 있습니다. 예를 들어, 코드베이스에서 우리는 사용했던 공개 기능이있는 클래스가 포함 된 라이브러리를 사용하고 있지만 이제 다른 잠재적 인 문제로 인해 사용을 방해하지 않습니다 (호출 할 안전한 방법이 있습니다). 또한 많은 코드가 직접 사용하는 해당 클래스에서 파생 된 수업이 있습니다. 그래서 우리는 모든 사람이 도움을 줄 수 있다면 사용하지 않는 것을 기억하도록 도출 된 클래스에서 주어진 기능을 비공개로 만들었습니다. 사용 능력은 제거되지 않지만 코드 리뷰에서 나중에 코드가 컴파일하려고 할 때 일부 용도로 사용됩니다.

  1. 기술적 인 문제가 없습니다.
  2. 기반을 공개적으로 상속하면이 작업을 수행해서는 안됩니다. 보호 또는 개인을 통해 상속하는 경우 기본 포인터가없는 한 의미가없는 메소드 사용을 방지 할 수 있습니다.

개인 상속에 대한 좋은 사용 사례는 청취자/관찰자 이벤트 인터페이스입니다.

개인 개체의 예제 코드 :

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 기본 물체에 캐스트하여 호출 할 수 있지만 더 어렵고 일반적으로 수행해서는 안됩니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top