如何强制子相同的虚函数首先调用其父虚函数

gig*_*gle 4 c++ oop polymorphism design-patterns

各位,我有一个案例需要子类在调用其覆盖虚函数之前首先需要调用其父虚函数.

BaseClass::Draw()
{

}

ChildClass::Draw()
{
    BaseClass::Draw(); // BaseClass Draw must be called first.
}

GrandChildClass::Draw()
{
    ChildClass::Draw(); // ChildClass Draw must be called first.
}
Run Code Online (Sandbox Code Playgroud)

我想隐藏客户端的这种行为.这有模式吗?

谢谢.

Jam*_*lis 16

对于简单的情况,您可以使用第二个私有成员函数来实现可重写行为:

class Base {
public:
    void Draw() { 
        // Base-class functionality here
        OverrideableDraw();
    }

private:
    virtual void OverrideableDraw() { }
};

class Derived : public Base {
private:
    virtual void OverrideableDraw() {
        // Derived-class functionality here
    }
};
Run Code Online (Sandbox Code Playgroud)

对于更复杂的层次结构(例如,您有多个继承级别),这是不可能的,因为任何派生类都可以覆盖任何虚拟成员函数(final在C++中没有).通常可以安全地假设每个派生类都在做正确的事情.虽然我可以想到几次我遇到了问题,因为派生类搞砸了覆盖,但这些情况通常很容易调试.

如果你真的很担心它并且真的想要保证首先执行基类重写,你可以使用这样的东西,虽然这是非常昂贵的(至少这个天真的实现非常昂贵):

#include <functional>
#include <iostream>
#include <vector>

class Base {
public:

    Base() {
        RegisterDrawCallback(std::bind(&Base::DrawCallback, this));
    }

    void Draw() {
        for (auto it(drawCallbacks_.begin()); it != drawCallbacks_.end(); ++it)
            (*it)();
    }

protected:

    typedef std::function<void(void)> DrawCallbackType;
    typedef std::vector<DrawCallbackType> DrawSequence;

    void RegisterDrawCallback(DrawCallbackType f) {
        drawCallbacks_.push_back(f);
    }

private:

    void DrawCallback() { std::cout << "Base" << std::endl; }

    DrawSequence drawCallbacks_;
};

class Derived : public Base {
public:

    Derived() {
        RegisterDrawCallback(std::bind(&Derived::DrawCallback, this));
    }

private:

    void DrawCallback() { std::cout << "Derived" << std::endl; }
};

class DerivedDerived : public Derived {
public:

    DerivedDerived() {
        RegisterDrawCallback(std::bind(&DerivedDerived::DrawCallback, this));
    }

private:

    void DrawCallback() { std::cout << "DerivedDerived" << std::endl; }
};
Run Code Online (Sandbox Code Playgroud)

[这只是一个选择; 其他人可能会想出一个更优雅的解决方案.就个人而言,我只是确保虚拟成员函数具有良好的文档记录,并将其留在那里.

  • 事实上,[我们应该更喜欢将虚拟功能设为私有,并提供公共呼叫者](http://www.gotw.ca/publications/mill18.htm). (3认同)