2016-06-19 2 views
5

다중 상속에서 기대하지 않았던 결과, virtual 메소드 및 기본 클래스에 대한 포인터가 있습니다. d.getStr()다중 상속, 가상 메소드 충돌 및 기본 클래스의 포인터


, dderived 예를 때 내가 예상대로 base_2 버전이 호출됩니다. 내가 예상대로 pderived 예 (또는 derived 인스턴스를 가리키는 base_2에 대한 포인터)에 대한 포인터가 p->getStr()

base_2 버전이 호출됩니다.

그러나 pderived 인스턴스를 가리키는 base_1에 대한 포인터가 p->getStr()

base_1 버전이 호출되고 I는 base_2 버전이라고 할 것이다 확신했다 (덕분 usinggetStr()virtual 방법이 있다는 사실) .

다음의 간단한 예입니다 :

#include <iostream> 

struct base_1 
{ 
    virtual std::string getStr() const 
    { return "string from base 1"; } 
}; 

struct base_2 
{ 
    virtual std::string getStr() const 
    { return "string from base 2"; } 
}; 

struct derived : public base_1, public base_2 
{ 
    using base_2::getStr; 
}; 


int main() 
{ 
    derived d; 

    derived * dp = &d; 
    base_1 * bp1 = &d; 
    base_2 * bp2 = &d; 

    std::cout << "from derived:   " << d.getStr() << std::endl; 
    std::cout << "from derived pointer: " << dp->getStr() << std::endl; 
    std::cout << "from base_1 pointer: " << bp1->getStr() << std::endl; 
    std::cout << "from base_2 pointer: " << bp2->getStr() << std::endl; 
} 

출력 인

내가 base_2 버전의 전화를 부과하는 것을 알고
from derived:   string from base 2 
from derived pointer: string from base 2 
from base_1 pointer: string from base 1 
from base_2 pointer: string from base 2 

, 내가 derived 다음과 같은 방법으로 추가 할 수 있습니다 다음

std::string getStr() const 
{ return base_2::getStr(); } 

제 질문은 :

1) base_1 (파생 된 인스턴스를 가리키는)에 대한 포인터가 using 지시어를 무시하고 base_1 버전을 getStr()이라고 부르는 이유는 무엇입니까?

2) getStr()base_2 버전을 부과 할 수있는 방법이 있나요, derived 인스턴스가 getStr()을 재정의하지 않고 base_1 포인터로 사용됩니까?

--- 편집 --- 답변에 대한

감사합니다.

나는 당신이 무슨 일이 일어나고 있는지 설명하고 있지만 나의 의심은 이해합니다 : 언어 (표준)가이면을 묘사합니까? 아니면 정의되지 않은 부분입니까?

는 말 : 나는 using 지시어를 제거하면 컴파일러가 선택하는 getStr()의 버전을 알 수 없어 나는 d.getStr()에서와 dp->getStr()에서 컴파일 오류 (error: request for member getStr is ambiguous)를 얻는다.

그러나 getStr()virtual입니다. 그래서 (나는 확신했다.) 기본 포인터는 파생 된 버전을 사용해야한다. 그러나 우리는 몇 가지 충돌하는 방법을 가지고 있습니다.

base_1 (또는 base_2)은 충돌 메서드의 두 버전 중 하나를 선택하는 권한이 부여 된 (또는 의무가있는) 포인터입니까?

아마도 내가 틀렸지 만, 이런 식으로 virtual 메서드는 virtual 메서드로 관리됩니다.

+1

'using '은 가시성을 돕습니다. 그것은 파생 된'overload'를 기본 가상 함수로 보이게 만들지 않습니다. – Arunmu

답변

0

1) 귀하의 코드가 정확히 수행하고있는 것처럼 보입니다. base_1을 가리키면 base_1 (또는 기본 클래스)에서 함수를 얻을 수 있습니다. base_1과 base_2로 구성되는 개체는 그 시점에서 알 수 없습니다. 왜냐하면 사용자가 파생 클래스가 아니라 기본 클래스를 가리키고 있기 때문입니다.

2) 아니오, 간단히 불가능합니다. 실제로 derived에서 getStr()을 오버로드해야합니다.

struct derived : public base_1, public base_2 
{ 
    using base_2::getStr; 
}; 

이 동일 함 :

+0

anser에게 감사하지만, (1)에 관해서는 일반적인 방법으로 기본/파생 클래스의 동작을 설명합니다. 'getStr()'은'virtual' 메소드입니다. – max66

+0

그건 중요하지 않습니다; 사실 가상 함수는 나란히있는 클래스에서 "점프"할 수 있으며, 클래스 관계 다이어그램을 그릴 경우 수직 만 가능합니다. 답변을 주셔서 감사합니다; – JvO

4

당신은 다음과 같은 방식으로 using 키워드를 사용하는 경우 것으로 기대하고이 경우

struct derived : public base_1, public base_2 
{ 
    void getStr() 
    { 
     base_2::getStr(); 
    } 
}; 

, 당신이 기대하는 행동 - - p가 base_1에 대한 포인터 인 경우 p->getStr()을 호출하면 실제로는 base_2::getStr()이 호출됩니다. derived 무시 base_1getStr(), 그래서 호출 base_1getStr(), 일반 포인터를 통해, derivedgetStr의 결과는() base_2getStr() 메소드를 호출하는 호출하기.

그러나 이것은 발생하지 않습니다. using 키워드는이 방식으로 메소드 호출을 전달하는 별칭이 아닙니다. using 키워드는 derived 클래스에 메소드를 만들지 않으므로 클래스 상속에는 영향을 미치지 않으며 derived_1getStr()은 하위 클래스에서 재정의되지 않습니다. 그리고 그 이유는 derived_1getStr() 호출이 derived_2getStr() 호출을 유발하지 않기 때문입니다. 파생 된 클래스는 getStr(), 각 기본 클래스에 대한 하나 2 개의 vtable 항목을 가지고있다, 그래서 제대로 base_1::getStr()base_2::getStr() 모두를 해결할 수 있기 때문에 발생

2

. using 지시어는 derived::getStr() vtable 항목을 만들지 않거나 기본 클래스 항목을 바꾸지 않고 사용할 기본 항목을 선택하기 만합니다. base_1에 대한 포인터를 통과 할 때 컴파일러는 derivedbase_1에서 가상 함수에 대한 vtable 항목을 "인식"하므로 getStr()base_1::getStr()으로 확인합니다. derived에 명시적인 getStr()을 추가하는 해결책은 명확성을 위해 기본 클래스와 일치하도록 가상으로 만드는 것이 바람직 할 수 있지만, 아마도 가장 깨끗한 해결책 일 것입니다.

+0

; 내 의심 : 파생 클래스의 2 vtable은 C++ 표준에 의해 부과되거나 구현 세부 사항입니까?다른 말로하면 : 이것은 표준에 의해 부과되었거나 다른 컴파일러가 다른 행동을 생성하도록 허가 되었기 때문에 발생합니다. – max66

+0

메 커닉은 구현 세부 사항이지만 기본 클래스 자체가 가상 메서드가있는 파생 클래스 일 수 있기 때문에 최종 결과는 정확한 메커니즘에 관계없이 거의 동일하게 작동해야합니다. 유일한 대안은 컴파일러가 모든 코드를보고 가리키는 객체의 실제 유형이 무엇인지 파악할 수 있다는 것입니다. 이는 매우 사소한 것입니다. –