2012-08-23 3 views
2

다음 잘못된 C++ 코드를 고려하십시오.반복적으로 상속 된 기본 클래스에서 재정의 가상을 모호하게하는 방법은 무엇입니까?

#include <assert.h> 

class NodeInterface { 
public: 
    virtual ~NodeInterface() {} 

    virtual int f (const int& n) const = 0; 
}; 

class ChildNodeInterface : public NodeInterface { 
public: 
    virtual ~ChildNodeInterface() {} 
}; 

class ParentNodeInterface : public NodeInterface { 
public: 
    virtual ~ParentNodeInterface() {} 
}; 

class ChildNode : public ChildNodeInterface { 
public: 
    virtual ~ChildNode() {} 

    virtual int f (const int& n) const { 
     return 2*n; 
    } 
}; 

class ParentNode : public ParentNodeInterface, private ChildNodeInterface { 
public: 
    explicit ParentNode() : 
     mChild (new ChildNode()) 
    { 
    } 

    virtual ~ParentNode() {} 

    ChildNodeInterface* GetChildHandle() { 
     return this; 
    } 

    virtual int f (const int& n) const { 
     return 3*n; 
    } 

private: 
    ChildNode* const mChild; 

    // How do I specify that I would like to override ChildNodeInterface::f? 
    virtual int f (const int& n) const { // On MSVC2010: C2535 member function already defined or declared 
     return 1 + mChild->f (n); 
    } 
}; 

int main() 
{ 
    ParentNode parent; 
    assert (parent.f (2) == 6); 
    ChildNode node; 
    assert (node.f (2) == 4); 
    ChildNodeInterface* child (parent.GetChildHandle()); 
    assert (child->f (2) == 5); 
    return 0; 
} 

ChildNodeInterfaceChildNode의 구현의 상단에 몇 가지 추가 기능을 추가 할 수 있도록, ParentNode 비공개 적 ChildNode처럼 보이게하는 나의 목표이다. 따라서 ParentNodeGetChildHandle이라는 단순성으로 표시된 변장의 ChildNode 핸들로 효과적으로 처리 될 수 있습니다. ParentNodeNodeInterface에서 반복적으로 상속되지 않는 경우 분명히 문제가되지 않습니다. 이후, 재정의를 명확하게 해독 할 수 있습니다. 이것은 다음과 같은 올바른 예에 의해 설명된다

#include <assert.h> 

class ChildNodeInterface { 
public: 
    virtual ~ChildNodeInterface() {} 

    virtual int ChildMethod (const int& n) const = 0; 
}; 

class ParentNodeInterface { 
public: 
    virtual ~ParentNodeInterface() {} 

    virtual int ParentMethod (const int& n) const = 0; 
}; 

class ChildNode : public ChildNodeInterface { 
public: 
    virtual ~ChildNode() {} 

    virtual int ChildMethod (const int& n) const { 
     return 2*n; 
    } 
}; 

class ParentNode : public ParentNodeInterface, private ChildNodeInterface { 
public: 
    explicit ParentNode() : 
     mChild (new ChildNode()), 
     mValue (1) 
    { 
    } 

    ChildNodeInterface* GetChildHandle() { 
     return this; 
    } 

    virtual int ParentMethod (const int& n) const { 
     return 3*n; 
    } 

private: 
    ChildNode* const mChild; 
    const int mValue; 

    virtual int ChildMethod (const int& n) const { 
     return mValue + mChild->ChildMethod (n); 
    } 
}; 

int main() 
{ 
    ParentNode parent; 
    assert (parent.ParentMethod (2) == 6); 
    ChildNode node; 
    assert (node.ChildMethod (2) == 4); 
    ChildNodeInterface* child (parent.GetChildHandle()); 
    assert (child->ChildMethod (2) == 5); 
    return 0; 
} 

그러나 ParentNodeInterfaceChildNodeInterface 모두 NodeInterface에서 상속 특별한 경우에, 모호성이 발생한다. main의 어설 션에서 분명히 알 수 있듯이 가상 상속인 NodeInterface을 목표로하지 않습니다. 실제로 NodeInterface::f의 고유 한 구현을 ParentNode에 사용하고자합니다. 가능하면 ParentNodeInterface::fChildNodeInterface::f의 구현을 ParentNode에 어떻게 구별 할 수 있는지 궁금합니다.

답변

1

디자인이 잘못되었다고 생각합니다. ChildNodeInterface를 상속 받았으며 ParentNode 클래스에 ChildNode 멤버가 있습니까?

