Is "new" required [inheritance]

Hello,

I've got a very basic question for which I couldn't find an answer on google:
Let's say I've got these two classes:

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

class Derived : public Base {

};

int main() {
    Base* foo = Derived();
    Base bar = Derived();
    
    Base* meh = new Derived();
    Base nah = new Derived();
}


Why does only meh work?


Also:

I noticed that by inserting a object (by value) into a map the deconstructor gets called 2 times. I know it needs to be copied but why 2?

Thanks in advance!
closed account (Dy7SLyTq)
i think it has to be a pointer to use new. idk about the second. how can you see that the deconstructor is called twice?
Adding a print to the deconstructor!

A more detailed answer to my first question would be appreciated!
closed account (Dy7SLyTq)
only pointers to objects can use new
The expression Derived() creates and default-constructs a temporary object of type Derived. That object can be used to copy-initialize a Derived or initialize a const lvalue reference or a non-const rvalue reference to Derived or to Base:

1
2
3
4
5
Derived d = Derived(); // or just Derived d;
const Derived& dr1 = Derived();
Derived&& dr2 = Derived();
const Base& br1 = Derived();
Base&& br2 = Derived();


It cannot be used to initialize Base* foo, because, well, it's not a pointer to anything, and it has no user-defined conversion to any pointer type.

It can be used to initialize Base bar because Base has a copy constructor from const Base&, and Derived works there. But, as Disch points out, you end up with an instance of Base, losing any special properties of Derived (object slicing)

On the other hand, new Derived (or "new Derived()" as you wrote probably under the influence of Java) creates and default-constructs a Derived object on heap, creates and initializes a pointer to it of type Derived*, and returns that pointer.

You can use a pointer to Derived to initialize Base* meh because there is an implicit conversion for pointers (any pointer to derived can be converted to pointer to its base).

You cannot use a pointer to initialize Base nah because Base has no constructor that takes a pointer to Derived.
Last edited on
Derived classes contain all information of their parent class, and then some. Therefore sizeof(Derived) will always be >= sizeof(Base).

A parent object ("bar" and "nah", in your example) only has sizeof(Base) bytes set aside for it in memory. This may not be large enough to hold all information necessary to represent a Derived object.

Therefore, if you attempt to assign a Derived to a Base object... you get what's called "object slicing", where the Derived-specific information of the object is "chopped off" because there isn't enough memory set aside in the Base object to contain all of it.


Creating the object with new sets aside as much space as is required for the object. A pointer to this set-aside space is then fed back.

A base pointer can safely point to this data because it is guaranteed to be at least big enough to have all the information required for a Base object. But it might also have information in addition to that which is Derived specific.


EDIT:

Elaborating further:

 
Base* foo = Derived();


This is a type error, because Derived() instantiates a temporary Derived object. You cannot assign an object to a pointer... they are fundamentally different types. A pointer is just an address whereas an object is the whole shebang.

 
Base bar = Derived();


This suffers from "object slicing". While syntactically legal... what's happening is you are creating a temporary Derived object, then assigning it to a Base object (bar).

In the process of assigning it, all Derived-specific information is "chopped off"/lost, and you're left with only the Base information (which is the only information 'bar' can hold... because it's a Base object and not a Derived object).

 
Base* meh = new Derived();


This is legal and correct. new Derived(); creates a nameless Derived object somewhere in heap memory, then gives back a pointer to that memory. Since a Derived object also contains Base information, "meh" can safely point to that Base information. The fact that there may also be additional information besides that Base information does not change that.

 
Base nah = new Derived();


This again is a type error. The use of new here means you get a pointer back.. which you are trying to assign to an object.


EDIT: crap I so got ninja'd
Last edited on
Ah okay I'm beginning to understand this.

Is the const required in line 4? Why?
What does Base&& stand for? A reference to a reference?

Also I still can't understand why foo doesn't work.


EDIT:
Thanks Disch! I'm migrating from years of Java and this is much more complicated that I thought!
Last edited on
@elfeck

You are wrong. There are two valid statements in this code snip

1
2
3
4
5
    Base* foo = Derived();
    Base bar = Derived();
    
    Base* meh = new Derived();
    Base nah = new Derived();


The first one (bolded) is valid because an object of a derived class is at the same time an object of the base class. The second one is valid by the same reason only there are used pointers.
Is the const required in line 4? Why?

only lvalue references to const can bind to rvalues (Derived() is an rvalue expression.. and before you ask "what?" http://en.cppreference.com/w/cpp/language/value_category )

What does Base&& stand for? A reference to a reference?

rvalue reference. C++ has two kinds of reference types (since C++11), but getting into the details of the type system would be slightly off-topic: the question is about why some initializations are allowed while others aren't.

Also I still can't understand why foo doesn't work.

The type of foo is "Pointer to base", the type of "Derived()" is Derived. There are no means to turn one into another.
Last edited on
Topic archived. No new replies allowed.