Polymorphism - in cplusplus tutorial

Hi guys,
I found below code when scanning the Tutorials and couldn't help wonder, isn't this a bit dangerous? Sure the code will work, no question about it, but...

1)
Main starts out with two objects: Rectangle rect; Triangle trgl;
both of which inherit the main class Polygon. In my world will the herby take a larger memory area than what Polygon does on it's own.

2)
Then the address of rect and triangle are set to a pointer Polygon* that must occupy a smaller memory area. The way I see it this means that unknown territory will be overwritten, or have I gotten this wrong?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  // pointers to base class
#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    void set_values (int a, int b)
      { width=a; height=b; }
};

class Rectangle: public Polygon {
  public:
    int area()
      { return width*height; }
};

class Triangle: public Polygon {
  public:
    int area()
      { return width*height/2; }
};

int main () {
  Rectangle rect;
  Triangle trgl;
  Polygon * ppoly1 = &rect;
  Polygon * ppoly2 = &trgl;
  ppoly1->set_values (4,5);
  ppoly2->set_values (4,5);
  cout << rect.area() << '\n';
  cout << trgl.area() << '\n';
  return 0;
}
Then the address of rect and triangle are set to a pointer Polygon* that must occupy a smaller memory area. The way I see it this means that unknown territory will be overwritten, or have I gotten this wrong?


You've got it wrong.

1
2
 Rectangle rect;
 Triangle trgl;

A rectangle object exists, taking up some memory. A Triangle object exists, taking up some memory.

Polygon * ppoly1
Now a pointer exists. That pointer takes up some memory. Probably 4 (or 8) bytes.

Did anything get overwritten? No.
What objects currently exist? A Triangle, a Rectangle, and a pointer.
Did any of those objects get created over the top of another object? No.

Now get the address of rect, &rect. This is a memory address. A single number. Something like 0x12341234

We put this number, this value 0x12341234 into the pointer we made. Before, that pointer had some value, and now it has a different value. That's it. We didn't write over the Rectangle, we got the address of the Rectangle and then we put that address in the pointer.
Last edited on

You've got it wrong. I think you've misunderstood things


1Rectangle rect;
2Triangle trgl;


A rectangle object exists, taking up some memory. A Triangle object exists, taking up some memory. Correct! Each one takes up the memory space of Polygon and Triangle

Polygon * ppoly1
Now a pointer exists. That pointer takes up some memory. Probably 4 (or 8) bytes. Correct, it points at the starting point of an area the size of the base class Polygon

Did anything get overwritten? No.
What objects currently exist? A Triangle, a Rectangle, and a pointer.
Did any of those objects get created over the top of another object? No. Correct again

Now get the address of rect, &rect. This is a memory address. A single number. Something like 0x12341234The start point from where the rect starts to consume memory, that's right

We put this number, this value 0x12341234 into the pointer we made. Before, that pointer had some value, and now it has a different value. That's it. We didn't write over the Rectangle, we got the address of the Rectangle and then we put that address in the pointer.
This is exactly my point "Before, that pointer had some value, and now it has a different value" The &rect holds the start point of a larger memory area then the pointer Polygon represents.

Polygon is a subset of Rectangle which is given by "class Rectangle: public Polygon"

Will the world come to an end now thanks to this? Probably not, but this is however something I wouldn't do simply because you will point out a larger object to a memory area that is actually smaller. It's like forcing down ones foot into a too small a shoe.

Using pointers is more than just passing start addresses in a neat way. The addresses are actually starting points to a memory area that is being specified by the pointer type.
The point of polymorphism is that a derived class extends the base class. I.e. all data of Polygon remain as they are within Triangle and hence you can acess those data via a pointer to Polygon without a problem.

The other way is not possible. You cannot have pointer to Triangle and assign a pointer to a Polygon object since the Polygon object does not have the extended data and accessing the extended data would lead to a crash.
@KentaH

Several points:

1. Moschops does know what he is talking about, I wouldn't be so keen to dismiss his extensive knowledge.

2. Consider using the quoting format button, so it is clear who said what.

3. IMO, you haven't understood this part, The very first sentence in that tutorial:

cplusplus polymorphism Tutorial wrote:
One of the key features of class inheritance is that a pointer to a derived class is type-compatible with a pointer to its base class. Polymorphism is the art of taking advantage of this simple but powerful and versatile feature.


This doesn't mean the derived pointer is converted to base pointer. In your example it is still actually a derived pointer, but it is type-compatible with a base pointer. This is demonstrated by the fact that ppoly1 and ppoly2 call the right function in their respective derived classes. This whole question is a common misconception.

Each class has an internal vtable of pointers, and this can be used to navigate up the inheritance tree in order to find a virtual function to execute. The idea here is to be able to have one virtual function that applies to all the derived classes underneath. This is so one can write the function once, rather than repeat the exact same function for every derived class. For example, this partial code:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Animal {};

class Bird : public Animal 
{
  virtual void fly(Bird* TheBird) {
     std::cout << "The " << TheBird->Name() << " is flying\n";
  }
};

