C++ 实现虚拟方法的组合行为

C++ 实现虚拟方法的组合行为,c++,composition,virtual-functions,virtual-inheritance,C++,Composition,Virtual Functions,Virtual Inheritance,假设我有几个阶层的继承人: class A { public: virtual void DoStuff() = 0; }; class B : public A { public: // Does some work void DoStuff() override; }; class C : public B { public: // Calls B::DoStuff and does other work void DoStuff() override

假设我有几个阶层的继承人:

class A {
public:
    virtual void DoStuff() = 0;
};

class B : public A {
public:
    // Does some work
    void DoStuff() override;
};

class C : public B {
public:
   // Calls B::DoStuff and does other work
   void DoStuff() override;
};
它可以天真地实现:

void Derived::DoStuff() {
    Base::DoStuff();
    ...
}
我认为,这种实现有一个严重的问题:在重写时,必须始终记住调用基本实现

备选方案:

class A {
public:
    void DoStuff() {
        for (auto& func: callbacks_) {
            func(this);
        }
    }

    virtual ~A() = default;
protected:
    template <class T>
    void AddDoStuff(T&& func) {
        callbacks_.emplace_back(std::forward<T>(func));
    }

private:
    template <class... Args>
    using CallbackHolder = std::vector<std::function<void(Args...)>>;

    CallbackHolder<A*> callbacks_;
};
A类{
公众:
void DoStuff(){
用于(自动和功能:回调){
func(本);
}
}
virtual~A()=默认值;
受保护的:
模板
void AddDoStuff(T&func){
回调(std::forward(func));
}
私人:
模板
使用CallbackHolder=std::vector;
回拨持有人回拨;
};
用法:

class Derived : public Base {
public:
    Derived() {
        AddDoStuff([](A* this_ptr){
            static_cast<Derived*>(this_ptr)->DoStuffImpl();
        });
    }
private:
    void DoStuffImpl();
};
派生类:公共基{
公众:
派生的(){
AddDoStuff([](A*本文件){
静态_cast(此_ptr)->dostuffinpl();
});
}
私人:
void dostuffinpl();
};
但是,我相信,与第一个实现相比,在实际调用
DoStuff()
时,它有很大的开销。在我看到的用例中,对象的长构造可能不是问题(如果需要,也可以尝试实现类似“短向量优化”的东西)

另外,我认为每个
DoStuff
方法的3个定义有点过于简单

我知道,通过使用继承模式simular to CRTP可以非常有效地解决这个问题,并且可以将基于模板的解决方案隐藏在接口类后面(
A
,在本例中),但我一直在想——难道没有更简单的解决方案吗

我对
从基调用派生实现的良好实现感兴趣,当且仅当派生类存在并且它有一个用于长继承链(或类似的东西)的重写方法时

谢谢

编辑:
我知道@Jarod42的答案中描述了一个想法,但我觉得这个想法并不合适,因为我认为它对于长继承链来说很难看——每个层次结构都必须使用不同的方法名。

您可以将类
B
更改为类似以下内容:

A类{
公众:
virtual~A()=默认值;
虚空DoStuff()=0;
};
B类:公共A{
公众:
void DoStuff()final{/*..*/DoExtraStuff();}
虚拟void DoExtraStuff(){}
};
丙类:公共乙类{
公众:
void DoExtraStuff()覆盖;
};

您可以将您的类
B
更改为如下内容:

A类{
公众:
virtual~A()=默认值;
虚空DoStuff()=0;
};
B类:公共A{
公众:
void DoStuff()final{/*..*/DoExtraStuff();}
虚拟void DoExtraStuff(){}
};
丙类:公共乙类{
公众:
void DoExtraStuff()覆盖;
};

我不确定自己是否理解正确,但建议似乎很好地解决了这一问题

我认为它起源于开闭原理。技术如下:

#include <iostream>
class B {
    public:
    void f() {
        before_f();
        f_();
    };

    private:
    void before_f() {
        std::cout << "will always be before f";
    }

    virtual void f_() = 0;
};

class D : public B{
    private:
    void f_() override {
        std::cout << "derived stuff\n";
    }
};

int main() {
    D d;
    d.f();
    return 0;
}
#包括
B类{
公众:
void f(){
在_f()之前;
f_u2;();
};
私人:
_f()之前无效{

我不确定我是否理解正确,但这个建议似乎很好地解决了这个问题

我认为它起源于开闭原理。技术如下:

#include <iostream>
class B {
    public:
    void f() {
        before_f();
        f_();
    };

    private:
    void before_f() {
        std::cout << "will always be before f";
    }

    virtual void f_() = 0;
};

class D : public B{
    private:
    void f_() override {
        std::cout << "derived stuff\n";
    }
};

int main() {
    D d;
    d.f();
    return 0;
}
#包括
B类{
公众:
void f(){
在_f()之前;
f_u2;();
};
私人:
_f()之前无效{

std::cout这是将面向公共的API与面向派生类的API分离。结合Jarod42的答案,将有助于很好地分离关注点。在小项目上做得过火,但在大项目上却很有帮助。@Eljay Yea,我很认真地对待。我想Jarod的答案如下所示“引入另一个抽象层不会解决任何问题。"规则。也就是说,总结一下。你搞错了。我想要一个深层继承链的解决方案。在这种情况下,为每个层次结构级别使用不同的方法名称变得非常奇怪。这是面向公共的API与面向派生类的API的分离。结合Jarod42的答案,可以很好地分离关注点。Overki我对小项目很感兴趣,但对大项目很有帮助。@Eljay Yea,我非常重视。我想Jarod的回答是“引入另一个抽象层不会解决任何问题。”规则。也就是说,总结一下。你搞错了。我想要一个深层继承链的解决方案。在这种情况下,为每个层次结构级别使用不同的方法名称变得非常奇怪。如果继承深度为5,则有5个不同的方法名称,它们基本上做相同的事情。如果你的Inhritance depth是5,每一层都为同一虚拟方法添加了更多的行为,那么我认为您可能首先需要重新考虑继承层次结构的复杂性。如果继承深度是5,那么您有5个不同的方法名称,它们基本上做相同的事情。我认为如果您的固有实例深度为5,并且每一层都向同一虚拟方法添加了更多行为,那么我认为您可能首先需要重新考虑继承层次结构的复杂性。“在重写时必须始终记住调用基实现。”这就是最主要的功能。“当重写时,必须始终记住调用基本实现。”这就是主要的功能。