强制调用基类虚函数

Posted

技术标签:

【中文标题】强制调用基类虚函数【英文标题】:Force calling base class virtual function 【发布时间】:2012-04-01 05:38:20 【问题描述】:

我有一些这样的活动

class Granpa // this would not be changed, as its in a dll and not written by me

public:

   virtual void onLoad()



class Father :public Granpa // my modification on Granpa

public:

    virtual void onLoad()
    
       // do important stuff
    



class Child :public Father// client will derive Father


   virtual void onLoad()
   
       // Father::onLoad(); // i'm trying do this without client explicitly writing the call

       // clients code
   

有没有办法强制调用 onLoad 而无需实际编写 Father::onLoad()

欢迎使用黑客解决方案 :)

【问题讨论】:

我不明白为什么这是个问题。你想明确地做某事,所以你需要在代码中明确地说出来…… 不添加一行代码来做你想明确发生的事情是不是有点懒惰? 由于在 ie MFC 处理程序中没有解决相同的问题(您总是需要从您的 CMyDialog::OnInitDialog() 显式调用 CDialog::OnInitDialog()),我认为您可以只要求用户这样做。 我同意@OliCharlesworth,我几乎看不出这是个问题。如果客户端想调用它,他会显式调用它。 如果没有强制调用的解决方案,我将添加他们应该调用它的文档。最好不要让客户的事情变得过于复杂。 【参考方案1】:

如果我理解正确的话,您希望这样每当调用被覆盖的函数时,必须始终首先调用基类实现。在这种情况下,您可以调查template pattern。比如:

class Base

public:
    void foo() 
        baseStuff();
        derivedStuff();
    

protected:
    virtual void derivedStuff() = 0;
private:
    void baseStuff()  ... 
;

class Derived : public Base 
protected:
    virtual void derivedStuff() 
        // This will always get called after baseStuff()
        ...
    
;

【讨论】:

也称为非虚拟接口(虽然我一直把它称为模板方法模式)。 derivedStuff 在两个类中也可以是私有的。 这里也可以找到很好的解释gotw.ca/publications/mill18.htm 正如我在回复中所说,在他的情况下,GrandPa 是外部代码,而 foo (onLoad) 被强制为虚拟。所以没有什么能阻止 Child 覆盖它。 @dweeves 新的虚拟 onLoaded 将被记录,但旧的 onLoad 不会。似乎没有更好的解决方案。【参考方案2】:

如上所述,但适用于您的情况。

class Father :public Granpa // my modification on Granpa


public:

    virtual void onLoad()
    
       // do important stuff
       onLoadHandler();
    
    virtual void onLoadHandler()=0;


class Child :public Father// client will derive Father  


   virtual void onLoadHandler()
   

       // clients code
   

但是,没有什么可以阻止 Child 覆盖 onLoad,因为 c++ 没有 final 关键字,而 Granpa onLoad 本身就是虚拟的。

【讨论】:

幸运的是,C++11 有一个 final 关键字。【参考方案3】:

嗯,没有广为人知、公认的方法来做到这一点。如果有的话,使用事件和信号的 GUI 库可能已经实现了它。但是,您的问题还有其他解决方案。

您可以使用 boost.Signals、sigslot 或您自己制作的东西来实现信号 - 事件连接。正如 GTK+ 所做的那样:

g_signal_connect(my_wdg, "expose-event", G_CALLBACK(my_func), NULL);

gboolean my_func(...)

    // do stuff 
    return FALSE; /* this tells the event handler to also call the base class's slot */

以不那么以 C 为中心的方式,这可以按照以下方式实现:

/* In Granpa */
/* typedef a functor as 'DerivedEventHandler' or use a boost::function */
std::vector< DerivedEventHandler > handlers;

void connect(DerivedEventHandler event)  handlers.push_back(event); 

/* in loop */
while (! iter = handlers.end() ) /* call event */

/* In constructor of Father */
this->connect( this->OnLoad );

/* In constructor of Child */
this->connect( this->OnLoad );

/* and so on... in all derived classes  */

【讨论】:

【参考方案4】:

如果所讨论的对象小且复制成本低,只需调用基类的复制构造函数,即 BaseClass(*derived_class_object).method()

【讨论】:

可能不是 OP 想要的,因为他问如何在没有明确编写 baseClass 函数调用的情况下执行此操作,而您也必须使用这种方法。

以上是关于强制调用基类虚函数的主要内容,如果未能解决你的问题,请参考以下文章

c++ 虚函数 如何调父类 而非子类

C++中的各种“虚“-- 虚函数纯虚函数虚继承虚基类虚析构纯虚析构抽象类讲解

C++中的各种“虚“-- 虚函数纯虚函数虚继承虚基类虚析构纯虚析构抽象类讲解

c++ 中如何调用基类的虚函数?

19.理解虚基类虚继承

深入理解C++ 虚函数表