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,并且每一层都向同一虚拟方法添加了更多行为,那么我认为您可能首先需要重新考虑继承层次结构的复杂性。“在重写时必须始终记住调用基实现。”这就是最主要的功能。“当重写时,必须始终记住调用基本实现。”这就是主要的功能。