C ++의 프라이빗 가상 메서드 프로젝트에서 이것을 발견했습니다. class HTMLDocument :

C ++에서 private 메서드를 가상으로 만드는 이점은 무엇입니까?

오픈 소스 C ++ 프로젝트에서 이것을 발견했습니다.

class HTMLDocument : public Document, public CachedResourceClient {
private:
    virtual bool childAllowed(Node*);
    virtual PassRefPtr<Element> createElement(const AtomicString& tagName, ExceptionCode&);
};



답변

Herb Sutter는 여기에 그것을 아주 잘 설명했습니다 .

지침 # 2 : 가상 기능을 비공개로 설정하는 것을 선호합니다.

이렇게하면 파생 클래스가 함수를 재정 의하여 필요에 따라 동작을 사용자 지정하고 파생 클래스에서 가상 함수를 직접 호출 할 수있게함으로써 (함수가 방금 보호 된 경우 가능했던 것처럼) 직접 노출하지 않아도됩니다. 요점은 사용자 정의가 가능한 가상 기능이 존재한다는 것입니다. 파생 클래스의 코드 내에서 직접 호출 할 필요가없는 경우에는 개인용으로 만 만들 필요가 없습니다.


답변

메서드가 가상 메서드 인 경우 전용 클래스 인 경우에도 파생 클래스에서 재정의 할 수 있습니다. 가상 메서드가 호출되면 재정의 된 버전이 호출됩니다.

(Prasoon Saurav가 그의 답변에서 인용 한 Herb Sutter와는 달리, C ++ FAQ Lite 대부분 사람들을 혼란스럽게하기 때문에 개인용 가상 장치에 대해 권장 합니다.)


답변

가상 멤버를 비공개로 선언하라는 모든 호출에도 불구하고이 주장은 단순히 물을 유지하지 않습니다. 종종 파생 클래스의 가상 함수 재정의는 기본 클래스 버전을 호출해야합니다. 선언되면 할 수 없습니다 private.

class Base
{
 private:

 int m_data;

 virtual void cleanup() { /*do something*/ }

 protected:
 Base(int idata): m_data (idata) {}

 public:

 int data() const { return m_data; }
 void set_data (int ndata) { m_data = ndata; cleanup(); }
};

class Derived: public Base
{
 private:
 void cleanup() override
 {
  // do other stuff
  Base::cleanup(); // nope, can't do it
 }
 public:
 Derived (int idata): base(idata) {}
};

당신은 기본 클래스 메서드를 선언 protected.

그런 다음 메서드를 재정의해야하지만 호출해서는 안된다는 주석을 통해 표시하는 추악한 방법을 취해야합니다.

class Base
{
 ...
 protected:
 // chained virtual function!
 // call in your derived version but nowhere else.
 // Use set_data instead
 virtual void cleanup() { /* do something */ }
 ...

따라서 Herb Sutter의 가이드 라인 # 3 … 그러나 말은 어쨌든 헛간에서 나왔습니다.

무언가를 선언 할 때 protected어떤 파생 클래스의 작성자가 보호 된 내부를 이해하고 적절하게 사용하기 위해 암시 적으로 신뢰하는 것입니다. friend선언이 private멤버에 대한 더 깊은 신뢰를 의미하는 것과 같습니다 .

그러한 신뢰를 위반하여 나쁜 행동을하는 사용자 (예 : 문서를 읽지 않고 ‘단서 없음’으로 표시)는 자신을 비난 할 수 있습니다.

업데이트 : 비공개 가상 기능을 사용하여 가상 기능 구현을 이러한 방식으로 “체인”할 수 있다는 의견이 있습니다. 그렇다면 꼭보고 싶습니다.

필자가 사용하는 C ++ 컴파일러는 파생 클래스 구현이 전용 기본 클래스 구현을 호출하도록 허용하지 않습니다.

C ++위원회가이 특정 액세스를 허용하기 위해 “비공개”를 완화했다면, 저는 모두 비공개 가상 기능을 사용할 것입니다. 말이 도난당한 후에도 헛간 문을 잠 그라는 권고를 받고 있습니다.


답변

Scott Meyers의 ‘Effective C ++’, Item 35 : 가상 기능의 대안 고려 를 읽으면서이 개념을 처음 접했습니다 . 관심을 가질만한 다른 사람들을 위해 Scott Mayers를 참조하고 싶었습니다.

Non-Virtual Interface 관용구를 통한 템플릿 메소드 패턴 의 일부입니다. . 공개 된 메소드는 가상이 아닙니다. 오히려 그들은 비공개 인 가상 메서드 호출을 래핑합니다. 그런 다음 기본 클래스는 비공개 가상 함수 호출 전후에 논리를 실행할 수 있습니다.

public:
  void NonVirtualCalc(...)
  {
    // Setup
    PrivateVirtualCalcCall(...);
    // Clean up
  }

이것은 매우 흥미로운 디자인 패턴이라고 생각하며 추가 된 컨트롤이 어떻게 유용한 지 알 수있을 것입니다.

