Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/147.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;11避免;请叫“超级”;代码气味_C++_C++11_Lambda - Fatal编程技术网

C++ C++;11避免;请叫“超级”;代码气味

C++ C++;11避免;请叫“超级”;代码气味,c++,c++11,lambda,C++,C++11,Lambda,我正在寻找避免“呼叫超级”代码气味的方法。当需要子类在重新实现虚拟函数时调用超类版本的虚拟函数时,就会出现这种代码味道 class Base { public: virtual void foo(){ ... } } class Derived : public Base { public: virtual void foo(){ Base::foo();// required! ... } } 如果继承只深入一层,我可以使用模板方法 class

我正在寻找避免“呼叫超级”代码气味的方法。当需要子类在重新实现虚拟函数时调用超类版本的虚拟函数时,就会出现这种代码味道

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(){ ... }
}
但如果我需要对派生类进行子类化,我就回到了开始的地方

我正在考虑注册的办法

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(){ ... }
}
类基
{
公众:
Base()
{
_registerCallback([this](){u baseFoo();});
}
void foo()
{
用于(自动f:_回调)
f();
}
受保护的:
无效寄存器回调(std::函数f)
{ 
_回调_回调;
}
派生类:公共基
{
公众:
派生的()
{
_registerCallback([this](){u derivedFoo();});
}
私人:
虚拟空_derivedFoo(){…}
}
是否有更标准的方法?此方法有任何问题或改进吗?

使用

class Derived : public Base
{
    public:
        virtual void foo(){ Base::foo();// required! ... }
}

是IMO最好的方法。我不确定为什么你会考虑“代码气味”。

  • 在您建议的最后一种方法中,出错的可能性更大
  • 更容易检测对
    Base::foo()
    的错过调用
  • 如果从
    Base
    派生的所有类都需要实现
    Base::foo()
    的功能,那么最好将公共代码放在
    Base::foo()
    中。派生类只需进行调用

  • 值得一提的是,我们在我的工作中大量使用了该模式,经过20多年的使用,它已被证明是健壮的。

    如果您在每个级别上引入新的虚拟成员函数,并在下一个级别上覆盖它,您可以继续使用模板方法:

    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>) {}
    };
    
    模板结构标记{};
    阶级基础
    {
    公众:
    void foo(){…;foo_impl(标记{});}
    受保护的:
    虚拟void foo_impl(tag){}
    };
    类Derived1:公共基
    {
    受保护的:
    虚空foo_impl(tag)重写final{…;foo_impl(tag{});}
    虚拟void foo_impl(tag){}
    };
    派生类2:公共派生类1
    {
    受保护的:
    虚空foo_impl(tag)重写final{…;foo_impl(tag{});}
    虚拟void foo_impl(tag){}
    };
    派生类3:公共派生类2
    {
    受保护的:
    虚空foo_impl(tag)重写final{…;foo_impl(tag{});}
    虚拟void foo_impl(tag){}
    };
    

    如果你不喜欢标签调度,你可以只给出不同的名字,也许类似于“代码> FooY-Pythnn。

    我考虑所有这些过度工程。

    一个主要关注点是孩子们不调用他们父母相应的成员函数,这提供了一个修复该部分的想法:

    #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
    }
    
    #包括
    阶级基础{
    公众:
    void foo(){
    foo_impl();
    assert(base_foo_called&“call base class foo_impl”);
    }
    受保护的:
    虚拟void foo_impl(){base_foo_called=true;}
    私人:
    bool base_foo_called=false;
    };
    类别:公共基础{
    受保护的:
    void foo_impl()覆盖{
    Base::foo_impl();
    }
    };
    类别衍生衍生罚款:公共衍生罚款{
    受保护的:
    void foo_impl()覆盖{
    DerivedFine::foo_impl();
    }
    };
    类衍生非罚款:公共衍生罚款{
    受保护的:
    void foo_impl()重写{}
    };
    int main(){
    (二)傅晴;;
    foo.foo();
    DerivedDerivedFine酒吧;
    bar.foo();
    DerivedNotfine baz;
    baz.foo();//这个断言
    }
    
    CRTP可以解决所有问题

    对于每个
    foo
    方法,您实现了一个空的非虚拟
    foo\u before()
    ,它在CRTP帮助程序中不起任何作用

    CRTP helper接受一个派生的和一个基。它的
    virtualvoid foo()
    调用
    static\u cast(this)->foo\u before()
    然后
    base::foo()
    然后
    after\u foo()


    这里有一个灵感来自

    其思想是利用结构/类的构造函数和析构函数提供某种“前/后函数调用”的事实因此,我们可以使用一个函子,在构造函数/析构函数中定义前置/后置函数调用,而不是在虚拟方法本身中执行前置/后置函数调用。这样,从基函子继承的函子将继承前置/后置函数调用

    代码 输出
    为什么调用父类的实现是一件坏事?问:你为什么还要浪费脑细胞来担心这么愚蠢的事情?如果有一种方法可以工作,就使用它。如果你不喜欢类的组织方式,就重构它。@MK.,因为不可避免地会有人忘记这样做,然后程序会错过一些重要的行为和规则需要一个很好的调试会话来修复。@MK.这是一种代码味道。这不一定是一个问题,但可能表明设计有缺陷。设计的问题是它很容易被破坏。任何编写子类的人都需要知道如何调用基类函数。您知道,从可以实例化的类中进行子类化也是一种错误通常被认为是一种代码味道?如果基函数是可实例化的,则最常需要调用基函数版本的虚拟函数?这正是我想要的,只需要稍微多一点的代码,而不是简单地处理超级调用。建议将虚拟函数改为私有函数,因为子类应该不需要调用pa租用类版本。还可以将标记移动到基中,并具有受保护的可见性,以避免污染全局命名空间。@JasonScott现在对于每个派生的impl,您必须回忆一下,以遵循这种重写和扩展模式(顺便说一句,这是非常不寻常的,请参阅)与其回忆调用基成员函数,还有什么好处???另外,在派生中不调用基成员函数,而是重写它,那么为什么要将其私有化呢?@yuri kilochek
    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
    
    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