3
votes

I have abstract class Number with 4 pure virtual operators (+,-,/,*). I want to make two derived classes Integer and Real and override there these operators. I don't properly understand how to declare operators. I haven't found any examples in the net similar to my situation.

My questions are: In abstract base class Number my operators must return reference Number& or pointer Number*, since I can't return abstract class itself. But what should I pass as arguments ? Number& or Number* too, but I need to store number in class, should I have in my base class something like void* num? Let's imagine, I have

class Number {
    ...
    virtual Number& operator+(const Number&) = 0;
};

How should I override this operator in derived class Integer?

1
Suppose you had that class hierarchy, could you give an example how you would use it? It's not clear what you really want to get in the end.Evg
@Evg hm, something like simple custom int class.Tovarisch
Not sure if you're on the right way with your design... You will need to be able to add Integers and Reals this way – but unless you have some common representation for numbers, you'll need to know about the type... On the other hand, if you had such common representation, then why not already use in Number base class?Aconcagua
" I don't really need atm to add real+int only real+real, int+int" then why bother about virtual methods or inheritance in the first place? You dont need Integer and Real to inherit from a base class in order to enable Integer + Integer and Real + Real463035818_is_not_a_number

1 Answers

5
votes

I know, but my tutor insist on doing it as overriding pure virtual operator as exercising in abstract classes, but I really don't get it.

Well, you can learn about two things from this:

  1. How virtual functions work in general (and a custom operator is just nothing else than an ordinary function, solely calling syntax differs).
  2. Virtual functions aren't the Holy Grail to solve anything.

The problem is that the classes Integer and Real most likely have differing internal representation – so you won't be able to do the addition/multiplication/... without knowing about the concrete type you received as second operand. Additionally, it is pretty unclear what the return type of mixed operand types should be.

I don't really need atm to add real+int only real+real, int+int

Well, OK, we can catch this:

class Number
{
public:
    virtual ~Number() { } // in any case, a virtual base class should have a virtual destructor!

    // fine so far:
    virtual Number& operator+(Number const&) const = 0;
    // we are not going to change the          ^
    // own instance, so we'd rather have this operator const
    // (in contrast to operator+=)
};

class Integer : public Number
{
public:
    Integer& operator+(Number const& other) const override
    // ^ co-variant return type, so that's fine
    {
        // at very first, we make sure that we actually received
        // an instance of type Integer
        auto o = dynamic_cast<Integer const*>(&other);
        if(!o)
            // if not, throwing an exception is a good candidate
            // for error handling...
            throw std::invalid_argument("argument must be of type Integer");

        // now as we know it IS an Integer:
        return Integer(this->value + o->value); // or whatever...
        // OOPS - the returned object gets lost when the function is left...
    }
};

If you want to be able to add Reals, too, then you'd have another type cast. Assuming Integer + Real results in Real, you'd then have to change the return type back to Number, though.

However, yet a great problem contained: The returned object gets destroyed as soon as the function is left, so the reference returned is dangling.

We'll have to fix this by some means. References aren't suitable, though, so we might opt for a smart pointer:

class Number
{
    virtual std::unique_ptr<Number> operator+(Number const&) const = 0;
};

class Integer : public Number
{
    std::unique_ptr<Number> operator+(Number const& other) const override
    //                 ^
    // unfortunately, now, as ordinary struct, no co-variance possible any more
    {
        return std::make_unique<Integer>(this->value + o->value);
    }
};

This problem again illustrates how inappropriate the approach chosen actually is...