파생 클래스에서 재정의 된 기능이 기본 클래스의 다른 과부하를 숨기는 이유는 무엇입니까?

StackOverflow https://stackoverflow.com/questions/1628768

  •  06-07-2019
  •  | 
  •  

문제

코드를 고려하십시오 :

#include <stdio.h>

class Base {
public: 
    virtual void gogo(int a){
        printf(" Base :: gogo (int) \n");
    };

    virtual void gogo(int* a){
        printf(" Base :: gogo (int*) \n");
    };
};

class Derived : public Base{
public:
    virtual void gogo(int* a){
        printf(" Derived :: gogo (int*) \n");
    };
};

int main(){
    Derived obj;
    obj.gogo(7);
}

이 오류가 발생했습니다.

>g++ -pedantic -Os test.cpp -o test
test.cpp: In function `int main()':
test.cpp:31: error: no matching function for call to `Derived::gogo(int)'
test.cpp:21: note: candidates are: virtual void Derived::gogo(int*) 
test.cpp:33:2: warning: no newline at end of file
>Exit code: 1

여기서 파생 클래스의 기능은 기본 클래스에서 동일한 이름 (서명이 아님)의 모든 함수를 일식합니다. 어떻게 든, C ++ 의이 동작은 괜찮아 보이지 않습니다. 다형성이 아닙니다.

도움이 되었습니까?

해결책

질문의 문구로 판단 할 때 ( "숨기기"라는 단어를 사용 했음) 이미 여기에서 무슨 일이 일어나고 있는지 알고 있습니다. 현상을 "이름 숨기기"라고합니다. 어떤 이유로 든 누군가가 이름 숨기기가 발생합니다. 응답하는 사람들은 이것을 "이름 숨기기"라고 불렀고 그것이 어떻게 작동하는지 설명하거나 (이미 알고있을 것입니다), 그것을 무시하는 방법 (당신이 요구 한 적이 없음)을 설명하지만 아무도 다루는 데 관심이없는 것 같습니다. 실제 "왜"질문.

결정, 숨어있는 이름의 이론적 근거, 즉 실제로 C ++로 설계되었으며 상속 된 과부하 기능 세트가 주어진 클래스의 현재 오버로드 세트와 혼합 될 수있는 경우 특정 반 직관적이고 예측할 수 없으며 잠재적으로 위험한 동작을 피하는 것입니다. C ++에서 과부하 해상도는 후보 세트에서 최상의 기능을 선택하여 작동한다는 것을 알고있을 것입니다. 이것은 인수 유형을 매개 변수 유형과 일치시킴으로써 수행됩니다. 일치하는 규칙은 때때로 복잡 할 수 있으며 종종 준비되지 않은 사용자가 비논리적으로 인식 될 수있는 결과로 이어집니다. 이전에 기존의 일련의 기능에 새로운 기능을 추가하면 과부하 해상도 결과가 다소 급격히 이동할 수 있습니다.

예를 들어, 기본 클래스라고 가정 해 봅시다 B 회원 기능이 있습니다 foo 유형의 매개 변수가 필요합니다 void *, 그리고 모든 전화 foo(NULL) 해결되었습니다 B::foo(void *). 이름을 숨기고 없다고 가정 해 봅시다. B::foo(void *) 내려 오는 여러 클래스에서 볼 수 있습니다 B. 그러나 일부 [간접적 인 원격] 자손으로 말씀 드리겠습니다. D 수업의 B 기능 foo(int) 정의됩니다. 이제 이름을 숨기지 않고 D 둘 다 있습니다 foo(void *) 그리고 foo(int) 과부하 해상도에 가시 및 참여. 전화는 어떤 기능을 할 것인가 foo(NULL) 유형의 객체를 통해 만들어지면 해결 D? 그들은 해결할 것입니다 D::foo(int), 부터 int Integral Zero와 더 잘 일치합니다 (즉 NULL) 어떤 포인터 유형보다. 따라서 계층 구조 전체에 걸쳐 foo(NULL) 입력하는 동안 하나의 기능으로 해결하십시오 D (그리고 아래) 그들은 갑자기 다른 사람으로 해결됩니다.

또 다른 예가 제공됩니다 C ++의 설계와 진화, 77 페이지 :

class Base {
    int x;
public:
    virtual void copy(Base* p) { x = p-> x; }
};

class Derived{
    int xx;
public:
    virtual void copy(Derived* p) { xx = p->xx; Base::copy(p); }
};

void f(Base a, Derived b)
{
    a.copy(&b); // ok: copy Base part of b
    b.copy(&a); // error: copy(Base*) is hidden by copy(Derived*)
}

이 규칙이 없으면 B의 상태는 부분적으로 업데이트되어 슬라이스로 이어질 것입니다.

이 행동은 언어가 설계되었을 때 바람직하지 않은 것으로 간주되었습니다. 더 나은 접근 방식으로 "이름 숨기기"사양을 따르기로 결정했습니다. 즉, 각 클래스는 선언하는 각 메소드 이름과 관련하여 "클린 시트"로 시작합니다. 이 동작을 무시하기 위해서는 사용자의 명시 적 조치가 필요합니다. 원래 상속 된 방법 (현재 감가 상각 된)의 재배치, 이제 분해 사용을 명시 적으로 사용합니다.

원래 게시물에서 올바르게 관찰 한 바와 같이 (나는 "다형성이 아님"비고를 언급하고 있습니다),이 행동은 클래스 간의 IS-A Relationsip을 위반하는 것으로 보일 수 있습니다. 이것은 사실이지만 분명히 그 당시에는 끝에서 숨어있는 것이 더 덜 악한 것으로 판명되었습니다.

다른 팁

이름 해상도 규칙에 따르면 이름 조회는 일치하는 이름이 발견되는 첫 번째 범위에서 중지된다고 말합니다. 이 시점에서 과부하 해상도 규칙은 사용 가능한 기능의 최상의 일치를 찾기 위해 시작됩니다.

이 경우 gogo(int*) 파생 클래스 범위에서 (단독으로) 발견되며 int에서 int*로 표준 변환이 없으므로 조회가 실패합니다.

해결책은 파생 클래스에서 사용 선언을 통해 기본 선언을 가져 오는 것입니다.

using Base::gogo;

... 이름 조회 규칙이 모든 후보자를 찾을 수있게하므로 예상대로 과부하 해결이 진행됩니다.

이것은 "디자인"입니다. 이 유형의 메소드에 대한 C ++ 오버로드 해상도는 다음과 같이 작동합니다.

  • 참조 유형에서 시작한 다음 기본 유형으로 이동하면 "Gogo"라는 메소드가있는 첫 번째 유형을 찾으십시오.
  • 해당 유형의 "Gogo"라는 메소드 만 고려하여 일치하는 과부하를 찾습니다.

파생에는 "고고"라는 일치 함수가 없으므로 과부하 해상도가 실패합니다.

이름 숨기기는 이름 해상도의 모호성을 방지하기 때문에 의미가 있습니다.

이 코드를 고려하십시오.

class Base
{
public:
    void func (float x) { ... }
}

class Derived: public Base
{
public:
    void func (double x) { ... }
}

Derived dobj;

만약에 Base::func(float) 숨겨지지 않았습니다 Derived::func(double) 파생에서 우리는 호출 할 때 기본 클래스 기능을 호출합니다. dobj.func(0.f), 플로트는 더블로 홍보 할 수 있지만.

참조: http://bastian.rieck.ru/blog/posts/2016/name_hiding_cxx/

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