  • 가상 기능을 만드는 이유 private 입니까? 가장 좋은 이유는 우리가 이미 public대면 방법을 제공했기 때문입니다 .
  • 단순히 만들지 않는 이유 protected다른 흥미로운 일에도이 방법을 사용할 수 있도록 것이 어떻습니까? 나는 그것이 항상 당신의 디자인과 당신이 기본 클래스가 어떻게 맞다고 믿는지에 달려 있다고 생각한다. 나는 파생 된 클래스 제작자가 필요한 논리를 구현하는 데 집중해야한다고 주장한다. 다른 모든 것은 이미 처리되었습니다. 또한 캡슐화 문제가 있습니다.

C ++ 관점에서 볼 때 클래스에서 호출 할 수 없더라도 private 가상 메서드를 재정의하는 것은 완전히 합법적입니다. 이것은 위에서 설명한 디자인을 지원합니다.


답변

필자는 파생 클래스가 최종 사용자에게 그러한 구멍을 노출하지 않고 기본 클래스에 대해 “공백을 채울”수 있도록 허용하는 데 사용합니다. 예를 들어, 전체 상태 시스템의 2/3 만 구현할 수있는 공통 기반에서 파생 된 고도로 상태 저장 개체가 있습니다 (파생 클래스는 템플릿 인수에 따라 나머지 1/3을 제공하며 기본은 템플릿이 될 수 없습니다. 다른 이유들).

많은 공용 API가 올바르게 작동하도록하려면 공통 기본 클래스가 필요하지만 (가변 템플릿을 사용하고 있습니다) 해당 개체를 야생으로 내보낼 수는 없습니다. 더 나쁜 것은 내가 크레이터를 순수한 가상 함수의 형태로 상태 머신에 남겨두면 “Private”가 아닌 모든 곳에있는 경우, 하위 클래스 중 하나에서 파생 된 영리하거나 우둔한 사용자가 사용자가 절대 만져서는 안되는 메서드를 재정의 할 수 있습니다. 그래서 상태 머신 ‘브레인’을 PRIVATE 가상 기능에 넣었습니다. 그런 다음 기본 클래스의 직계 자식이 가상이 아닌 재정의에 대한 공백을 채우고 사용자는 상태 시스템을 망칠 염려없이 결과 개체를 안전하게 사용하거나 추가 파생 클래스를 만들 수 있습니다.

공개 가상 메소드를 가져서는 안된다는 주장에 대해서는 BS라고합니다. 사용자는 공개 가상 가상 시스템처럼 쉽게 개인 가상 가상을 부적절하게 재정의 할 수 있습니다. 결국 새 클래스를 정의하는 것입니다. 대중이 특정 API를 수정해서는 안된다면 공개적으로 액세스 할 수있는 객체에서 가상으로 만들지 마십시오.


답변