Search code examples
c++classexceptionpolymorphism

How to throw an exception if two different derived class objects from the same base class interact with each other?


I have an abstract base class(X) with A & B derived classes.

I have a class method in X that is inherited by both classes A & B.

I want the method to be able to throw an exception if two different derived class objects interact with each other.

Here is a contrived example:

class Food{
    public:
        int count;
        void combine(Food* a) {
            this->count += a->count;
        }
};


class Orange : Food {
    public:
        Orange(int x) {
            count = x;
        }
};

class Apple : Food{
    public:
        Apple(int x) {
            count = x;
        }
};


int main() {
    Food* basket_apples = new Apple(5);
    Food* basket_oranges = new Orange(4);
    Food* crate_oranges = new Orange(10);
    crate_oranges.combine(basket_oranges);//should work fine
    crate_oranges.combine(basket_apples); //should produce error
}

A solution I considered is to override the combine method in both derived classes but that violates DRY(Don't repeat yourself).

I want to know if there are any other options to solve this issue.


Solution

  • You can check that in the combine function :

    void combine(Food const& a) // You should pass your argument by const ref
    {
        assert(typeid(*this) == typeid(a)); // This checks ONLY IN DEBUG !
        this->count += a.count;
    }
    

    If you want to manage the error, use exception or return value :

    void combine(Food const& a)
    {
        if(typeid(*this) != typeid(a))
          throw std::runtime_error("Invalid combined types");
        this->count += a.count;
    }
    
    
    int combine(Food const& a)
    {
        if(typeid(*this) != typeid(a))
          return 1;
        this->count += a.count;
        return 0;
    }
    

    Maybe you should transfert the counter ?

    void combine(Food& a) // Passing by non-const reference
    {
        assert(typeid(*this) == typeid(a));
        this->count += a.count;
        a.count = 0;
    }
    

    Note: As said by user17732522, your member function combine should be virtual and the class should better manage the inheritence. I suggest this :

    class Food
    {
        protected:
            std::size_t count; // You want this value >= 0
        public:
            Food(std::size_t count_) : count(count_) {}
            Food(Food const& f) : count(f.count) {}
            virtual ~Food() {}
            virtual std::string name() const = 0;
            virtual void combine(Food const& a)
            {
                assert(typeid(a) == typeid(*this));
                this->count += a.count;
            }
    };
    
    
    class Orange : public Food
    {
        public:
            Orange(std::size_t x) : Food(x) {}
            Orange(Orange const& o) : Food(o) {}
            virtual ~Orange() {}
            virtual std::string name() const { return "orange"; }
    };
    
    class Apple : public Food
    {
        public:
            Apple(std::size_t x) : Food(x) {}
            Apple(Apple const& a) : Food(a) {}
            virtual ~Apple() {}
            virtual std::string name() const { return "apple"; }
    };