ChildNode 클래스가 상속 트리에 없으므로 상속 된 ChildNodeInterface에서 f 함수 구현을 사용할 수 없습니다.

동일한 서명으로 함수를 두 번 리디렉션하는 것은 C++에서 허용되지 않습니다. 따라서 동일한 가상 함수의 2 구현을 작성할 수 없습니다.

그래서 당신은 두 가지 옵션이 있습니다 :

  1. [베스트] 올바른 방법으로 상속을 사용하여 클래스 디자인을 재정의하려고합니다. 다이아몬드 상속은 종종 나쁜 디자인 때문입니다.
  2. [최단]는 ChildNodeInterface로부터 상속을 제거하고 부모 자식 동작을 추가 인 parentNode : F() 함수 (F)를 호출 mChild-> 선호 :

    가상 INT의 F (CONST & N INT) const { return 3 * n + mChild-> f (n); } 하지만 내가 원하는 동작인지는 알 수 없습니다. ChildNodeInterface 정말 ChildNode::f()와는 아무 상관이 없다는 것을

+0

"동일한 서명으로 함수를 두 번 재정의하는 것은 C++에서 허용되지 않습니다"라고 말합니다. 나는 동의한다, 그러나 나는 왜이 가상이 정확하게 동일한 서명을 가지고 있는지 이해하지 못한다. 'ParentNode'에서 볼 수 있듯이'f'는 public과 private (차이점)과 ChildNodeInterface 또는 ParentNodeInterface (또 다른 차이점)를 통해 발생합니다. – Krokeledocus

+0

내가 아는 한 가시성은 서명의 일부가 아닙니다. ParentNode 관점에서 가시성은 동일합니다. 다이아몬드 상속과 더불어, 컴파일러는 어느 상속 경로에서 프로토 타입을 얻을 수 있는지 알 수 없기 때문에 일반적인 기반에서 가상 함수를 재정의 할 수 없습니다. 가상 상속을해야만 이런 종류의 일을 할 수 있습니다. –

1

참고.

ChildNodeInterface에서 개인적으로 상속하는 대신 귀하의 문제를 해결하려면 ChildNode에서 상속받은 보조 유형을 사용하고 ChildNode::f()을 해당 유형으로 대체하십시오.ParentNode 그 개체 중 하나에 대한 포인터를 유지 (또는 해당 유형의 일반 세 멤버가), 및 ParentNode::GetChildHandle()가 호출 될 때 그 일에 대한 포인터를 나눠 수 있습니다

// the helper class 
class ParentNode; 
class ParentsChildNodeInterface : public ChildNode { 
public: 
    virtual int f (const int& n) const; 
}; 

class ParentNode : public ParentNodeInterface { 

public: 
    explicit ParentNode() : 
     mChild (new ParentsChildNodeInterface()) 
    { 
    } 

    virtual ~ParentNode() {} 

    ChildNodeInterface* GetChildHandle() { 
     return mChild; 
    } 

    virtual int f (const int& n) const { 
     return 3*n; 
    } 

private: 
    ParentsChildNodeInterface* const mChild; 
}; 

// the new, special f() for children of parents... 
int ParentsChildNodeInterface::f (const int& n) const { 
    return 1 + ChildNode::f (n); 
} 

게시으로, 헬퍼 클래스 아무튼 ParentNode 개체의 모든 상태에 액세스해야합니다. 하지만 도움이 필요하면 도우미 클래스 friendParentNode으로 만들 수 있습니다.

+0

사실, 도우미 클래스가 문제를 해결할 것이지만 상태에 액세스하려면 'ParentNode'와의 우정을 필요로합니다. 이것은 바람직하지 않습니다. 방금이 시련의 목적을 설명하기 위해 실례를 추가했습니다. 'NodeInterface'의 반복 된 상속에 의한 모호함 만이 문제를 일으킨다는 것을 알 수 있습니다. – Krokeledocus

+0

헬퍼 클래스는'ParentNode' 구현 세부 사항입니다. 도우미를 '친구'로 만드는 것은 한 가지 의미로는 바람직하지 않을 수 있지만, 단지 그들 사이에 가까운 이식 관계가 있다는 사실을 반영합니다. 'ParentNode' 안에 도우미 클래스를 선언하여 '구현 세부 사항'을 강화할 수 있습니다. 또는 도우미 클래스를 선언하고 그것을 'friend'로 표시하고 'ParentNode.cpp' 구현 파일 안에 전체 구현 (도우미 클래스의 정의 포함)을 넣습니다. 그 밖의 다른 방법으로는 도우미 클래스에 액세스 할 수 없습니다. –

관련 문제