General questions about inheritance and polymorphism

brainstorming/designing for a programming project

Page 1 of 1

7 Replies - 2586 Views - Last Post: 22 November 2010 - 06:07 PM Rate Topic: -----

#1 lunixer  Icon User is offline

  • D.I.C Head

Reputation: 2
  • View blog
  • Posts: 120
  • Joined: 29-January 10

General questions about inheritance and polymorphism

Posted 21 November 2010 - 09:05 PM

Hello!

I have an assignment that I have to do where I am making an interface that allows a user to create three types of numbers: pairs (X, Y) complex, and rational. I have to give the user the ability to store these numbers in a Set data structure (no order, no repeats) with the ability to add two sets (which means that Sets must contain the same type of data), add a number to a set if it is of the correct type, etc. I am supposed to do this all with polymorphism and am not allowed to make the Set a template class.

So my questions are these. First, if I make the numbers inherit a superclass, such as MyNumber, and then design the Set to hold objects of type MyNumber, I will be able to instantiate them with items of any subclass, such as Pairs or Complex, correct?

But then, how do I prevent an item of a different type from being added to the Set? My professor is very keen on responsibility-driven design, so it should definitely be the responsibility of the Set to ensure that it only holds one type of number. But obviously I can't have the item return a bool or something to say whether or not it is a type, as that would go against the logic of OO programming and polymorphism. Or am I reading too much into this? The exact specs, as per the assignment sheet say,

"Design and implement a Set that stores objects of type MultiNumber. A ‘set’ is a collection of
entities that share some common properties. Elements in a Set are typically not ordered. A set may
not contain duplicates. Elements in the Set are stored using some aggregate data type, such as an array.
For this project, you will use dynamic memory management, i.e. elements will be stored in a dynamic
array. Complex and Rational are specializations of MultiNumber. Therefore, the user should be
able to manipulate Sets of Complex numbers, or Rational numbers. However, the user should not
be able to create Sets of MultiNumbers."

That last line is what is confusing me so much. Thanks for the help.

Is This A Good Question/Topic? 0
  • +

Replies To: General questions about inheritance and polymorphism

#2 r.stiltskin  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2032
  • View blog
  • Posts: 5,435
  • Joined: 27-December 05

Re: General questions about inheritance and polymorphism

Posted 21 November 2010 - 10:03 PM

So suppose the Set class has a public function like
static Set* createComplex();
which calls a private constructor and initializes an empty Set object to be a set of only Complex objects, and returns a pointer to that Set.

Functions that manipulate the set (adding, deleting, searching, etc.) can each have a test to verify that the arguments sent by the user are of the correct type, and there would be a version of each function for each of the derived classes of MultiNumber.

I think that would work, although I'd be interested to hear if anyone has a better suggestion.

This post has been edited by r.stiltskin: 21 November 2010 - 10:05 PM

Was This Post Helpful? 1
  • +
  • -

#3 Oler1s  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1397
  • View blog
  • Posts: 3,884
  • Joined: 04-June 09

Re: General questions about inheritance and polymorphism

Posted 22 November 2010 - 11:41 AM

First, let me comment on r.stiltskin's answer. It's not good. Here's why. What is the purpose of polymorphism? To be able to work with a type higher up in an inheritance tree. For example, to work with the notion of a fruit (instead of an apple, banana, orange, etc.). But individual objects resolve to their actual types. For example, you work with the notion of a fruit, but it resolves to an apple, banana, etc, even though you don't know about it.

The Set class stores some kind of numeric object. However, the point of polymorphism is that you only work with this abstract numeric concept. And the actual implementation is resolved through polymorphism. Unfortunately, if you start writing code like createComplex, where you know about the underlying types, you've just defeated the whole point of polymorphism. If you know about the underlying types, there's no need for polymorphism. You've hand written code for each possible variation anyway.

Let me put it another way. The variation should happen in the inheritance tree. Pairs are added one way. Complex another. Rational yet another. That's the idea of OO. However, r.stiltskin says to write the variations in the set class. Huh? But this is a common mistake.

Unfortunately, you need an explicit way to encode type, without directly enforcing that type in your Set class. Very awkward. There's a couple of ways to do this. Here's one idea. Specify an enumeration of known types. Your set class should store the type it allows. Furthermore, the number class should also store the type it is.

This allows you to enforce "type" constraint, without working with the underlying types directly.
Was This Post Helpful? 2
  • +
  • -

#4 r.stiltskin  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2032
  • View blog
  • Posts: 5,435
  • Joined: 27-December 05

