1

To realize Polymorphism, we need to use a base class pointer to a derived class instance. Everything about polymorphism is good except, what if every derived class has one or several its own member function? If the base class pointer cannot access these derived class member function, then what is so convenient about polymorphism?

Below is an example. "shape" is a base class. "square" and "circle" are two derived classes.

class shape {
public:
    virtual void getArea()=0;
};

class square: public shape {
private:
    int edge;
public:
    square(){edge = 1;}
    virtual void getArea(){  //polymorphism
        cout << edge*edge << "\n";
    }
    void getNumberOfEdge(){  //a new member function
        cout << "4\n";
    }
};

class circle: public shape {
private:
    int radius;
public:
    circle(){radius = 1;}
    virtual void getArea(){  //polymorphism
        cout << 3*radius*radius << "\n";
    }
    void getCurvature(){     //a new member function
        cout << 1/radius << "\n";
    }
};


int main(){
    shape* arr[2] = {
        new square(),
        new circle()
    };
    arr[0]->getArea();
    arr[1]->getArea();
    arr[0]->getNumberOfEdge();  //compiler error
}

getArea() is a good example of realizing polymorphism. However accessing derived class member function gives me compiler error, which I understand why perfectly. But from a designing point of view, we do not want to add a bunch of virtual functions to the base class just for the sake of each derived class, right?

3
  • 2
    Sure, it's not great if you are trying to mix different things (square and circle) but if you have a bunch of similar things (all different types of polygons) then it works really well. Polymorphism is just one tool in the programming tool box Commented Oct 3, 2019 at 21:37
  • But looks like for that purpose (all different types of polygons) I can just use one class "polygon" and have several instances, right? The whole point of having bunch of derived classes is that they all have their own characteristics, hence more members. Commented Oct 3, 2019 at 22:16
  • 1
    This is off topic but you shouldn't use raw C-style arrays polymorphically. Array indexing will use the size of the base class to move to the correct address but often the derived class may be larger than the base class. Better to contain an array of std::unique_ptr instead. Commented Oct 3, 2019 at 22:44

3 Answers 3

4

Functionality in the base class should be in the base class. Functionality that's specific to specific derived classes should be in those derived classes. The virtual functions let code manipulate shapes and perform operations that are valid for any shape without having to understand how to perform those functions on every possible type of shape that may exist now and in the future.

But from a designing point of view, we do not want to add a bunch of virtual functions to the base class just for the sake of each derived class, right?

If those operations make sense on the base class, then they should probably go there. If they are shape-specific, then they belong in the specific classes for which that functionality makes sense.

Say you have a system that uses shapes but has no derived class for octagon. The point of polymorphism is that code can be written today that will work perfectly on octagons later should someone add them.

Sign up to request clarification or add additional context in comments.

Comments

1

With the proper types you can work the number of edge problems quite well, taking polymorphism to the edge!

Let's add a class:

// a base interface for all Edged shapes
struct HasEdges {
   virtual void getNumberOfEdges() const = 0;
    // side note: I'd prefer this getter to return an int
    // but keeping it as in the example
};

Now for Square (but not for Circle!) we can do:

class Square: public Shape, public HasEdges {
// ...
public:
    void getNumberOfEdges() const override { /* implement */ }
// ...
};

And the main would go like this:

int main(){
    shape* arr[2] = {
        new square(),
        new circle()
    };
    arr[0]->getArea();
    arr[1]->getArea();
    // some edgy thing below
    HasEdges* hasEdges = dynamic_cast<HasEdges*>(arr[0]);
    // above returns null on failure
    if(hasEdges) hasEdges->getNumberOfEdges();

    // releasing memory...
}

3 Comments

assuming above returns null on failure No need to assume. dynamic_cast will return nullptr when no conversion is possible.
Thanks for the answer. Why cannot I just cast arr[0] from shape to square?
because there might be another edgy shape, e.g. Triangle, and we want to avoid a list of if(cast-to-square-succeeds) ... else-if(cast-to-octagon-succeeds)
0

Run-time Polymorphism means polymorphic behavior at run time. The behavior defined by getArea() is changed at run time based on the instance it points to. So, you are mixing with polymorphism with static compiling problem. Base class does not have a member function name getNumberOfEdge() and you get compiler error.

If you want to have specific implementation called, why not make compiler see it and cast it?

public:
    virtual void getArea()=0;
    template<typename T>
    const T * get() {
        return dynamic_cast<T *>(this);
    }
};

Now you can cast it to whatever derived type:

auto square_instance = arr[0]->get<square>();
if (square_instance) {
    square_instance->getNumberOfEdge();
}

A different approach to run time polymorphism is suggested by Sean Parent. I liked it and trying this approach more often now.

1 Comment

the 'get' method should probably perform dynamic_cast as reinterpret_cast would never fail

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.