2012-08-23 14 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; 
} 

這是我的目標,使ParentNode私下看起來像一個ChildNode,以便它可以在ChildNode的實施ChildNodeInterface的頂部添加一些額外的功能。 ParentNode因此可以有效地被視爲ChildNode-變相處理,由簡單的GetChildHandle指示。 顯然,如果ParentNode不會從NodeInterface繼承,那就沒有問題了。因爲,人們可以輕鬆地消除歧義。這是通過下面的正確的示例示出:

#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。我打算在ParentNode中實現NodeInterface::f的真正獨特實現。 我不知道如何(如果可能)我可以區分ParentNodeInterface::fChildNodeInterface::fParentNode的實施。

回答

1

我認爲你的設計是錯誤的。您繼承了ChildNodeInterface,並且您的ParentNode類中有一個ChildNode成員?

由於ChildNode類不在繼承樹中,因此繼承的ChildNodeInterface不允許使用f函數實現。

C++不允許使用相同的簽名重新定義兩次函數。所以編寫2個相同的虛擬函數的實現是無法完成的。

所以,你有2種選擇:

  1. [最佳]嘗試以正確的方式使用繼承來重新定義你的類的設計。鑽石繼承通常是由於糟糕的設計。
  2. [最快]從ChildNodeInterface刪除繼承和偏好調用mChild-> f()的在ParentNode :: f()的函數來兒童行爲添加到父:

    虛擬INT F(const int的&Ñ )const { return 3 * n + mChild-> f(n); } 但我不知道這是否是你想要的行爲。

+0

你說「C++不允許使用相同的簽名重新定義兩次函數」。我同意,但我不明白爲什麼這些虛擬機具有完全相同的簽名。正如從'ParentNode'看到的,'f'在公共和私有(這是一個區別)以及通過'ChildNodeInterface'或'ParentNodeInterface'(這是另一個區別)。 – Krokeledocus

+0

據我所知,知名度不是簽名的一部分。在ParentNode的觀點中,可見性是相同的。除了菱形繼承之外,由於編譯器無法知道原型的繼承路徑是什麼,所以不能從公共基礎重新定義虛擬函數。你必須做虛擬繼承才能做這種事 –

1

請注意,ChildNodeInterface的確與ChildNode::f()無關。

但爲了解決您的問題(我認爲),而不是私人繼承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」實現文件中,否則其他任何人都不能訪問輔助類。 –