4

讓我們考慮兩個類AB具有以下接口:用私有基本函數覆蓋公共虛函數?

class A { 
public: 
    virtual void start() {} //default implementation does nothing 
}; 

class B { 
public: 
    void start() {/*do some stuff*/} 
}; 

,然後第三類來自繼承,A公開,因爲它實現了這個「接口」,並B私下,因爲這是實現細節。

但是,在這個具體的實現中,start()只需要包含一個調用B::start()。所以我想我可以使用一個快捷方式,並執行以下操作:

class C: public A, private B { 
public: 
    using B::start; 
}; 

並且完成它,但顯然它不起作用。所以我得到using私人基地功能不工作,以覆蓋虛擬。從那裏,兩個問題:

  • 有什麼辦法使這項工作,因爲我認爲它可能已經奏效?
  • 爲什麼編譯器會接受此代碼有效?正如我所看到的,現在有兩個start()函數,它們在C中具有完全相同的簽名,但編譯器似乎很好,只能調用A::start()

編輯:一些精度:

  • 的目標是通過A指針來操作C對象。
  • 我目前正在使用一個簡單的函數,只是調用B::start(),我特別想知道使用聲明是否確實可以「覆蓋」一個虛擬的,如果不是,如何允許這兩個函數共存。
  • 爲簡單起見,我可能會省略一些諸如virtual繼承的內容。
+1

目前尚不清楚你期待什麼。 'C c; c.start();'應該調用'B :: start()'。 – ZDF

+0

這是在這裏工作:http://ideone.com/e71lnB – Rama

+1

@拉馬我想這是關於A * a =&c; A->開始();爲您的ideone示例 – grek40

回答

5

有沒有什麼辦法讓這項工作,因爲我認爲它可能已經工作?

你應該重寫成員函數和顯式調用B::start()

class C: public A, private B { 
public: 
    void start() override { B::start(); } 
}; 

爲什麼會編譯器接受此代碼,爲有效?正如我所看到的那樣 現在是兩個start()功能與C和 完全相同的簽名,但編譯器似乎很好,只有調用A::start()

你是對的,在C中有兩個成員函數(A::start()B::start())。並且在class C中,如果沒有重寫start()或通過執行using ...::start()使任何基類的start()可見,則在嘗試使用來自C的對象的unqauified namelookup調用成員函數時會出現模糊性錯誤。

class A { 
public: 
    virtual void start() { std::cout << "From A\n"; } 
}; 

class B { 
public: 
    void start() { std::cout << "From B\n"; } 
}; 

class C: public A, private B { 
}; 

int main(){ 
    A* a = new C(); 
    a->start();  //Ok, calls A::start() 

    C* c = new C(); 
    c->start();  //Error, ambiguous   
} 

爲了解決這個問題,你必須使用合格的名稱,如:

C* c = new C(); 
    c->A::start();  //Ok, calls A::start() 

現在,做一個在class Cusing B::start()僅僅聲明start()B::start()每當這樣的名稱是從物體上使用C

class A { 
public: 
    virtual void start() { std::cout << "From A\n"; } 
}; 

class B { 
public: 
    void start() { std::cout << "From B\n"; } 
}; 

class C: public A, private B { 
public: 
    using B::start(); 
}; 

int main(){ 
    A* a = new C(); 
    a->start();  //Ok, calls A::start() 

    C* c = new C(); 
    c->start();  //Ok, calls B::start() 
} 

using B::start使功能void B::start()C中可見,它不覆蓋它。要調用使上述所有不合格的成員函數調用,調用B::start(),你應該重寫成員函數C,並使其撥打B::start()

class A { 
public: 
    virtual void start() { std::cout << "From A\n"; } 
}; 

class B { 
public: 
    void start() { std::cout << "From B\n"; } 
}; 

class C: public A, private B { 
public: 
    void start() override { B::start(); } 
}; 

int main(){ 
    A* a = new C(); 
    a->start();   //Ok, calls C::start() which in turn calls B::start() 
         // ^^^^^^^^^^^^^^^^ - by virtual dispatch 

    C* c = new C(); 
    c->start();   //Ok, calls C::start() which in turn calls B::start() 

} 
+0

是的,我正在訴諸你先提到的解決方案。我更想知道是否有一種方法讓'using'指令「覆蓋」虛函數。 – JBL

+1

@JBL,'using'不影響vtables,它主要是C++中用於處理名稱的編譯時機制,不能用於重寫,另一方面重寫對類vtable中的成員函數指針有影響 – WhiZTiM

+0

是的,我現在明白了。感謝您的解釋! – JBL

0

我想你可能會混淆'公共'和'私人'的含義。在C++繼承上下文中,這僅僅意味着一切都知道'C'是'A',而對於'B'類型只有其他'C'對象可以訪問'C'對象的父方法('B'方法) 。

第二部分是關於繼承模糊性。 沒有using B::start;繼承確實是模棱兩可的,不會編譯(當去c.start()時),所以實際上如果您不想使用合格的路徑,那麼在您的示例中添加該語句不是可選的(使用A或B)。

我不清楚你是否期望c()->start();調用'A'方法或'B'方法,但是使用語句會以你想要的方式確定它。你寫代碼的方式有點不確定 - 'A'真的需要嗎?

+0

事後思考 - 如果你需要所有的物品開始,這看起來像你需要一個家長A和B(鑽石!)的虛擬開始。 – kabanus

+0

我的第一個想法是讓B繼承A或其他東西。然後我想,「添加一個虛擬的繼承結構,以避免單行方法重寫?不,謝謝。」 – grek40

+0

@ grek40我同意,但我們不知道代碼的全部範圍,所以這可能是系統的邏輯。如果是這樣,那麼是的,不用了,謝謝! – kabanus