2010-08-19 8 views
8

'사용'키워드를 사용하는 대신 파생 된 함수를 래핑하여 문제를 해결할 수 있지만 실제로 다음 작업은 수행되지 않습니다. 컴파일러는 'get_elem'이 여전히 'Bar'클래스의 순수 가상이라는 것을 알려줍니다). 당신의 예에서별도의 상속 된 메서드를 '사용'하여 순수 가상 함수를 덮어 씁니다.

class Elem {}; 

class DerivedElem : public Elem {}; 

class Foo { 

    public: 
    virtual Elem& get_elem() = 0; 

}; 

class Goo { 

    protected: 
    DerivedElem elem; 

    public: 
    DerivedElem& get_elem() { return elem; } 

}; 


class Bar : public Foo, public Goo { 

    public: 
    using Goo::get_elem; 

}; 

int main(void) { 

    Bar bar; 

} 

건배,

+4

깔끔하고 작지만 독립적 인 예입니다. 코드 질문을 가진 모든 사람들이 당신이 한 일을 해줬 으면 좋겠어요. – Omnifarious

+0

공변 반환 형식에 대해 Foo에서 파생되지 않아야합니까? – Chubsdad

+0

안녕 얘들 아, 신속하고 도움이 답변을 주셔서 감사합니다. 다형성 할당 Steve에 관한 좋은 팁. 경우에는 누군가가 내 실제 코드 '끈적 거리는'에서, 이러한 구조에서 찾고 이유를 궁금하면 다음 템플릿 클래스에 의해 상속와 '푸'는 다양한 클래스를 액세스하기위한 '인터페이스'입니다 템플릿 클래스입니다. – Tom

답변

2

는 푸와 구대성은 별도의 클래스입니다. Bar에서 Goo의 get_elem 메서드는 서명이 일치하더라도 Foo의 메서드와 전혀 다릅니다.

using Goo::get_elem을 가짐으로써 컴파일러에게 get_elem()에 대한 비정규 화 된 호출을 Goo의 비정규 화 된 호출로 해결하도록 지시합니다.

5

Goo가 인터페이스 Foo를 다른 방식으로 구현하도록 설계된 "믹스 인"인 경우 (다른 구현과 다른 믹스 인이있을 수 있음), Goo는 Foo (Bar가 그렇게하는 대신)에서 파생 될 수 있습니다.

Goo가 인터페이스 Foo를 구현하도록 설계되지 않은 경우 사실상 동일한 서명 기능을 수행 할 때 순수 가상 기능을 구현 한 것처럼 Bar를 처리하는 것은 끔찍한 실수입니다 . 암시 적 인터페이스와 C++에서 "duck"타이핑을 원한다면 할 수 있지만 템플릿으로해야합니다. 옳고 그른가, 순수 가상 함수는 명시 적으로 선언 된 인터페이스 용이고 Goo의 get_elem 함수는 Foo::get_elem을 구현하도록 명시 적으로 선언되지 않았습니다. 그래서 그렇지 않습니다.

그 이유는 원칙적으로 언어가 using Goo::get_elem for Foo;을 정의 할 수 없거나 Bar가 호출을 래핑하는 많은 상용구를 포함 할 필요가 없도록 Bar에 그러한 선언을 정의 할 수 없다고 생각합니다.

당신은 아마 Goo 정말 푸에 대해 알지 못하고, 어느 정도이 기능을 지원 할 수 있도록 템플릿을 사용하여 뭔가를 할 수 있습니다

template <typename T> 
class Goo : public T { 

    protected: 
    DerivedElem elem; 

    public: 
    DerivedElem& get_elem() { return elem; } 
}; 

class Bar : public Goo<Foo> {}; 

class Baz : public Goo<Fuu> {}; 

을 Fuu는 get_elem 기능이 다른 인터페이스입니다. 분명히 Bar의 작성자는 GooFoo 계약을 실제로 구현하고 Baz 계약을 확인하는 것이 Fuu임을 확인해야합니다.