// The Name() is defined in these classes
class Sparrow : public Bird {};
class Starling  : public Bird {};
class Pigeon  : public Bird {};


Now if one had a container of Bird pointers loaded with pointers to Sparrow, Starling, Pigeon etcetera, and the fly function was called for each one of them, the correct action is carried out.

KentaH wrote:
Polygon is a subset of Rectangle which is given by "class Rectangle: public Polygon"


IMO, you have that backwards. Polygon is the base class and represents the super set.

Hopefully this has cleared up these issues ? :+)
IMO, you have that backwards. Polygon is the base class and represents the super set.

While you're (obviously) right about everything else, I think you've misunderstood the terminology here.

Rectangle inherits from Polygon. That means that, in C++ terminology, Rectangle is the subclass.

However, from the point of view of set terminology, Rectangle contains all of Polygon, plus extra specialization. Therefore, in set terminology, Rectangle is a superset.

All of which is tangential to the main questions about polymorphism in this thread.
Last edited on
@MikeyBoy

I was thinking in terms of mathematical set theory, where the specialisation of a subset (has 4 sides with right angles) is not seen as something the superset does not have because Polygon has 3 or more sides with variable angles. But in C++, the specialisation in a derived class is something the base class does not have, so I can see how that makes it a superset. A little counter-intuitive, but I get it now - Cheers ++ThingsLearnt;


The &rect holds the start point of a larger memory area then the pointer Polygon represents.


I think you're saying that a Polygon pointer should ALWAYS "represent" only a Polygon object, just a base Polygon, not ever something derived from Polygon.

That is incorrect.

The pointer Polygon does NOT "represent" a base Polygon object only. The pointer Polygon "represents" ANYTHING derived from a Polygon type.

A Polygon pointer should point at something that is a Polygon. Is a Polygon a Polygon? Yes. So that's fine. Is a Rectangle a Polygon? Yes. So that's fine. Is a Triangle a Polygon? Yes. So that's fine.

The original concern was "unknown territory will be overwritten". What unknown territory? What lump of memory that was unknown will have been overwritten?

The addresses are actually starting points to a memory area that is being specified by the pointer type.
That's incorrect. The pointer does NOT specify how much memory the object being pointed to takes up. Take this very example. We have a Polygon pointer, pointing at a Rectangle. That's fine, because a Rectangle IS a Polygon. We could instead make that pointer point at a base Polygon object, which would also be fine. The pointer type does NOT tell you anything about how much memory the object it's pointing to occupies. It's got nothing to do with that.

Assuming that someone hasn't screwed with the type-safety, in this example code, when you see a Polygon pointer you know that whatever it is pointing to has the class function set_values.. and that's ALL you know. You don't know if it's a base Polygon, or a Rectangle, or a Triangle. The pointer doesn't know. This is how it works.

As an aside, analogies in programming are almost always unhelpful. Analogies about shoe sizes are completely irrelevant here. You seem to be suggesting that we're taking something big, and trying to fit it into a small space? We're not. At all. Anywhere. That idea doesn't apply to anything at all in this situation.



Is it possible for a bad programmer who thinks that a Polygon pointer will ONLY ever point to a base Polygon object to write bad code because the Polygon pointer is in fact pointing to a Rectangle? Yes. It's called "object slicing" and it's bad. Don't do it.
Last edited on
Maybe I've got this wrong or maybe it's been lost in translation somehow, I don't know and to be honest I don't care. In above is a number of statements slapped in my face, things that are kindergarten-basics for most people who have ever looked in a basic C++ tutorial. I must however say that that IdeasMan have a point here when he mention the vtable.
To me this forum sticks out a bit to much as a "mutual admiration society" for my taste. It become a kick-ass-competition in stead of a place to get knowledge. There's no way one can learn anything from this, I'm sorry to say.
You're absolutely right, basic facts do get explained very very basically around here. This is for two reasons.

Firstly, English is not everyone's first language, so simplicity helps.
Secondly, a lot of people carry very unfortunate misunderstandings about C++, and stating the simple fact clearly and simply is the best way to make those simple facts clear.

You're not willing to learn if it means you have to take a hit to your ego, which is of course fine. It's very common for people in this situation to blame other people; many programmers take a long time to let their ego go. You won't be missed. Many people learn a great deal here, and some people explicitly express thanks for the very simple wording used.
Last edited on
@KentaH

All I see in this thread is people trying to help explain things to you to make it clearer. I don't understand your comment about things being "slapped in [your] face". What is it you would prefer us to do? Omit facts? Leave explanations out?

I will also point out that you waded in being defensive in your second thread in this post, accusing others of misunderstanding you. If you feel people haven't been as helpful as you'd like them to be, perhaps you should look to your own behaviour?

We're just ordinary developers giving up our time and effort, free of charge, to help others who come here wanting to learn. We're not paid teachers, or anything. Bear that in mind.
Topic archived. No new replies allowed.