C ++ 가상 함수를 안전하게 재정의


100

가상 함수가있는 기본 클래스가 있고 파생 클래스에서 해당 함수를 재정의하고 싶습니다. 파생 클래스에서 선언 한 함수가 실제로 기본 클래스의 함수를 재정의하는지 컴파일러에서 확인하는 방법이 있습니까? 이전 함수를 재정의하는 대신 실수로 새 함수를 선언하지 않았는지 확인하는 매크로 또는 무언가를 추가하고 싶습니다.

이 예를 보자 :

class parent {
public:
  virtual void handle_event(int something) const {
    // boring default code
  }
};

class child : public parent {
public:
  virtual void handle_event(int something) {
    // new exciting code
  }
};

int main() {
  parent *p = new child();
  p->handle_event(1);
}

자식의 메서드가 선언을 놓치고 새 메서드를 선언 하기 때문에 parent::handle_event()대신 여기 가 호출됩니다 . 이것은 함수 이름의 오타이거나 매개 변수 유형의 사소한 차이 일 수도 있습니다. 기본 클래스의 인터페이스가 변경되고 일부 파생 클래스가 변경 사항을 반영하도록 업데이트되지 않은 경우에도 쉽게 발생할 수 있습니다.child::handle_event()const

이 문제를 피할 수있는 방법이 있습니까? 어떻게 든 컴파일러 나 다른 도구에 이것을 확인하도록 지시 할 수 있습니까? 유용한 컴파일러 플래그 (g ++ 권장)가 있습니까? 이러한 문제를 어떻게 피할 수 있습니까?


2
좋은 질문입니다. 내 자식 클래스 함수가 ​​왜 지금 한 시간 이후로 호출되지 않는지 알아 내려고 노력했습니다!
Akash Mankar 2014

답변:


89

g ++ 4.7부터는 새로운 C ++ 11 override키워드를 이해합니다 .

class child : public parent {
    public:
      // force handle_event to override a existing function in parent
      // error out if the function with the correct signature does not exist
      void handle_event(int something) override;
};

@hirschhornsalz : handle_event 함수를 구현하고 함수 구현 끝에 override를 추가하면 g ++에서 오류가 발생한다는 것을 발견했습니다. override 키워드 다음에 오는 클래스 선언에서 인라인 함수 구현을 제공하면 모든 것이 정상입니다. 왜?
h9uest

3
@ h9uest override를 정의에 사용해야합니다. 인라인 구현은 정의와 구현 모두이므로 괜찮습니다.
Gunther Piez 2015 년

@hirschhornsalz 네, g ++에서 동일한 오류 메시지를 받았습니다. 참고로 여러분과 g ++ 오류 메시지 모두 "클래스 정의"라는 용어를 사용했습니다. "declaration"({declaration, definition} pair)을 사용해야하지 않습니까? "정의 및 구현"이라고 말하여이 특정 맥락에서 자신을 명확히했지만, 왜 C ++ 커뮤니티가 갑자기 클래스의 용어를 변경하기로 결정했는지 궁금합니다.
h9uest

20

C #의 override키워드 와 같은 것은 C ++의 일부가 아닙니다.

gcc에서 -Woverloaded-virtual이름은 같지만 서명이 충분히 다른 기본 클래스 가상 함수를 숨기지 않도록 경고합니다. 그러나 함수 이름 자체의 철자를 잘못 입력하여 함수를 재정의하는 데 실패하지 않도록 보호하지는 않습니다.


2
Visual C ++를 사용하는 경우입니다
Steve Rowe

4
Visual C ++를 사용하면 C ++ override에서 키워드가 만들어지지 않습니다 . 하지만 잘못된 C ++ 소스 코드를 컴파일 할 수있는 무언가를 사용하고 있다는 의미 일 수 있습니다. ;)
CB Bailey

3
재정의가 유효하지 않은 C ++라는 사실은 Visual C ++가 아니라 표준이 잘못되었음을 의미합니다
Jon

2
@Jon : 좋아, 이제 당신이 운전하는 것을 봅니다. 개인적으로 C # 스타일 override기능 을 사용하거나 떠날 수 있습니다 . 재정의 실패로 인해 문제가 거의 발생하지 않았으며 비교적 쉽게 진단하고 수정할 수있었습니다. 내가 동의하지 않을 것이라고 생각하는 한 가지는 VC ++ 사용자가 사용해야한다는 것입니다. 특정 프로젝트를 이식 할 필요가 없더라도 모든 플랫폼에서 C ++가 C ++처럼 보이기를 원합니다. C ++ 0x에 [[base_check]], [[override]][[hiding]]속성이 있으므로 원하는 경우 검사를 재정의하도록 선택할 수 있습니다.
CB Bailey

5
충분히 재미있게, 주석 후 몇 년 VC ++를 사용은하지 않는 override키워드 가 보인다 짓을 . 글쎄, 적절한 키워드는 아니지만 C ++ 11의 특수 식별자입니다. Microsoft는이의 특별한 경우를 만들기 위해 및 속성에 대한 일반적인 형식을 따르도록 충분히 열심히 밀어 override: 표준으로 만들었다
dribeas - 데이비드 로드리게스