그런데,이 공분산 형태는 조금 사납다. Foo를 보면 누군가가 표현식 bar.get_elem() = Elem()이 유효 할 것으로 기대할 수 있으며 그렇지 않으면 LSP가 위반됩니다. 참고 문헌은 그처럼 재미 있습니다. ((Foo &)bar).get_elem() = Elem()은 유효하지만 일반적으로 작동하지 않습니다! 서브 오브젝트는 Elem에만 할당되며, 그 이유는 ((Foo &)bar).get_elem() = DerivedElem()입니다. 다형성 할당은 기본적으로 불쾌합니다.

1

C++의 많은 이상한 코너 중 하나가 발생했습니다. 이 경우 C++에서는 서로 다른 클래스에서 상속 된 두 개의 가상 함수가 동일한 이름 및 형식 시그니처를 갖고 있더라도 동일한 함수가 아니라고 간주합니다.

C++이 이런 식으로 행동하는 데는 몇 가지 좋은 이유가 있습니다. 예를 들어, 두 함수가 실제로 같은 이름과 형식 시그니처가 있음에도 불구하고 실제로 같지 않은 경우가 종종 있습니다. 두 기능의 의미 적 의미는 다릅니다.여기

은 예입니다

namespace vendor1 { 

class Circle { 
public: 
    virtual ::std::size_t size() const { return sizeof(*this); } 
}; 

} // namespace vendor1 


namespace vendor2 { 

class Shape { 
public: 
    virtual double size() const = 0; 
}; 

class Circle : public Shape { 
public: 
    virtual double size() const { return radius_ * radius_ * M_PI; } 
}; 

} // namespace vendor2 

그리고 당신이 시도 :

namespace my_namespace { 

class Circle : public ::vendor1::Circle, public ::vendor2::Circle { 
// Oops, there is no good definition for size 
}; 

그래서이에 의지해야한다 :

namespace my_namespace { 

class Vendor1Circle : public ::vendor1::Circle { 
public: 
    virtual ::std::size_t data_structure_size() const { return size(); } 
}; 

class Vendor2Circle : public ::vendor2::Circle { 
public: 
    virtual double area() const { return size(); } 
}; 

class Circle : public Vendor1Circle, public Vendor2Circle { 
// Now size is still ambiguous and should stay that way 
// And in my opinion the compiler should issue a warning if you try 
// to redefine it 
}; 

그래서를 C++ 좋은 이유가있다 동일한 형식 시그니처 (반환 유형이 유형 시그니처의 일부가 아님)로 가상 함수를 처리하고 두 가지 차이점의 이름 다른 기능으로 기본 기지.

까지 using까지 ... 모두 using 지시어는 "이 네임 스페이스에서이 네임 스페이스의 이름을 여기에 선언 된 것처럼 추가합니다."라고 말합니다. 가상 함수에 관한 한 이것은 null 개념입니다. 이름을 사용할 때 모호성이 다른 방식으로 해결되어야한다고 제안합니다. 단지 이름을 선언 할뿐 이름을 정의하지는 않습니다. 가상 함수를 재정의하려면 새 정의가 필요합니다.

OTOH, 당신은 다음과 같이 인라인 간단한 썽크 재정에 넣으면 : 좋은 컴파일러는 그것을보고도 함수를 만드는 귀찮게하지 알고, 대신 가상 테이블 항목이 바이올린한다

class Bar : public Foo, public Goo { 

    public: 
    virtual DerivedElem& get_elem() { return Goo::get_elem(); } 
}; 

옳은 일을하는 것. 그것은 실제로 코드를 방출하고 주소를 촬영할 경우에 사용할 수있는 기호가해야 할 수도 있습니다,하지만 여전히 단순히 Foo * 통해 호출 할 때 함수가 완전히 사라질 필요로 가상 테이블을 바이올린 할 수 있어야한다.

관련 문제