需要重写的虚函数来调用基类的实现
在C++类的层次结构中,是否有可能强制要求特定的虚函数总是调用其基类的实现呢? (就像构造链的方式?)需要重写的虚函数来调用基类的实现
我正在寻找一个深层次的层次结构有一些常见的接口函数,每个孩子覆盖的情况。我希望每个派生类的重写可以链接到基类。我们可以直接明确地使用,例如下面的代码来做到这一点,但是有可能有人实现新的派生类可能会忘记链接到基地。
是否存在某种模式来强制执行此操作,以至于如果覆盖未能链接基础,则编译器会引发错误?
所以,在
class CAA
{
virtual void OnEvent(CEvent *e) {
// do base implementation stuff;
}
}
class CBB : public CAA
{
typedef CAA BaseClass;
virtual void OnEvent(CEvent *e) {
DoCustomCBBStuff();
BaseClass::OnEvent(e); // chain to base
}
}
class CCC : public CBB
{
typedef CBB BaseClass;
virtual void OnEvent(CEvent *e) {
Frobble();
Glorp();
BaseClass::OnEvent(e); // chain to CBB which chains to CAA, etc
}
}
class CDD : public CCC
{
typedef CCC BaseClass;
virtual void OnEvent(CEvent *e) {
Meep();
// oops! forgot to chain to base!
}
}
有没有办法,一些模板把戏或语法的噱头,使CDD抛出一个比较明显的错误?
其完成的方式是基类方法不是虚拟的,并且调用受保护的虚拟方法。
当然,只处理一个级别。
在你的特殊情况下,大量的基础设施可以使它工作,但这不值得。
典型的反应是加注释
// Always call base class method
这是答案和接受之间的looong时间。 – Joshua 2015-09-25 21:51:57
没有什么,直接执行压倒一切的功能做什么特别,除了返回一个特定类型。但是,如果您使基类虚函数private
没有函数可以在基类上调用它,但派生类可以覆盖它。然后您还提供一个调用虚函数的函数以及一个执行基类逻辑的函数。来自基类的逻辑可能应该进入单独的函数(可能直接使用非虚拟转发函数),以避免在对象实际上是基础对象时执行两次。
问题在于它只覆盖一个层次 - 即所有派生调用都将调用最终基本函数,但所有中间类都不参与该派对。 – Xeo 2012-01-03 00:09:24
@Xeo:同意。我不知道任何强制虚拟函数链接的方法。尽管我认为链接覆盖函数的想法很可爱(它用于例如Xt中,这是一个用C语言实现的用于X11的面向对象的GUI系统;所有面向对象的东西都需要手动完成),但我并不需要任何东西,但建设和破坏偶然被链接。 – 2012-01-03 00:13:16
有一个在C++语言对此没有支持,但扩大对KerrekSB的评论,你可以做这样的事情:
class A {
public:
void DoEvent(int i) {
for (auto event = events.begin(); event != events.end(); ++event)
(this->*(*event))(i);
}
protected:
typedef void (A::*Event)(int);
A(Event e) {
events.push_back(&A::OnEvent);
events.push_back(e);
}
void OnEvent(int i) {
cout << "A::OnEvent " << i << endl;
}
vector<Event> events;
};
class B : public A {
public:
B() : A((Event)&B::OnEvent) { }
protected:
B(Event e) : A((Event)&B::OnEvent) {
events.push_back(e);
}
void OnEvent(int i) {
cout << "B::OnEvent " << i << endl;
}
};
class C : public B {
public:
C() : B((Event)&C::OnEvent) { }
protected:
C(Event e) : B((Event)&C::OnEvent) {
events.push_back(e);
}
void OnEvent(int i) {
cout << "C::OnEvent " << i << endl;
}
};
然后使用它像这样
int main() {
A* ba = new B;
ba->DoEvent(32);
B* bb = new B;
bb->DoEvent(212);
A* ca = new C;
ca->DoEvent(44212);
B* cb = new C;
cb->DoEvent(2);
C* cc = new C;
cc->DoEvent(9);
}
输出
A::OnEvent 32
B::OnEvent 32
A::OnEvent 212
B::OnEvent 212
A::OnEvent 44212
B::OnEvent 44212
C::OnEvent 44212
A::OnEvent 2
B::OnEvent 2
C::OnEvent 2
A::OnEvent 9
B::OnEvent 9
C::OnEvent 9
你必须做一些工作,但你不必手动调用baser成员功能在每次通话结束时。 Here is the live demo。
这就是我所指的太多工作,但它确实有效。 – Joshua 2012-01-03 02:09:24
@Joshua不幸的是,这是最好的,你可以做,而不只是诉诸公约。 – 2012-01-03 02:10:01
使用friend
将一个特殊的“隐藏”类型放在Base类中,以确保只有Base可以创建它。 This code on ideone。
如果有多个级别,这不幸的是不能保证立即的基类被调用。因此,struct E : public D;
可能实施E::foo()
与B:: foo
呼叫时,您可能更喜欢拨打电话D::foo()
。
struct Base {
struct Hidden {
friend class Base;
private:
Hidden() {}
};
virtual Hidden foo() {
cout << "Base" << endl;
return Hidden(); // this can create a Hidden
}
};
struct D : public B {
virtual Hidden foo() {
cout << "D" << endl;
// return Hidden(); // error as the constructor is private from here
return B :: foo();
}
};
如果你试图执行d :: foo的()没有返回或return Hidden()
,你会得到一个错误信息。编译这个的唯一方法是使用return B :: foo()
。
Cuty ......但有点瑕疵。 'foo()'应该返回一个const引用,并且你应该防止创建'Hidden'的副本。当然,你的解决方案已经可以防止意外遗漏,特别是将'Hidden'重命名为'DoNotForgetToChain'或类似的东西。 – 2012-01-03 07:16:12
遵循通过模板类派生的简单规则是可能的。
#include <iostream>
struct TEvent
{
};
struct Base {
virtual void CallOnEvent(TEvent * e)
{
OnEvent(e);
}
virtual void OnEvent(TEvent * e)
{
std::cout << "Base::Event" << std::endl;
}
void CallUp(TEvent * e)
{
}
};
template <typename B>
struct TDerived : public B
{
void CallUp(TEvent * e)
{
B::CallUp(e);
B::OnEvent(e);
}
virtual void CallOnEvent(TEvent * e)
{
CallUp(e);
this->OnEvent(e);
}
};
struct Derived01 : public TDerived<Base>
{
void OnEvent(TEvent * e)
{
std::cout << "Derived01::Event" << std::endl;
}
};
struct Derived02 : public TDerived<Derived01>
{
void OnEvent(TEvent * e)
{
std::cout << "Derived02::Event" << std::endl;
}
};
struct Derived03 : public TDerived<Derived02>
{
void OnEvent(TEvent * e)
{
std::cout << "Derived03::Event" << std::endl;
}
};
struct Derived04 : public TDerived<Derived03>
{
void OnEvent(TEvent * e)
{
std::cout << "Derived04::Event" << std::endl;
}
};
int main(void)
{
Derived04 lD4;
lD4.CallOnEvent(0);
return 0;
}
此代码的产率(codepad):
Base::Event
Derived01::Event
Derived02::Event
Derived03::Event
Derived04::Event
关于使用typeid
一些答案。除了调试以外,我绝不会考虑使用typeid
。这是因为两件事情:
- 动态类型检查可以以更有效的方式(来完成,而无需创建
type_info
对象即使用dynamic_cast
,一些方法 - C++标准基本上只保证的typeid是否存在等,但算不上什么关于它是如何工作(大多数事情都是“具体的编译器”)
编辑:
稍微更具有多重继承的复杂例子。 不幸的是,如果没有在多个类中继承的类的显式调用(主要是因为不清楚在这种情况下会发生什么,所以我们必须明确定义行为),这个问题是无法解决的。
#include <iostream>
struct TEvent
{
};
struct Base {
virtual void CallOnEvent(TEvent * e)
{
OnEvent(e);
}
virtual void OnEvent(TEvent * e)
{
std::cout << "Base::Event" << std::endl;
}
void CallUp(TEvent * e)
{
}
};
template <typename B >
struct TDerived : public B
{
void CallUp(TEvent * e)
{
B::CallUp(e);
B::OnEvent(e);
}
virtual void CallOnEvent(TEvent * e)
{
CallUp(e);
this->OnEvent(e);
}
};
struct Derived01 : virtual public TDerived<Base>
{
void OnEvent(TEvent * e)
{
std::cout << "Derived01::Event" << std::endl;
}
};
struct Derived02 : virtual public TDerived<Derived01>
{
void OnEvent(TEvent * e)
{
std::cout << "Derived02::Event" << std::endl;
}
};
typedef TDerived<Derived02> TDerived02;
typedef TDerived<Derived01> TDerived01;
struct Derived03 : virtual public TDerived02, virtual public TDerived01
{
void OnEvent(TEvent * e)
{
std::cout << "Derived03::Event" << std::endl;
}
virtual void CallOnEvent(TEvent * e)
{
CallUp(e);
Derived03::OnEvent(e);
}
void CallUp(TEvent * e)
{
TDerived02::CallUp(e);
TDerived01::CallUp(e);
}
};
struct Derived04 : public TDerived<Derived03>
{
void OnEvent(TEvent * e)
{
std::cout << "Derived04::Event" << std::endl;
}
};
int main(void)
{
Derived04 lD4;
Derived03 lD3;
lD3.CallOnEvent(0);
std::cout << std::endl;
lD4.CallOnEvent(0);
return (0);
}
结果(ideone):
Base::Event \ \
Derived01::Event | - from Derived02 |
Derived02::Event/ |-- from Derived03
Base::Event \__ from Derived01 |
Derived01::Event/ |
Derived03::Event /
Base::Event \ \ \
Derived01::Event | - from Derived02 | |
Derived02::Event/ |-- from Derived03 |-- from Derived04
Base::Event \__ from Derived01 | |
Derived01::Event/ | |
Derived03::Event / |
Derived04::Event /
据我所知,但你总是给基类的非虚函数和存储函数指针的清单,其中每个派生类增加它的一部分。 – 2012-01-03 00:06:27
有效地复制了[如何强制子虚拟函数先调用其父虚函数](http://*.com/questions/5644338/how-to-force-child-same-virtual-function-call-its -parent-virtual-function-first) – 2012-01-03 00:49:35
[C++:自动调用基类方法?](http://*.com/questions/3107974/c-call-a-base-class-method-automatically ) – outis 2012-04-22 00:10:12