18

내가 아는 한, 추상적으로 만들 수 없나요?

class parent {
public:
  virtual void handle_event(int something) const = 0 {
    // boring default code
  }
};

나는 www.parashift.com에서 실제로 추상적 인 방법을 구현할 수 있다고 읽었다 고 생각했습니다. 개인적으로 이해가되는 것은 하위 클래스가이를 구현하도록 강제하는 것뿐입니다. 아무도 구현 자체를 허용하지 않는다고 말한 사람은 없습니다.


이제야 이것이 단순한 소멸자 이상에서 작동한다는 것을 깨달았습니다! 훌륭한 발견.
strager

1
여기에는 몇 가지 잠재적 인 단점이 있습니다. 1) 하나 이상의 메서드를 추상으로 표시하는 또 다른 점은 기본 클래스를 인스턴스화 할 수 없게 만드는 것입니다. 이는 클래스의 의도 된 사용의 일부가 아닌 경우 문제가 될 수 있습니다. 2) 기본 클래스는 처음부터 수정할 수 없습니다.
Michael Burr

3
나는 Michael Burr에 동의합니다. 기본 클래스 추상을 만드는 것은 문제의 일부가 아닙니다. 파생 클래스가 재정의하기를 원하는 가상 메서드에 기능이있는 기본 클래스를 갖는 것이 합리적입니다. 그리고 다른 프로그래머가 기본 클래스의 함수 이름을 바꾸고 파생 클래스가 더 이상이를 재정의하지 않도록하는 것으로부터 보호하려는 것이 합리적입니다. 이 경우 Microsoft "재정의"확장은 매우 유용합니다. 안타깝게도 표준 없이는이를 수행 할 수있는 좋은 방법이 없기 때문에 표준에 추가되는 것을보고 싶습니다.
Brian

이것은 또한 기본 메소드 (예 BaseClass::method():)가 파생 된 구현 ( DerivedClass::method()예 : 기본값) 에서 호출되는 것을 방지합니다 .
Narcolessico

11

MSVC에서는 CLR override용으로 컴파일하지 않더라도 CLR 키워드를 사용할 수 있습니다 .

g ++에서는 모든 경우에이를 강제하는 직접적인 방법이 없습니다. 다른 사람들은를 사용하여 서명 차이를 포착하는 방법에 대해 좋은 답변을 제공했습니다 -Woverloaded-virtual. 향후 버전에서는 누군가 __attribute__ ((override))C ++ 0x 구문을 사용하여 유사 하거나 동등한 구문을 추가 할 수 있습니다 .


9

MSVC ++에서는 키워드 를 사용할 수 있습니다.override

class child : public parent {
public:
  virtual void handle_event(int something) <b>override</b> {
    // new exciting code
  }
};

override MSVC ++의 네이티브 및 CLR 코드 모두에서 작동합니다.


5

함수를 추상화하여 파생 클래스가 재정의하는 것 외에 다른 선택이 없도록합니다.

@Ray 코드가 유효하지 않습니다.

class parent {
public:
  virtual void handle_event(int something) const = 0 {
    // boring default code
  }
};

추상 함수는 본문을 인라인으로 정의 할 수 없습니다. 수정해야합니다.

class parent {
public:
  virtual void handle_event(int something) const = 0;
};

void parent::handle_event( int something ) { /* do w/e you want here. */ }

3

나는 당신의 논리에 약간의 변화를 제안 할 것입니다. 수행해야 할 작업에 따라 작동하거나 작동하지 않을 수 있습니다.

handle_event ()는 여전히 "지루한 기본 코드"를 수행 할 수 있지만 가상 대신 "새로운 흥미로운 코드"를 수행하려는 지점에서 기본 클래스가 추상 메서드 (예 : 재정의해야 함) 메서드를 호출하도록합니다. 하위 클래스에서 제공합니다.

편집 : 그리고 나중에 일부 하위 클래스가 "새로운 흥미로운 코드"를 제공 할 필요 가 없다고 결정 하면 추상을 가상으로 변경하고 "삽입 된"기능의 빈 기본 클래스 구현을 제공 할 수 있습니다.


2

컴파일러는 기본 클래스 함수가 ​​숨겨지면 생성 할 수 있다는 경고를 표시 할 수 있습니다. 그렇다면 활성화하십시오. 그러면 매개 변수 목록에서 const 충돌과 차이점이 발견됩니다. 불행히도 이것은 철자 오류를 발견하지 못할 것입니다.

예를 들어, 이것은 Microsoft Visual C ++의 경고 C4263입니다.


1

C ++ 11 override키워드를 파생 클래스 내에서 함수 선언과 함께 사용하면 컴파일러가 선언 된 함수가 실제로 일부 기본 클래스 함수를 재정의하는지 확인하도록합니다. 그렇지 않으면 컴파일러에서 오류가 발생합니다.

따라서 override지정자를 사용 하여 동적 다형성 (함수 재정의)을 보장 할 수 있습니다 .

class derived: public base{
public:
  virtual void func_name(int var_name) override {
    // statement
  }
};
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.