分解类子集的纯虚函数的实现
Factorizing implementations of pure, virtual functions for a subset of classes
>假设我有这样的类层次结构:
class A {
public:
virtual void foo(Base *b) = 0;
};
class B {
public:
virtual void bar() = 0;
};
class C : public A, public B {
public:
virtual void viz() = 0;
};
class E : public C {
public:
/** Some non-pure virtual functions, does not implement foo */
};
class F : public E {
/** does not implement foo */
}
class G : public E {
/** does not implement foo */
}
现在,我想定义 F 和 G 的派生、"模板化"版本,它声明了一个方法foo(T *s)
,其中 foo(Base *s)
的实现只是通过应用static_cast调用 foo(T *s(。
此外,我不想向 A 公开模板参数,因为这些接口(A、B、C、E(的用户不应该暴露给模板参数(我正在编写一个插件架构,派生给定接口的用户类型从插件来回传递到基本系统(。
我想定义这个类:
template <typename T>
class GA : public A{
...
}
并像这样定义 F 和 G 的"模板化"推导:
template <typename T>
class GF : public F, public GA {
...
}
问题是 GF 看到了两个不同的 foo(( 声明(一个由 A 给出,另一个由 GA 给出(,所以当我尝试实例化 GF 时,编译器会给我一个错误,因为 foo(Base( 没有定义。
我该如何解决这个问题?
为什么需要多重继承? 为什么不干脆class C : public Impl { ... }
?
我认为问题是Impl
继承了类A
,而类C
继承了Impl
和A
。这意味着将有两个类A
实例,其中第一个 A 的foo
由 Impl
实现,第二个 A 仍然是纯虚拟的。虚拟继承可以解决这个问题,例如:
class A {
public:
virtual void foo () = 0;
};
class Impl : virtual public A {
public:
virtual void foo () {}
};
class C
: virtual public A
, public Impl
{
};
我会建议一些不同的东西。将该函数声明为纯虚函数,然后通过转发调用在派生类中轻松实现它:
class A {
public:
virtual void foo() = 0;
};
void A::foo() {}
class B : public A {
void foo() { A::foo(); }
};
但这并不意味着你不能实现你想要做的事情。如果要在另一个类中实现该操作并使用继承,可以通过两种不同的方式执行此操作:
中间型:
实现提供该功能的中间类型,然后每个派生类型都可以扩展原始A
或中间I
类型:
class I : public A {
public:
void foo() {}
};
class B : public I {}; // inherits the implementation
class C : public A { // needs to provide it's own implementation
void foo() {}
};
虚拟继承:
或者,您可以使用虚拟继承(在三个选项中,我会避免这种情况(。通过这种方式,您可以创建一个层次结构,其中V
虚拟继承A
并提供实现。其余的类可以直接从A
继承,也可以从虚拟A
继承,也可以从V
继承。通过使用虚拟继承,可以确保层次结构中存在单个A
子对象:
class V : public virtual A {
public:
void foo() {}
};
class B : public virtual A, V { // Inheritance from V is a detail, can be private
// no need to provide foo
};
请注意,对于这个特定的有限问题,这种方法是矫枉过正的,并且它有副作用(对象的布局会有所不同,并且它们的大小会略大(。同样,除非你想混合和匹配来自不同同级类的函数,否则我会避免这种情况。
您的代码中出了什么问题
代码中的问题是您多次从A
继承,一次通过D
,另一次直接继承。虽然在 D
的路径中,虚函数不再是纯函数,但在来自A
的直接路径中,虚函数仍然未定义。
这也会导致错误消息中的第二个问题:歧义。由于有两个A
子对象,因此当您尝试对派生最多的类型调用foo
时,编译器无法确定要在两个子对象中的哪一个A
执行请求。如果尝试从派生最多的类型直接转换为A
,则会出现相同类型的歧义问题,因为编译器不知道要引用哪个A
。
Impl 没有在 D 和 C 中实现 A 的方法,它是一个完全不同的基础。
你应该从 A 派生 Impl,然后从 Impl 而不是 A 派生该子集的每个类。
如果你想同时从 A 和 Impl 继承,那么你需要使用虚拟继承。
编辑:
有关更多详细信息,请参阅@Vlad的答案。
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 分段 排序函数实现中的错误
- 无法去函数实现 vim
- C++ 20 中的运算符 == 和 <=> 应该作为成员还是自由函数实现?
- 为什么在这种情况下不调用我的虚拟函数实现?
- 我能否通过将函数实现为类对象方法来避免使用互斥锁
- 嵌套的模板结构构造函数实现
- C++接口的工厂函数实现
- 链表中的递归长度函数实现
- 我可以期望某些 STL 函数实现是可自动矢量化的吗?
- 如何将深拷贝构造函数实现到链表中?
- 虚拟 CTOR 的克隆函数实现是否有问题
- 没有捕获列表的 lambda 通常作为普通函数实现吗?
- C++二叉树打印函数实现
- C++:默认构造函数实现
- C++派生类中的纯虚函数实现
- 决定放置函数实现的位置
- 强制实施纯虚函数实现,可能使用不同的参数类型
- 如何让成员函数实现依赖于类的模板参数?
- 我们如何将Ostream函数作为类的成员函数实现,而不是作为朋友函数,以便我可以用作虚拟函数