假设如下:
class A{ virtual void f() = 0; };
class B{ virtual void f() = 0; };
我可以以某种方式执行以下操作吗?
class C : public A, public B
{
virtual void A::f(){ printf("f() from A"); }
virtual void B::f(){ printf("f() from B"); }
};
所以现在我可以做了???
A* pa = new C();
pa->f(); // prints f() from A;
B* pb = (B*)pa;
pb->f(); // prints f() from B;
感谢!!!
答案 0 :(得分:5)
第一个解决方案
这个问题提醒了'立面'的设计模式。 这应该重写为:
class AC : public A
{ public: virtual void f(){ cout << "f() from A" << endl;}; };
class BC : public B ...
class C : public AC, public BC {};
其中C是'立面'。
所以在正确的调用语法中应该是这样的:
C* c = new C();
c->AC::f();
c->BC::f();
如果AC和AC之间没有任何共享限制不管怎么说,这应该可以完成工作,因为它没有被玷污。
第二个解决方案
另一个解决方案,感谢Casey (参见第一条评论),是在模板中使用C类的前向声明,以允许调用方法来定义后者。
template <typename C>
class AC : public A {
public:
void f() { static_cast<C&>(*this).f_from_A(); }
};
template <typename C>
class BC : public B { ... };
所以实现部分可以在同一个类中完成。
class C : public AC<C>, public BC<C> {
public:
void f_from_A() { cout << "f_from_A" << endl; };
void f_from_B() ...
};
调用部分更干净,因为它没有显示任何实现细节和它最接近问题:
C* c = new C();
((A*) c) -> f();
((B*) c) -> f();
C上没有'default'f(),可以打破继承的预期行为,并且更难阅读。
答案 1 :(得分:0)
供参考,以下链接似乎相关:
第二个链接中的OP问他是否可以像你问的那样做:
class Impl : public A , public B
{
public:
void A::Function () { cout << "A::Function" << endl; }
void B::Function () { cout << "B::Function" << endl; }
};
Johannes的回答是:
您不能在那里使用限定名称。我写的void Function(){ ...}你正在重写这两个功能。 Herb Sutter展示了它是如何做到的 得到解决。
另一种选择是重命名这些功能,因为显然它们 做一些不同的事情(否则我不会看到问题 用相同的行为覆盖两者。)
因此,一个可能的解决方案(来自第一个链接的James Kanze)看起来像这样:
class RemapA : public A
{
virtual void fnInA() = 0;
public:
virtual void fn()
{
fnInA();
}
};
class RemapB : public B
{
virtual int fnInB() = 0;
public:
virtual int fn()
{
return fnInB();
}
};
class C : public RemapA, public RemapB
{
virtual void fnInA() { /* ... */ }
virtual void fnInB() { /* ... */ }
// ...
};
Herb Sutter的链接基本上也说了同样的话。
答案 2 :(得分:0)
是。每个基类都有自己的虚拟表 因此,通过强制转换,编译器将检查您要转换为的类型的虚拟表,并获取指向匹配函数的指针。
class A {public: virtual void f() = 0; };
class B {public: virtual void f() = 0; };
class C : public A, public B
{
public:
virtual void A::f(){ printf("f() from A"); }
virtual void B::f(){ printf("f() from B"); }
};
海湾合作委员会不支持这一点。只有Visual Studio可以。
但是你必须总是使用强制转换或范围函数调用 这将返回一个模糊的调用错误:
C* c = new C();
c->f(); //ambiguous call compilation error
This will work:
((B*)c)->f();
编译器显示如下结构:( Visual studio flag: /d1reportSingleClassLayout C )
class C size(8):
+---
| +--- (base class A)
0 | | {vfptr}
| +---
| +--- (base class B)
4 | | {vfptr}
| +---
+---
C::$vftable@A@:
| &C_meta
| 0
0 | &C::f
C::$vftable@B@:
| -4
0 | &CCCC::f
C::f this adjustor: 4
C::f this adjustor: 0
基本上你可以看到匹配虚拟表的2个独立指针 所以你可以这样做:
C* c= new C();
B* b = (B*)c;
b->f(); // calls C::B::f()
A* a = (A*)c;
a->f(); // calls C::A::f()
or even:
a = (A*)(C*)b;
a->f(); // calls C::A::f()
请注意,您需要将b
大小写回派生类以访问A
类型基类的虚用表。
答案 3 :(得分:0)
要覆盖这两个函数,您需要中间类。 C中的using声明选择A:
的功能#include <iostream>
struct A{ virtual void f() = 0; };
struct B{ virtual void f() = 0; };
struct IntermediateA : public A {
virtual void f() override { std::cout << "A\n"; }
};
struct IntermediateB : public B {
virtual void f() override { std::cout << "B\n"; }
};
struct C : public IntermediateA, public IntermediateB
{
using IntermediateA::f;
};
int main() {
C c;
B* b = &c;
c.f();
b->f();
return 0;
}