关于c++继承的初学者架构主题

beginners architectural topic on c++ inheritance

本文关键字:初学者 c++ 继承 关于      更新时间:2023-10-16

来自纯C语言,我是一个c++新手,对OO-Development或多或少还是个新手,因此我提前为我对下面主题的"幼稚"观点道歉。在下文中,我试图用一种抽象的方式来描述我的问题(我的真实类是不同的)。代码部分可能是不可编译的,并且应该只被视为"思想"。

我有几个类,继承自一个基类:

    class Base
    {
    };

    class Derived1 : Base
    {
    };
    class Derived2 : Base
    {
    };
    class Derived3 : Base
    {
    };

此外,vector包含指向派生类实例的指针:

    std::vector<Base *> collection;

现在我想提供这个类方案以供其他用户使用。问题:当用户遍历集合时,他/她如何知道具体的类类型?为派生类提供虚成员函数并不是一个好的选择,因为我事先不知道用户想要做什么。

他/她应该使用dynamic_cast(…)来获得具体类吗?不好…

经过几个小时的思考,我最终得出了以下"解决方案",但我不确定这是真的很好还是完全是胡说八道:

为用户对象定义一个接口,在上面提到的类上操作:

    class Derived1;
    class Derived2;
    class Derived3;
    class AbstractUserOperation
    {
        virtual void call(Derived1 * cl) = 0;
        virtual void call(Derived2 * cl) = 0;
        virtual void call(Derived3 * cl) = 0;
    };

用户根据该接口定义一个具体类,以实现他/她对派生类的每种可能类型的期望操作:

    class ConcreteUserOperation : AbstractUserOperation
    {
        virtual void call(Derived1 * cl)
        {
            ...
        }
        virtual void call(Derived2 * cl)
        {
            ...
        }
        virtual void call(Derived3 * cl)
        {
            ...
        }
    };

我自己的类和以前一样,由"evaluate"扩展:

    class Base
    {
        virtual void evaluate(AbstractUserOperation* o) = 0;
    };

    class Derived1 : public Base
    {
        void evaluate(AbstractUserOperation *o)
        {
            o->call(this);
        }
    };
    class Derived2 : public Base
    {
        void evaluate(AbstractUserOperation *o)
        {
            o->call(this);
        }
    };
    class Derived3 : public Base
    {
        void evaluate(AbstractUserOperation *o)
        {
            o->call(this);
        }
    };

用法如下:

    std::vector<Base *> collection;
    ConcreteUserOperation o; // a user defined class, doing whatever with x
    x = collection[i];
    x->evaluate(&o); // call user defined operation o on x

当然,这有点"复杂",但是,至少,一切都取决于用户定义具体的操作类,这与我的类方案完全分离。

从架构的角度来看,这种方法可以接受吗??希望这对这里的专家来说不会太无聊;-)

非常感谢。迈克尔。

我不是这个访问者概念的专家,但我使用类似的代码,我总是认为它是回调。我认为,你甚至不需要为ConcreteUserOperation定义一个接口,如果你允许调用派生的评估类似std::function作为回调参数。然后你甚至可以把lambda作为访问者。Samplecode:

  #include <functional>
  struct Base {};
  struct Derived : public Base
  {
     void evaluate(std::function<void(const Derived&)> callback) const
     {
        callback(*this);
     }
     int foo = 42;
  };
  int main()
  {
     Derived d;
     d.evaluate([](const Derived& d){std::cout << d.foo << std::endl;});
  } 

生活的例子:http://coliru.stacked-crooked.com/a/223af4a43077a760