Re: General questions about inheritance and polymorphism

Posted 22 November 2010 - 01:58 PM

View PostOler1s, on 22 November 2010 - 01:41 PM, said:

First, let me comment on r.stiltskin's answer...

First of all thanks for the constructive criticism. It reminded me of some things that I had forgotten since I studied polymorphism. But it also pointed out that I didn't make myself clear on a few points, so I'll try to explain my thoughts more clearly.

When I suggested the function createComplex, I didn't mean "create a complex number", I meant "create an empty set that will hold complex numbers". (I should have called it createComplexSet.) My reasoning was that the set constructor MUST know the actual type of object that the set will hold, because the set is only allowed to hold a single derived type (am I wrong about this?). The set class can accomplish this by having a set constructor that takes a ComplexNumber pointer as its argument, and another that takes a Pair pointer as its argument, and so on -- but maybe the user isn't ready to add an element yet but only wants to create an empty set. I suppose the user can call the constructor, sending a null pointer of the correct sub-type. Is that a better way to do this?


Quote

The variation should happen in the inheritance tree. Pairs are added one way. Complex another. Rational yet another. That's the idea of OO. However, r.stiltskin says to write the variations in the set class.

When I said "Functions that manipulate the set (adding, deleting, searching, etc.)", I didn't mean functions that manipulate the MultiNumbers, as in "complex1 + complex2", but rather functions that add elements to the set, delete elements from the set, and so on. Not that there would be a function called addCplxNumToSet(Complex *) and another called addPairToSet(Pair *). Instead, by "a ... version for each of the derived classes" I meant a function called addToSet(Complex *) and one called addToSet(Pair *), where the type of the argument supplied determines which function is called. Is this wrong?


Quote

Unfortunately, you need an explicit way to encode type, without directly enforcing that type in your Set class.
I don't understand what you mean here.
Was This Post Helpful? 0
  • +
  • -

#5 lunixer  Icon User is offline

  • D.I.C Head

Reputation: 2
  • View blog
  • Posts: 120
  • Joined: 29-January 10

Re: General questions about inheritance and polymorphism

Posted 22 November 2010 - 02:12 PM

View Postr.stiltskin, on 22 November 2010 - 02:58 PM, said:

View PostOler1s, on 22 November 2010 - 01:41 PM, said:

First, let me comment on r.stiltskin's answer...

First of all thanks for the constructive criticism. It reminded me of some things that I had forgotten since I studied polymorphism. But it also pointed out that I didn't make myself clear on a few points, so I'll try to explain my thoughts more clearly.

When I suggested the function createComplex, I didn't mean "create a complex number", I meant "create an empty set that will hold complex numbers". (I should have called it createComplexSet.) My reasoning was that the set constructor MUST know the actual type of object that the set will hold, because the set is only allowed to hold a single derived type (am I wrong about this?). The set class can accomplish this by having a set constructor that takes a ComplexNumber pointer as its argument, and another that takes a Pair pointer as its argument, and so on -- but maybe the user isn't ready to add an element yet but only wants to create an empty set. I suppose the user can call the constructor, sending a null pointer of the correct sub-type. Is that a better way to do this?


Quote

Unfortunately, you need an explicit way to encode type, without directly enforcing that type in your Set class.
I don't understand what you mean here.


I think that these two points are related. The problem with my entire assignment is that I am not allowed to use templates. And without templates there is no way to prevent a different type from being added (Pairs to a Complex Set) even after the set has been instantiated. I think that is what he meant by enforcing the type. So no, I don't think that you were correct above that a set can only hold a single derived type since I am writing the set to hold multinumbers, and all derived types have an is-a relationship with multinumber. At least that's how I'm reading my problem right now, but I only have practical template/polymorphism experience in Java.
Was This Post Helpful? 0
  • +
  • -

#6 r.stiltskin  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2032
  • View blog
  • Posts: 5,435
  • Joined: 27-December 05

Re: General questions about inheritance and polymorphism

Posted 22 November 2010 - 02:43 PM

I was thinking that part of the process of "initializing" a set would be setting some variable to identify its specific type, but didn't think about exactly how to do that. I suppose that's what Oler1s had in mind when he mentioned an enumeration. but I don't get what he meant by "without directly enforcing that type in your Set class". I think that once each set object is created, it must directly enforce its type. Am I wrong about that?

This post has been edited by r.stiltskin: 22 November 2010 - 03:02 PM

Was This Post Helpful? 0
  • +
  • -

#7 Oler1s  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 1397
  • View blog
  • Posts: 3,884
  • Joined: 04-June 09

