在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抛出更明显的错误?
答案 0 :(得分:9)
它的完成方式是基类方法不是虚拟的,并且调用受保护的虚方法。
当然,只处理一个级别。
在您的特定情况下,大量的基础设施可以使其发挥作用,但这不值得。
典型的回应是添加评论
// Always call base class method
答案 1 :(得分:3)
使用私有构造函数在Base类中放置一个特殊的“隐藏”类型,使用friend
确保只有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();
}
};
如果您尝试在没有返回或使用return Hidden()
的情况下实现D :: foo(),则会收到错误消息。编译它的唯一方法是使用return B :: foo()
。
答案 2 :(得分:3)
遵循一个简单的规则来推导模板类是可能的。
#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
,某些方法修改强>
具有多重继承的稍微复杂的示例。 遗憾的是,如果没有从多个基类继承的类中的显式调用,这个问题是不可解决的(主要是因为在这种情况下不清楚应该发生什么,所以我们必须明确定义行为)。
#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 /
答案 3 :(得分:1)
在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
答案 4 :(得分:0)
除了返回某种类型之外,没有任何东西可以直接执行覆盖函数来执行任何操作。但是,如果您创建基类虚函数private
,则没有函数可以在基类上调用它,但派生类可以覆盖它。然后,您还提供了一个调用虚函数的public
函数以及一个执行基类逻辑的函数。基类中的逻辑可能应该进入一个单独的函数(可能是直接的非虚拟转发函数),以避免在对象实际上恰好是基础对象时执行两次。
答案 5 :(得分:0)
这是一个铿锵有力的检查:https://reviews.llvm.org/rCTE329448(由我制作,所以你可以就此提出任何问题)