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