Re: General questions about inheritance and polymorphism

Posted 22 November 2010 - 03:05 PM

Quote

My reasoning was that the set constructor MUST know the actual type of object that the set will hold, because the set is only allowed to hold a single derived type (am I wrong about this?)
As I said, there's a way to enforce type constraint without working with the underlying type. My last paragraphs in the post suggest one idea to provide type information without working with C++ type.

Quote

The set class can accomplish this by having a set constructor that takes a ComplexNumber pointer as its argument, and another that takes a Pair pointer as its argument, and so on
Which is precisely what I said not to do. Your set class now knows about types down the inheritance tree, even though the point of polymorphism is that you don't know such details.

Quote

Not that there would be a function called addCplxNumToSet(Complex *) and another called addPairToSet(Pair *). Instead, by "a ... version for each of the derived classes" I meant a function called addToSet(Complex *) and one called addToSet(Pair *)
So what you are saying is that instead of calling the function addCplxNumToSet, you call it addToSet. It's a name change, but the argument is still a pointer to Complex. In other words, you know about something down the inheritance tree, even you should not.

Quote

I don't understand what you mean here.
Here's a related example. Let's say you had to store in an array a bunch of fruits, with the restriction of only allowing red fruits. How would you do this?

The logical way to do this is have a parameter for any fruit, but check if the fruit is red, through a member function or variable that gets the color. Yes? You operate generically on all fruits, but resolve the implementation by checking for the color. And each fruit implementation, like apple, orange, banana, has a different color.

The problem here is exactly the same. You need to store a bunch of NumericalTypes . Under the condition that they all of one precise type. Like above, you should be operating generically on all NumericalTypes. There is a trap here. The information you want reflects type, so it's very easy to make a mistake and write different code paths for the different C++ types.

But that's precisely what you shouldn't do. Because there is one important principle in inheritance trees. That you can work on a base type without knowing about a derived type. That's an important property in OO. So to enforce different types, you have to create additional information. Just like you have additional information for color in a fruit, you need additional information for type in a NumericalType.

Do you see this? Think about this. The design issue I'm talking about is very commonly violated. But it should not be. If you don't, you're welcome to ignore what I said. But you will learn through experience. There is a very fundamental logical violation in what you propose.

This post has been edited by Oler1s: 22 November 2010 - 03:06 PM

Was This Post Helpful? 1
  • +
  • -

#8 r.stiltskin  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2032
  • View blog
  • Posts: 5,435
  • Joined: 27-December 05

Re: General questions about inheritance and polymorphism

Posted 22 November 2010 - 06:07 PM

Thank you. Clearly I have to do some reading about polymorphism. But here is a possible solution to the immediate question.

Presumably an empty set can be "generic" -- it has no members so one object of any type of MultiNumber can be added to it without violating any rules. So a default constructor can create an empty set, or a constructor which takes a MultiNumber (BASE class pointer argument) can create a set whose "type" is implicitly the class of the DERIVED class object pointed to by that pointer. Subsequent "proposed members" to the set (also passed in using BASE class pointers) can be validated before adding them to the set by comparing their typeid with that of any current member of the set. I think this will work as long as the MultiNumbers are polymorphic.

#include <iostream>
#include <typeinfo>
using namespace std;

class base {
  private:
    int n;
  
  public:
    base() : n(1) {}
    int getN() { return n; }
    virtual void greet() = 0;
};

class derived1 : public base {
  private:
    int o;
    
  public:
    derived1() : o(2) {}
    int getO() { return o; }
    void greet() {
        cout << "hello\n";
    }
};

class derived2 : public base {
    private:
        int p;

    public:
        derived2() : p(3) {}
        int getP() { return p; }
        void greet() {
            cout << "howdy\n";
        }
};

int main () {
  derived1 d1;
  derived2 d2, d3;
  base *BP1, *BP2, *BP3;
  BP1 = &d1;
  BP2 = &d2;
  BP3 = &d3;
  
//  cout << "o: " << d1.getO() << endl;
//  cout << "n: " << d1.getN() << endl;
  cout << boolalpha << ( typeid(*BP1)==typeid(base) ) << endl;
  cout << ( typeid(*BP1)==typeid(derived1) ) << endl;
  cout << ( typeid(*BP2)==typeid(derived2) ) << endl;
  cout << ( typeid(*BP1)==typeid(*BP2) ) << endl;
  cout << ( typeid(*BP2)==typeid(*BP3) ) << endl;
  
}


Was This Post Helpful? 0
  • +
  • -

Page 1 of 1