【问题标题】:Getting OOP right正确使用 OOP
【发布时间】:2011-07-01 06:36:40
【问题描述】:

好的,这是我的问题。我有以下课程:

class Job {
   bool isComplete() {}
   void setComplete() {}
   //other functions
};

class SongJob: public Job {
   vector<Job> v;
   string getArtist() {}
   void setArtist() {}
   void addTrack() {}
   string getTrack() {}
   // other functions
};
// This were already implemeted

现在我想实现一个 VideoJob 并从 Job 派生它。但这是我的问题。我还有以下功能,它被设置为仅适用于 SongJob:

void process(SongJob s)
{
// not the real functions
   s.setArtist();
   ..............
   s.getArtist();
   .............
   s.getArtist();
   ...............
   s.setArtist()
}

在这里,我只想表明该函数仅使用派生对象方法。因此,如果我有另一个从 Job 派生的对象,我需要将参数更改为 Job,但是编译器将不知道这些函数,我不知道要为每个人测试它是什么类型的对象,然后将其转换为可以调用正确的函数。

所以可以把所有的函数都放在基类中,因为那样我就没有问题了,但是我不知道这是否正确OOP,如果一个类处理歌曲而另一个处理视频,我好的 oop 意味着有 2 个类。

如果我没有说清楚,请说清楚,我会尝试更好地解释。
简而言之,我想使用多态性。

【问题讨论】:

    标签: c++


    【解决方案1】:

    将类SongJobVideoJob 共有的所有东西放到一个公共基类中是完全可以的。但是,如果您想添加与艺术家无关的Job 的子类,这将导致问题。

    关于您发布的代码有一些注意事项。首先,您的班级Job 显然不是abstract base class。这意味着您可以拥有只是工作的工作。不是SongJob,也不是VideoJob。如果您想明确不能有简单的Job,请将基类抽象化:

    class Job {
        virtual bool isComplete() = 0;
        virtual void setComplete() = 0;
       //other functions
    };
    

    现在,您无法创建 Job 的实例:

    Job job; // compiler-error
    std::vector<Job> jobs; // compiler-error
    

    请注意,这些函数现在是虚拟的,这意味着子类可以覆盖它们。 = 0 和 end 意味着子类必须提供这些函数的实现(它们是 pure virtual member functions)。

    其次,您的班级SongJob 有一个成员std::vector&lt;Job&gt;。这几乎肯定不是你想要的。如果你给这个向量添加一个SongJob,它就会变成一个普通的Job。这种效果称为slicing。为了防止它,您必须将其设为std::vector&lt;Job*&gt;

    这里还有很多话要说,但那会很远。我建议你找一个好的book

    【讨论】:

    • 所以如果 SonjJob 有一个容器来存储歌曲和 VideoJob 一个来存储视频,我应该把这些容器也放在基类中吗?我认为派生类不会留下任何数据:)
    • 我知道 oop 的细节,我的问题是我需要用 c++ 和 java 来做这个,所以我需要用两种语言来思考 :) 但你的解释很好。谢谢
    • @vBx:不,您可以将这些容器放入类本身。它们是不同的,因为一个存储only 视频,而另一个存储only 歌曲。这不是共性,而是不同,所以不属于基类。
    • 好的,我做了一个基本函数 add() 并在 SongJob 中添加到歌曲容器中,在 VideoJob 中我添加到视频容器中,我想这是对的
    • @vBx:只有在SongJob 在其VideoJobv 中有VideoJob 实例的情况下才可以。如果您对此不满意,您将不得不在SongJobVideoJob 上创建非多态Add 方法,并且您将永远无法从基本类型访问Add。但是您仍然可以在基本类型上创建一个只读的 GetAllV 方法...
    【解决方案2】:

    在您的基类Job 中,您可以将这些方法添加为virtual methods,以便从Job 派生的类可能会或可能不会覆盖这些特定方法。

    在您的 SongJob 类中,您覆盖方法并且不要在 VideoJob 中覆盖它们

    在,void process() 传递一个指向基类Job 的指针

    void process(Job *s) 
    

    然后它将根据 objec 指向的地址调用适当的方法,该地址将是 SongJob 对象。

    【讨论】:

      【解决方案3】:

      在 C++ 中,您必须做两件事才能使多态性起作用:

      • 通过对基类型的引用 (&amp;) 或指针 (*) 访问多态函数
      • 在基类型中将多态函数定义为virtual

      所以,把这些改成:

      class Job {
        bool isComplete() {}
        void setComplete() {}
      };
      
      void process(SongJob s)
      {
        // ...
      }
      

      收件人:

      class Job {
        public: // You forgot this...
        virtual bool isComplete() { }
        virtual void setComplete() { }
      };
      
      void process(Job& s)
      {
        // ...
      }
      

      如果您无法在基类的process 中定义您需要的所有功能(如果您想要的所有成员函数都不适用于所有派生类型),那么您需要转至@987654329 @ 到 Job 的成员函数中,并将其设为虚拟:

      class Job {
        public:
        virtual bool isComplete() { }
        virtual void setComplete() { }
        virtual void process() = 0;
      };
      
      // ...
      
      int main(int argc, char* argv[])
      {
          SongJob sj;
          Job& jobByRef = sj;
          Job* jobByPointer = new SongJob();
      
          // These call the derived implementation of process, on SongJob
          jobByRef.process();
          jobByPointer->process();
      
          delete jobByPointer;
          jobByPointer = new VideoJob();
      
          // This calls the derived implementation of process, on VideoJob
          jobByPointer->process();
      
          return 0;
      }
      

      当然,process 有两种不同的实现方式。每个班级类型一个。

      人们会告诉你各种各样的“is-a”与“has-a”的东西,以及关于这个愚蠢的“多态性”的各种复杂的东西;他们是正确的。

      但这基本上是多态性的重点,从实用的意义上说:这样你就不必在调用函数之前检查每个类的类型。你可以只调用基类型的函数,最后会调用正确的派生实现。

      顺便说一句,在 C++ 中,virtual ... someFunc(...) = 0; 表示中定义的函数的类型无法实例化,而必须在派生类中实现。它被称为“pure virtual”函数,它所定义的类变成了“abstract”。

      【讨论】:

        【解决方案4】:

        您的问题来自您在对象上调用 process 方法的事实。您应该在 Job 类上有一个 Process 方法,并在派生类中覆盖此方法。

        【讨论】:

        • 好吧,这就是我所做的,但是我需要使用 typeid 因为一个派生对象有一个 hasNewTrack() 而另一个派生对象有 hasnewClip() 所以基类对象没有任何这个,所以我得到错误
        • Job.Process 可以是纯虚拟的(没有实现)。那么SongJob.Process会调用hasNewTrack,但不会调用hasNewClip,VideoJob.Process会调用hasNewClip,但不会调用hasNewTrack。
        【解决方案5】:

        使用纯虚函数:

        class Job
        {
        virtual string getArtist() =0;
        };
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-08-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多