强制调用基类虚函数

23

我有一些像这样的事件

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
   }
}

有没有一种方法可以强制调用 Father::onLoad(),而不必实际编写该代码?

欢迎使用 hackish 解决方案 :)


4
我不明白这是为什么会有问题。你想要明确地做某事,所以你需要在代码中明确地说明... - Oliver Charlesworth
2
不加一行代码来明确地执行你想要发生的事情,是不是有点懒呢? - Ed Heal
由于在MFC处理程序中没有解决相同的问题(您始终需要从CMyDialog :: OnInitDialog() 显式调用CDialog :: OnInitDialog()),因此我认为您可以要求用户这样做。 - Mikhail
1
我同意@OliCharlesworth的观点,我很难看出这是一个问题。如果客户想调用它,他将明确地调用它。 - ApprenticeHacker
1
如果没有强制调用的解决方案,我会在文档中添加他们应该调用它的说明。对于客户端来说,不要把事情搞得太复杂是更好的。 - mikbal
显示剩余3条评论
4个回答

35

如果我理解正确,您希望每当调用重载函数时,基类实现都必须首先被调用。在这种情况下,您可以研究一下模板模式。类似这样的代码:

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()
        ...
    }
};

2
也被称为非虚拟接口(尽管我一直把它称为模板方法模式)。 - Mark B
derivedStuff 在两个类中也可以是私有的。 - Dan
2
这里也可以找到一个很好的解释:http://www.gotw.ca/publications/mill18.htm - pmr
1
正如我在之前的回应中所说,对于他的情况,GrandPa是外部代码,而foo(onLoad)被强制成虚函数。因此,没有任何阻止Child覆盖它的东西。 - dweeves
Base::derivedStuff() 应该是 protected,而不是 private,否则你无法在 Derived 中重新实现它。 - Aurélien Gâteau
显示剩余2条评论

3
如上所建议,但适用于您的情况。
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
   }
}

然而,由于C++没有final关键字并且爷爷的onLoad本身是虚拟的,因此无法阻止子类重写onLoad。

6
幸运的是,C++11有一个final关键字。 - juanchopanza

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  */

-1
如果所涉及的对象很小且复制成本低廉,只需调用基类的复制构造函数,即BaseClass(*derived_class_object).method()。

可能不是 OP 想要的,因为他询问如何在不显式调用 baseClass 函数的情况下完成,而使用这种方法仍然需要进行此操作。 - Haatschii

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接