2

I have a base class Movie and some derived classes (FunnyMovie, DramaMovie, ClassicMovie). I need to store them in Binary Search Trees of Movies. Each movie has different sorting parameters. First I'm trying to sort the FunnyMovies. When I compare two FunnyMovie objects, the FunnyMovie equal operator function is called. But when I compare two Movie pointers containing funny movies, then the Movie equal operator function is called instead of the FunnyMovie equal operator function.

How can I make that the Movie pointers call the equal operator function of the (derived) object they are pointing to instead of the base class Movie function?

Here is the Movie Class:

#ifndef MOVIE_H
#define MOVIE_H
#include <sstream>
#include "RentableItem.h"
#include "Person.h"
using namespace std;

class Movie : public RentableItem
{
    friend ostream& operator<<(ostream &outStream, const Movie& movie);
public:
    Movie();
    Movie(int stock, string directorFirstName, string directorLastName, string title);
    ~Movie();

    string getTitle();
    Person& getDirector();
    char getMediaType();

    virtual bool operator==(const Movie& rhsMovie)const;
    virtual bool operator!=(const Movie& rhsMovie)const;
    virtual bool operator<(const Movie& rhsMovie) const;
    virtual bool operator>(const Movie& rhsMovie) const;
    virtual bool operator<=(const Movie& rhsMovie)const;
    virtual bool operator>=(const Movie& rhsMovie)const;

protected:
    Person* director;   // address
    string title;
    char mediaType;    // it is "D" for all movies as DVD.
};

#endif

Here is the Movie equal operator implementation:

#include "Movie.h"
using namespace std;

bool Movie::operator==(const Movie& rhsMovie)const
{
    return (*this->director == *rhsMovie.director && this->mediaType == rhsMovie.mediaType &&
        this->title == rhsMovie.title);
}

This is FunnyMovie class:

#ifndef FUNNYMOVIE_H
#define FUNNYMOVIE_H
#include "Movie.h"
using namespace std;

class FunnyMovie : public Movie
{
    friend ostream& operator<<(ostream &outStream, const FunnyMovie& movie);
public:
    FunnyMovie();
    FunnyMovie(int stock, string directorFirstName, string directorLastName,
        string title, int releaseYear);
    ~FunnyMovie();

    int getReleaseYear();
    bool operator==(const FunnyMovie& rhsMovie)const;
    bool operator!=(const FunnyMovie& rhsMovie)const;
    bool operator<(const FunnyMovie& rhsMovie)const;
    bool operator>(const FunnyMovie& rhsMovie)const;
    bool operator<=(const FunnyMovie& rhsMovie)const;
    bool operator>=(const FunnyMovie& rhsMovie)const;

private:
    int releaseYear;
};

#endif

Here is FunnyMovie implementation of equal operator:

#include "FunnyMovie.h"
using namespace std;

FunnyMovie::FunnyMovie()
{
    releaseYear = 0;
}

FunnyMovie::FunnyMovie(int stock, string directorFirstName, string  directorLastName,
    string title, int releaseYear) : Movie(stock, directorFirstName, directorLastName, 
    title)
{
    this->releaseYear = releaseYear;
}

FunnyMovie::~FunnyMovie()
{
}

bool FunnyMovie::operator==(const FunnyMovie& rhsMovie)const
{
    return (this->title == rhsMovie.title && this->releaseYear == rhsMovie.releaseYear);
}

I thought that maybe it isn't calling FunnyMovie method because they don't have the same parameters, so I changed

bool FunnyMovie::operator==(const FunnyMovie& rhsMovie)const
// to:
bool FunnyMovie::operator==(const Movie& rhsMovie)const

But then Movie objects don't have releaseYear, and releaseYear is essential for FunnyMovie sorting.

  • 2
    You should show the code that makes use of operators. – marom Dec 11 '15 at 21:01
  • you probably are trying to compare two concrete type movies. try comparing pointers/references of plymorphic container – ForeverStudent Dec 11 '15 at 21:01
  • 1
    Off topic: [`using namespace std;` is bad.](http://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice) Using it in a header is very bad. ` ~Movie();` should be declared virtual. – user4581301 Dec 11 '15 at 21:07

2 Answers2

3

For a virtual function to work on derived classes, it has to have the same signature as in the base class. So your operator==() must take a const Movie & argument.

I haven't tried, but what I'd try to do to make your matches more specific is to work with dynamic casts. Something like what follows.

// The "override" clause makes sure that the virtual function signature matches the one in the base class
bool FunnyMovie::operator==(const Movie &other) const override {
  bool result=false; // Presume different.

  FunnyMovie *fm_other=dynamic_cast<FunnyMovie *>(&other);
  if(fm_other){
    // Perform FunnyMovie comparison with ‘fm_other’ and set ‘result’.
  }
  else{
    // Perform generic Movie comparison with ‘other’ and set ‘result’.
  }

  return result;
}
Paulo1205
  • 918
  • 4
  • 9
1

To answer your question, the reason that:

Movie& a = PickAFunnyMovie();
Movie& b = PickAFunnyMovie();

if (a==b) printf("Same\n");

doesn't invoke FunnyMovie::operator ==is because you didn't override it in the derived class. Always prefer to use the override keyword when overriding - it means the compiler will warn you if you get it wrong. So, you absolutely must change the declaration in FunnyMovie to:

bool operator ==(const Movie& rhs) const;

So in the definition, you are going to have to write something like:

bool FunnyMovie::operator ==(const Movie& rhs) const
{
    auto pRhs = dynamic_cast<const FunnyMovie*>(&rhs);
    if (!pRhs) return false;  // Not a funny movie - must be different.

    // Now you can access `ReleaseYear` through `pRhs` 
}

For comparisons like operator <() you are going to have to decide how to sort different movies.

Actually, a better route is going to be to have:

bool operator <(const Movie& rhs) const
{
   const auto lhs_type = typeid(*this);
   const auto rhs_type = typeid(rhs);

   if (lhs_type == rhs_type)
       return cmp(rhs)
   else
       return lhs_type.before(rhs_type);
}

You do not override or hide or overload the comparison operators - you just use the base class implementations. The only function you have to overload is int cmp(const Movie&rhs) const which returns 0, -1, 1 as *this is respectively equal, less, or greater than rhs. The base class guarantees that rhs will be the same type as *this so you can just dynamic_cast the reference, and not have to check for cast failure.

Note that this technique will provide an ordering of different Movie types - if you want to pick a specific type, you will also need to give the base class a virtual getClassOrder function which returns a number the comparison operators can use to compare Movies of different types.