-3

Suppose in C++, if we have a base class Base and a child class derived:

class Base{
    protected:
         int x_base;
};

class Derived : public Base {
   public:
         int x_prot;
};

Than in the main we can create a pointer to the base class and let it point to a derived class object:

Base *b;
b=new Derived;

But this instead is not allowed:

Derived *b;
b=new Base;

I would have thought the opposite.

When we call Base* b we are saying to the code that b will be a pointer to an object to class Base. In the example, he will be ready to allocate space for x_base (but not for x_prot). So we should not be able to use the pointer of the base class to point to an object of the child class.

Where is the error in my reasoning?

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
Thomas
  • 301
  • 3
  • 18
  • 4
    every `Derived` is a `Base` but the reverse is not true. Seems like you could use a [c++ book](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) – 463035818_is_not_an_ai Aug 22 '19 at 15:10
  • 1
    What if you did `b->x_prot` in the second example? `Base` has no `x_prot`. It doesn't make sense to assign a `Base` to a `Derived *`. – François Andrieux Aug 22 '19 at 15:10
  • 2
    Pointers and space needed for class object are two completely unrelated topics. – Yksisarvinen Aug 22 '19 at 15:11
  • i fixed some formatting and fixed a typo "be a pointer to an object to class b" -> "be a pointer to an object to class Base" – 463035818_is_not_an_ai Aug 22 '19 at 15:13
  • 1
    note that naked `new` should be avoided, you need pointers (or references) for polymorphism but that is orthogonal to whether you dynamically allocate or not. Use `Base b; Base* p = &b;` if you want a raw pointer to a `Base` object (unless you really need dynamically allocated objects) – 463035818_is_not_an_ai Aug 22 '19 at 15:17
  • 1
    This is almost certainly a dupe, but it is not "too broad". – Omnifarious Aug 22 '19 at 15:57
  • Thanks for the comments. Just to be clear, I wanted to know *why* my argument was wrong (knowing that it was wrong) . I was not saying : "my argument is correct: disprove it". If you feel like, you can contribute in the discussion of the respective answers as well :) – Thomas Aug 22 '19 at 17:34

2 Answers2

4

When we call Base* b we are saying to the code that b will be a pointer to an object to class Base.

We are indeed saying that. And it is true! Every Derived object starts with a Base sub-object. That's the nature of inheritance. A Derived is a Base plus some additional stuff on the end.

It's perfectly fine to make a pointer that completely ignores the additional stuff on the end: that is what an upcast does.

In the example, he will be ready to allocate space for x_base (but not for x_prot)

Pointers don't allocate things. They point to things.

In fact, your reasoning is exactly why Derived* b=new Base; is broken - the thing that does create an object (i.e. new) is only creating a Base, not a Derived. To then say that b points to a thing that can be treated as a full Derived would simply be untrue. You're missing all the additional stuff on the end.

Where is the error in my reasoning?

I think that you are thinking of the pointers as the things that create the objects, but that is not true. They are things that point to objects that were created by something else (e.g. new).

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Really thanks a lot for your explanation. So, if this way of reasoning is true, when I do "Base * b = new Derived", I will have allocated memory on the heap for a Derived object ( with new ) and then I will be able to access only the members heredited by the class "b", because the pointer has only those attributes. Is this a correct reasoning ? The other members are "lost"? [ PS: analogous reason when allocating on the stack, with "Derived c; Base* b; b=&c", following the observation on the comments ] – Thomas Aug 22 '19 at 17:23
  • 1
    @Thomas: Yes, that is correct. However, do note that you can still do a `static_cast(b)` to "get them back" because there's still a perfectly valid `Derived` object out there somewhere in existence for you to play with (or `dynamic_cast`, though that's not so useful without `virtual` stuff going on, which is how we usually work around this "problem" we're talking about by using virtual dispatch on base class members). And yeah how you allocate doesn't really matter this is just about how pointers work. – Lightness Races in Orbit Aug 22 '19 at 17:41
  • Thanks! I know this is going a bit off topic, but what is than the advantage of doing "Base *b = new Derived" and not simply a plain "Base *b = new Base", since I can only access in any case Base attributes? I know that if I have a virtual function as a memeber of Base and a corresponding member of derived, I will overwrite the virtual function of the Base. But is this the only advantage? (it does not look such a big advantage to me... but I am trying to learn... ) – Thomas Aug 22 '19 at 17:50
  • @Thomas Consider when you have `Derived1` and `Derived2` and `Derived3` and don't know in advance which one you're going to make, so you need a common pointer type to "store" them for a while. Then you're probably looking at polymorphism and virtual dispatch to actually make that useful, but we're definitely getting into "read your book" territory now! – Lightness Races in Orbit Aug 22 '19 at 17:55
  • Thanks :) I ordered a c++ book :) ( for the moment I was following basic online courses )... but I learnt also much from this discussion (I think) – Thomas Aug 22 '19 at 18:01
3

Let's say you invited a plumber to your home for the next Tuesday. And only thing you know he is a base plumber. You also know that there are 2 types of plumbers - base one and extended. Base plumber can repair a pipe. Extended one can do what base one does and also can install a water heater as well. You do not know which one will come to your house base one or extended, so it is safe to assume he is base. So when guy arrives you can ask him to repair your pipe and you will be fine. But let's say you want your heater installed as well. You cannot ask this guy to install it as he came just as base plumber. So you have 2 options:

  1. You tell the guy - I do not care who you are or I've seen you doing your job somewhere else or some other way I realized so I assume you are an extended plumber. So I call you now extended and go install my heater. So if your assumption is right you will be fine. If you are wrong then guy may install water heater upside down, can brake it completely or even make your whole house to explode.

  2. You ask the guy: are you an extended plumber? If he says yes, then you tell him - install the heater. So you either would be fine or would not have the work done.

So back to your case - it is safe to treat extended plumber as base one, they both can repair a pipe. On another side it is not safe to assume that a plumber is extended so you either need a way to know that or ask him. First one is done by static_cast<> and the second by dynamic_cast<>. But it should be more clear now why converting derived class pointer (extended plumber) to base is safe but not the vice versa.

Slava
  • 43,454
  • 1
  • 47
  • 90