我正在寻找避免“召唤超级”代码气味的方法。当重新实现该函数时需要子类来调用超类的虚函数版本时,会出现此代码异味。
class Base
{
public:
virtual void foo(){ ... }
}
class Derived : public Base
{
public:
virtual void foo(){ Base::foo();// required! ... }
}
如果继承只有一层深,我可以使用模板方法
class Base
{
public:
void foo(){ ... ; foo_impl(); }
protected:
virtual void foo_impl(){}
}
class Derived : public Base
{
protected:
virtual void foo_impl(){ ... }
}
但如果我需要继承Derived,我会回到我开始的地方。
我正在考虑注册方法。
class Base
{
public:
Base()
{
_registerCallback( [this](){ _baseFoo(); } );
}
void foo()
{
for( auto f : _callbacks )
f();
}
protected:
void registerCallback( std::function<void()> f )
{
_callbacks << f;
}
private:
void _baseFoo() { ... }
std::list< std::function<void()> > _callbacks;
}
class Derived : public Base
{
public:
Derived()
{
_registerCallback( [this](){ _derivedFoo(); } );
}
private:
virtual void _derivedFoo(){ ... }
}
有更标准的方法吗?这种方法有任何问题或改进吗?
答案 0 :(得分:5)
使用
sizeof(a)->b
是IMO的最佳方法。我不确定你为什么会考虑#34;代码闻到&#34;。
class Derived : public Base
{
public:
virtual void foo(){ Base::foo();// required! ... }
}
的未接来电。Base::foo()
派生的所有类别都需要实现Base
所做的,那么公共代码在Base::foo()
中的效果会更好。派生类只需要进行调用。对于它的价值,我们在我的工作中使用了这种模式,并且已经证明它已经使用了20多年。
答案 1 :(得分:1)
如果在每个级别引入新的虚拟成员函数并在下一个级别覆盖它,则可以继续使用模板方法:
template <typename> struct tag {};
class Base
{
public:
void foo() { ... ; foo_impl(tag<Base>{}); }
protected:
virtual void foo_impl(tag<Base>) {}
};
class Derived1 : public Base
{
protected:
virtual void foo_impl(tag<Base>) override final { ... ; foo_impl(tag<Derived1>{}); }
virtual void foo_impl(tag<Derived1>) {}
};
class Derived2 : public Derived1
{
protected:
virtual void foo_impl(tag<Derived1>) override final { ... ; foo_impl(tag<Derived2>{}); }
virtual void foo_impl(tag<Derived2>) {}
};
class Derived3 : public Derived2
{
protected:
virtual void foo_impl(tag<Derived2>) override final { ... ; foo_impl(tag<Derived3>{}); }
virtual void foo_impl(tag<Derived3>) {}
};
如果你不喜欢标签发送,你可以给方法改为不同的名字,或许类似于foo_impl_N
。
答案 2 :(得分:0)
我认为所有这些过度工程。
chris mentioned主要关注的是孩子们没有打电话给他们父母的相应成员函数,这给出了修复该部分的想法:
#include <cassert>
class Base {
public:
void foo() {
foo_impl();
assert(base_foo_called && "call base class foo_impl");
}
protected:
virtual void foo_impl() { base_foo_called = true; }
private:
bool base_foo_called = false;
};
class DerivedFine : public Base {
protected:
void foo_impl() override {
Base::foo_impl();
}
};
class DerivedDerivedFine : public DerivedFine {
protected:
void foo_impl() override {
DerivedFine::foo_impl();
}
};
class DerivedDerivedNotFine : public DerivedFine {
protected:
void foo_impl() override {}
};
int main() {
DerivedFine foo;
foo.foo();
DerivedDerivedFine bar;
bar.foo();
DerivedDerivedNotFine baz;
baz.foo(); // this asserts
}
答案 3 :(得分:0)
这是一个受this answer启发的想法
这个想法是利用struct /类的构造函数和析构函数提供一种被继承的“前/后函数调用”机制的事实。因此,我们可以使用函子并在构造函数/析构函数中定义pre / post函数调用,而不是在虚拟方法本身中执行pre / post函数调用。这样,从基函子继承的函子将继承pre / post函数调用。
struct BasePrePostFunctor
{
BasePrePostFunctor()
{
printf("Base pre-func\n");
}
virtual void operator()()
{
printf("Base Main func\n");
}
~BasePrePostFunctor()
{
printf("Base post-func\n");
}
};
struct DerivedPrePostFunctor : BasePrePostFunctor
{
DerivedPrePostFunctor()
{
printf("Derived pre-func\n");
}
void operator()() override
{
printf("Derived main func\n");
}
~DerivedPrePostFunctor()
{
printf("Derived post-func\n");
}
};
class BaseClass
{
public:
virtual void virtual_func()
{
BasePrePostFunctor func;
func();
}
};
class DerivedClass : public BaseClass
{
public:
void virtual_func() override
{
DerivedPrePostFunctor func;
func();
}
};
int main(int argc, char** argv)
{
DerivedClass derived;
derived.virtual_func();
};
Base pre-func
Derived pre-func
Derived main func
Derived post-func
Base post-func
答案 4 :(得分:-1)
CRTP可以解决所有问题。
对于每个foo
方法,您实现一个空的非虚拟foo_before()
,它在您的CRTP帮助程序中不执行任何操作。
CRTP助手采用派生和基础。其virtual void foo()
调用static_cast<Derived*>(this)->foo_before()
然后调用Base::foo()
然后调用after_foo()
。
struct Base {
virtual void foo() { std::cout << "foo\n"; }
virtual ~Base() {};
};
template<class D, class B=Base>
struct foo_helper:B {
virtual void foo() {
static_cast<D*>(this)->before_foo();
this->B::foo();
static_cast<D*>(this)->after_foo();
}
private:
void before_foo() {}; void after_foo() {};
};
struct Derived1 : foo_helper<Derived1> {
void before_foo() { std::cout << "before1\n"; }
};
struct Derived2 : foo_helper<Derived2> {
void before_foo() { std::cout << "before2\n"; }
void after_foo() { std::cout << "after2\n"; }
};
struct DoubleDerived : foo_helper<DoubleDerived, Derived2> {
void after_foo() { std::cout << "even more after\n"; }
};
int main() {
std::cout << "---- Derived1\n";
Derived1 d1;
d1.foo();
std::cout << "---- Derived2\n";
Derived2 d2;
d2.foo();
std::cout << "---- DoubleDerived\n";
DoubleDerived dd;
dd.foo();
}
输出:
---- Derived1
before1
foo
---- Derived2
before2
foo
after2
---- DoubleDerived
before2
foo
after2
even more after