2012-09-18 6 views
1

에 결합 과부하 최우선 결과는 다음 코드C++ 이상한 행동

class X{ 
public: 
    virtual void foo(X x){ } 
    virtual void foo(int index){ } 

}; 

class Y : public X{ 
public: 

    void foo(int index){ } 
}; 


int main(){ 
    Y y; 
    y.foo(X()); //Error, see below 
} 

클래스 X을 고려 가상 foo 방법을 과부하가있다. 한 버전은 X이고 다른 버전은 int입니다. 이제 Y 클래스는 X에서 상속 받고 foo(int) 메서드를 재정의합니다. foo(X) 메서드는 재정의 할 수 없으며 그대로 유지해야합니다. main 방법 형 Y의 객체를 생성하고 foo(X) 호출시

그러나, 컴파일러는 다음의 불평 :

In function ‘int main()’: 
error: no matching function for call to ‘Y::foo(X)’ 
note: candidate is: 
note: virtual void Y::foo(int) 
note: no known conversion for argument 1 from ‘X’ to ‘int’ 

따라서 유일한 후보는 재정의 foo(int) 방법이다. 다른 방법이 사라진 것처럼 보인다. 재정의 버전을 제거하면 Ypublic Y : public X{};으로 선언하면 모든 것이 정상적으로 작동합니다. 왜 이런 일이 생길까요?

답변

5

파생 클래스가 과 동일한 이름을 가진 멤버를 기본 클래스의 멤버로 정의하면 파생 클래스 이름이 기본 클래스 이름을 숨 깁니다.

귀하의 경우 함수 Y::fooX::foo을 숨 깁니다. 당신은 Y 등의 범위에 그것을 가지고해야합니다

class Y : public X{ 
public: 

    using X::foo; //it brings X::foo into the scope of Y 

    void foo(int index){ } 
}; 
+0

왜 이가요? 이것은 기본적으로 파생 클래스가 Liskov의 원칙에 위배된다는 것을 의미합니다. 이는 C++에 대한 상당히 나쁜 디자인 결정입니다. – gexicide

+3

@gexicide : 사실 포인터는 LSP를 위반하지 않습니다. * 포인터 나 기본 클래스에 대한 참조를 통해 *에 액세스하면 모든 기본 클래스의 메서드가 여전히 사용 가능합니다. –

+0

@gexicide :이 언어를 결정한 이유는 기본 클래스 함수를 숨기지 않으면 예기치 않은 동작이 발생할 수 있기 때문입니다. 'X' 대신에 다른 기본 클래스의 과부하가'double'을 취했다고 상상해보십시오. 'Y' 클래스의 사용자는'Y :: foo (int)'가 호출되기를 기대하면서'y.foo (2.5)'를 호출 할 수 있습니다; 대신'X :: foo (double)'가 호출 될 것이다; 사용자가 존재할 것으로 기대하지 않는 함수. – Gorpik

1

오버로드는 같은 범위에 정의 된 이름 에 적용됩니다. 따라서 foo 클래스의 Y은 다른 범위로 정의되었으므로 X 클래스의 foo에 과부하가 걸리지 않습니다. 이것은 비공식적으로 "이름 숨기기"로 알려져 있습니다. 기본 클래스 이름을 파생 클래스로 가져 오려면 using 지시문을 추가하십시오.

using X::foo;