#include <iostream>
void doSomething (int x) {std::cout << "Do something with " << x << std::endl;}
struct Base {
virtual int foo() const {return 5;}
virtual int goo() const {return 6;}
virtual int hoo() const {return 7;}
void noTemplatePattern() const {
// Code A
if (Base::foo() < 6) {
// Code B
}
doSomething (Base::goo());
// Code C
if (Base::hoo() > 10) {
// Code D
}
}
void templatePattern() const {
// Code A
if (foo() < 6) {
// Code B
}
doSomething (goo());
// Code C
if (hoo() > 10) {
// Code D
}
}
};
struct Derived : Base {
virtual int foo() const override {return 12;}
virtual int goo() const override {return 13;}
virtual int hoo() const override {return 14;}
};
int main() {
Derived d;
d.noTemplatePattern();
d.templatePattern();
}
除了为每个代码创建帮助函数之外,如何最好避免重复代码A,代码B,代码C,代码D等中包含的代码?有更通用的方式吗?除了一个使用模板模式,我有相同的功能,而另一个没有。虚函数之间的代码体是相同的。如果我为每个相同的部分定义一个辅助函数,它会变得非常混乱,并且它们也会有太多。
如果您需要更多说明,请参阅我的生产代码片段,说明这一点。 SpellCaster
源自LivingBeing
,LivingBeing::cannotAttackLongRange(int)
覆盖SpellCaster::cannotAttackLongRange(int)
。
inline std::set<LivingBeingProxy*> LivingBeing::unattackableTargets() const {
std::set<LivingBeingProxy*> nonTargets;
if (isCharmed()) {
for (auto it = std::next(getStatesList(CHARM_SPELL).begin(), 1); it != getStatesList(CHARM_SPELL).end(); ++it)
nonTargets.emplace (std::dynamic_pointer_cast<CharmedStateBase>(*it)->getCharmer());
}
for (LivingBeingProxy* x : getLocation()->allBeingsAlive()) {
if ( (x->heightAboveGround() > damageInflictor(0)->getReach()) && !canFly()
&& LivingBeing::cannotAttackLongRange(distanceBetween(this, x->getActual()))) //*** virtual method here!
{nonTargets.emplace(x); continue;}
if ( (x->heightAboveGround()) < 0 && (x->getTerrain() == InWater) && !canSwim() )
{nonTargets.emplace(x); continue;}
}
// ...
return nonTargets;
}
inline std::set<LivingBeingProxy*> LivingBeing::unattackableTargetsIncludingBySpells() const {
std::set<LivingBeingProxy*> nonTargets;
if (isCharmed()) {
for (auto it = std::next(getStatesList(CHARM_SPELL).begin(), 1); it != getStatesList(CHARM_SPELL).end(); ++it)
nonTargets.emplace (std::dynamic_pointer_cast<CharmedStateBase>(*it)->getCharmer());
}
for (LivingBeingProxy* x : getLocation()->allBeingsAlive()) {
if ( (x->heightAboveGround() > damageInflictor(0)->getReach()) && !canFly()
&& cannotAttackLongRange (distanceBetween(this, x->getActual()))) //*** virtual method here!
{nonTargets.emplace(x); continue;}
if ( (x->heightAboveGround()) < 0 && (x->getTerrain() == InWater) && !canSwim() )
{nonTargets.emplace(x); continue;}
}
// ...
return nonTargets;
}
LivingBeing::unattackableTargets()
计算所有不能被普通武器攻击的敌人,而LivingBeing::unattackableTargetsIncludingBySpells()
计算所有不能被普通武器和法术攻击的敌人。一个SpellCaster
想要在使用普通武器进行攻击时调用第一个,并且在使用咒语攻击时想要调用第二个。
答案 0 :(得分:1)
如果templatePattern
/ noTemplatePattern
冗长且复杂且boo
,goo
和hoo
很简单,您可以执行以下操作:
struct Base {
virtual int foo(bool = false) const {return 5;}
virtual int goo(bool = false) const {return 6;}
virtual int hoo(bool = false) const {return 7;}
void Pattern(bool base) const {
// Code A
if (foo(base) < 6) {
// Code B
}
doSomething (goo(base));
// Code C
if (hoo(base) > 10) {
// Code D
}
}
};
struct Derived : Base {
int foo(bool base = false) const override {return base ? Base::foo() : 12;}
int goo(bool base = false) const override {return base ? Base::goo() : 13;}
int hoo(bool base = false) const override {return base ? Base::hoo() : 14;}
};
int main() {
Derived d;
d.Pattern(true); // d.noTemplatePattern();
d.Pattern(false); // d.templatePattern();
}
不完全优雅,但可能在特定情况下有效。
注意:如果您使用override
关键字,则无需重复virtual
个关键字。
答案 1 :(得分:1)
使用模板和CRTP,如果合适,您可以执行以下操作:
template <typename T, typename D>
void helper(const D& base)
{
// Code A
if (base.T::foo() < 6) {
// Code B
}
doSomething (base.T::goo());
// Code C
if (base.T::hoo() > 10) {
// Code D
}
}
struct Base {
virtual ~Base() = default;
virtual int foo() const {return 5;}
virtual int goo() const {return 6;}
virtual int hoo() const {return 7;}
void noTemplatePattern() const
{
// use Base::foo, Base::goo and Base::hoo
helper<Base>(*this);
}
#if 0
virtual void templatePattern() const = 0;
#endif
};
template <typename Derived>
struct BaseImpl : Base {
template <typename Derived>
void BaseImpl<Derived>::templatePattern() const {
// use Derived::foo, Derived::goo and Derived::hoo
helper<Derived>(static_cast<const Derived&>(*this));
}
};
答案 2 :(得分:1)
使用标签调度的一种解决方案(但需要更多代码foo
,goo
,hoo
)
struct Base {
virtual int foo() const {return foo(std::false_type());}
virtual int goo() const {return goo(std::false_type());}
virtual int hoo() const {return hoo(std::false_type());}
void noTemplatePattern() const { doIt (std::false_type()); }
void templatePattern() const { doIt (std::true_type()); }
private:
template <typename T>
void doIt (T t) const {
// Code A
if (foo(t) < 6) {
// Code B
}
doSomething (goo(t));
// Code C
if (hoo(t) > 10) {
// Code D
}
}
// tag dispatching between virtual call and Base::call
int foo(std::false_type) const {return 5;}
int goo(std::false_type) const {return 6;}
int hoo(std::false_type) const {return 7;}
int foo(std::true_type) const {return foo();}
int goo(std::true_type) const {return goo();}
int hoo(std::true_type) const {return hoo();}
};
答案 3 :(得分:0)
好的,这是我想到的一个解决方案,但是虽然它有效但我不知道它是否被认为是好的(需要一些意见)。但至少避免了所有重复的代码,因此对这些部分的任何更改只需要进行一次:
#include <iostream>
void doSomething (int x) {std::cout << "Do something with " << x << std::endl;}
struct Base {
virtual int foo() const {return fooBase();}
virtual int goo() const {return gooBase();}
virtual int hoo() const {return hooBase();}
virtual void voidFunction() const {voidFunctionBase();}
void noTemplatePattern() const {
doIt (&Base::fooBase, &Base::gooBase, &Base::hooBase, &Base::voidFunctionBase);
}
void templatePattern() const {
doIt (&Base::foo, &Base::goo, &Base::hoo, &Base::voidFunction);
}
private:
void doIt (int(Base::*a)()const, int(Base::*b)()const, int(Base::*c)()const,
void(Base::*d)()const) const {
// Code A
if ((this->*a)() < 6) {
// Code B
}
doSomething((this->*b)());
// Code C
if ((this->*c)() > 10) {
// Code D
}
(this->*d)();
// Code E
}
int fooBase() const {return 5;}
int gooBase() const {return 6;}
int hooBase() const {return 7;}
void voidFunctionBase() const {std::cout << "Base::voidFunction() called.\n";}
};
struct Derived : Base {
virtual int foo() const override {return 12;}
virtual int goo() const override {return 13;}
virtual int hoo() const override {return 14;}
virtual void voidFunction() const override {std::cout << "Derived::voidFunction() called.\n";}
};
int main() {
Derived d;
d.noTemplatePattern();
d.templatePattern();
}
输出:
Do something with 6
Base::voidFunction() called.
Do something with 13
Derived::voidFunction() called.
评论?一个更好的解决方案?
答案 4 :(得分:0)
如果适用,可能的方法是切割对象:
void noTemplatePattern() const {
// copy only Base part (slicing). Require that Base is constructible
Base(*this).templatePattern();
}
答案 5 :(得分:0)
对此有一个非常简单,毫不含糊的解决方案:只需将两个函数替换为带有参数bool includeSpells
的函数。然后你可以在函数中检查这个参数并执行相应的函数调用。功能的其余部分保持不变。