需要重写的虚函数来调用基类的实现

需要重写的虚函数来调用基类的实现

问题描述:

在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抛出一个比较明显的错误?

+1

据我所知,但你总是给基类的非虚函数和存储函数指针的清单,其中每个派生类增加它的一部分。 – 2012-01-03 00:06:27

+0

有效地复制了[如何强制子虚拟函数先调用其父虚函数](http://*.com/questions/5644338/how-to-force-child-same-virtual-function-call-its -parent-virtual-function-first) – 2012-01-03 00:49:35

+0

[C++:自动调用基类方法?](http://*.com/questions/3107974/c-call-a-base-class-method-automatically ) – outis 2012-04-22 00:10:12

其完成的方式是基类方法不是虚拟的,并且调用受保护的虚拟方法。

当然,只处理一个级别。

在你的特殊情况下,大量的基础设施可以使它工作,但这不值得。

典型的反应是加注释

// Always call base class method 
+1

这是答案和接受之间的looong时间。 – Joshua 2015-09-25 21:51:57

没有什么,直接执行压倒一切的功能做什么特别,除了返回一个特定类型。但是,如果您使基类虚函数private没有函数可以在基类上调用它,但派生类可以覆盖它。然后您还提供一个调用虚函数的函数以及一个执行基类逻辑的函数。来自基类的逻辑可能应该进入单独的函数(可能直接使用非虚拟转发函数),以避免在对象实际上是基础对象时执行两次。

+0

问题在于它只覆盖一个层次 - 即所有派生调用都将调用最终基本函数,但所有中间类都不参与该派对。 – Xeo 2012-01-03 00:09:24

+0

@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

+0

这就是我所指的太多工作,但它确实有效。 – Joshua 2012-01-03 02:09:24

+0

@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()

+0

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          /