A Base Class pointer can point to a derived class object. Why is the vice-versa not true?
If I tell you I have a dog, you can safely assume that I have a pet.
If I tell you I have a pet, you don't know if that animal is a dog, it could be a cat or maybe even a giraffe. Without knowing some extra information you can't safely assume I have a dog.
similarly a derived object is a base class object (as it's a sub class), so it can be pointed to by a base class pointer. However, a base class object is not a derived class object so it can't be assigned to a derived class pointer.
(The creaking you will now hear is the analogy stretching)
Suppose you now want to buy me a gift for my pet.
In the first scenario you know it is a dog, you can buy me a leash, everyone is happy.
In the second scenario I haven't told you what my pet is so if you are going to buy me a gift anyway you need to know information I haven't told you (or just guess), you buy me a leash, if it turns out I really did have a dog everyone is happy.
However if I actually had a cat then we now know you made a bad assumption (cast) and have an unhappy cat on a leash (runtime error).
Why can't a derived class pointer point to a base class object without casting?
Edit: Re-reading your question and my answer leads me to say this at the top:
Your understanding of is a
in C++ (polymorphism, in general) is wrong.
A is B
means A has at least the properties of B, possibly more
, by definition.
This is compatible with your statements that a Dog
has a Pet
and that [the attributes of] a Pet is[are] a subset of [attributes] of Dog
.
It's a matter of definition of polymorphism and inheritance. The diagrams you draw are aligned with the in-memory representation of instances of Pet
and Dog
, but are misleading in the way you interpret them.
Pet* p = new Dog;
The pointer p
is defined to point to any Pet-compatible object, which in C++, is any subtype of Pet
(Note: Pet
is a subtype of itself by definition). The runtime is assured that, when the object behind p
is accessed, it will contain whatever a Pet
is expected to contain, and possibly more. The "possibly more" part is the Dog
in your diagram. The way you draw your diagram lends to a misleading interpretation.
Think of the layout of class-specific members in memory:
Pet: [pet data]
Dog: [pet data][dog data]
Cat: [pet data][cat data]
Now, whenever Pet *p
points to, is required to have the [pet data]
part, and optionally, anything else. From the above listing, Pet *p
may point to any of the three. As long you use Pet *p
to access the objects, you may only access the [pet data]
, because you don't know what, if anything, is afterwards. It's a contract that says This is at least a Pet, maybe more.
Whatever Dog *d
points to, must have the [pet data]
and [dog data]
. So the only object in memory it may point to, above, is the dog. Conversely, through Dog *d
, you may access both [pet data]
and [dog data]
. Similar for the Cat
.
Let's interpret the declarations you are confused about:
Pet* p = new Dog; // [1] - allowed!
Dog* d = new Pet; // [2] - not allowed without explicit casting!
My understanding is that 1 should not be allowed without warnings
because there is no way a pointer should be able to point to an object
of its superset's type (Dog object is a superset of Pet) simply
because Pet does not know anything about the new members that Dog
might have declared (the Dog - Pet subset in the diagram above).
The pointer p
expects to find [pet data]
at the location it points to. Since the right-hand-side is a Dog
, and every Dog
object has [pet data]
in front of its [dog data]
, pointing to an object of type Dog
is perfectly okay.
The compiler doesn't know what else is behind the pointer, and this is why you cannot access [dog data]
through p
.
The declaration is allowed because the presence of [pet data]
can be guaranteed by the compiler at compile-time. (this statement is obviously simplified from reality, to fit your problem description)
1 is equivalent of an int* trying to point to a double object!
There is no such subtype relationship between int and double, as is between Dog
and Pet
in C++. Try not to mix these into the discussion, because they are different: you cast between values of int and double ((int) double
is explicit, (double) int
is implicit), you cannot cast between pointers to them. Just forget this comparison.
As to [2]: the declaration states "d
points to an object that has [pet data]
and [dog data]
, possibly more." But you are allocating only [pet data]
, so the compiler tells you you cannot do this.
In fact, the compiler cannot guarantee whether this is okay and it refuses to compile. There are legitimate situations where the compiler refuses to compile, but you, the programmer, know better. That's what static_cast
and dynamic_cast
are for. The simplest example in our context is:
d = p; // won't compile
d = static_cast<Dog *>(p); // [3]
d = dynamic_cast<Dog *>(p); // [4]
[3] will succeed always and lead to possibly hard-to-track bugs if p
is not really a Dog
.
[4] will will return NULL
if p
is not really a Dog
.
I warmly suggest trying these casts out to see what you get. You should get garbage for [dog data]
from the static_cast
and a NULL
pointer for the dynamic_cast
, assuming RTTI is enabled.
Why a pointer to the base class can point an object of the child class?
When we call
Base* b
we are saying to the code thatb
will be a pointer to an object to classBase
.
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 forx_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
).
Any purpose of using base class pointers to derived class if virtual function is not used
Using a base class pointer or reference allows you to write functions that accept all derived classes of the base generically so you can call common non-virtual functions on them. Otherwise, you'd have to write the same function multiple times for the different derived classes, or use templates.
#include <iostream>
class base_t {
public:
void non_virtual() const { std::cout << "Hello, World!\n"; }
};
class derived_1_t : public base_t { };
class derived_2_t : public base_t { };
void do_something(const base_t& obj) {
obj.non_virtual();
}
int main() {
derived_1_t var1;
derived_2_t var2;
do_something(var1);
do_something(var2);
}
Can we convert a base class pointer to a derived class pointer without knowing its real type?
The thing is: You need to know the type of each variable at compile time. Hence you cannot have a magical function f
that takes an A* a
and returns either B*
or C*
based on the runtime information of the actual type of the object pointed to by a
. Even for the the following example the function B::f
will be called but the return type will be of type A*
since this is determined at compile time.
#include <iostream>
struct A {
virtual A* get_ptr() { std::cout << "A!\n"; return this; }
virtual ~A() {}
};
struct B: A {
B* get_ptr() { std::cout << "B!\n"; return this; }
};
void f(A*) { std::cout << "But here A!\n";}
void f(B*) { std::cout << "But here B!\n";}
int main() {
B b;
A* a = &b;
f(a->get_ptr());
}
Will print B!
(since it is virtual) and then call the A*
(since the compiler assumes that a->get_ptr()
returns a A*
) overload.
What you can do is:
- Switch on an enum that you store in the object that corresponds to its type.
- Trial and error with
dynamic_cast
.
Addendum: You will probably not need to cast a pointer to the object you have to the actual type of the object, since you can (as done above) just call the virtual functions and those will call the functions belonging to the actual type of your object. Hence, when you think that you need to do the cast as you described, think again, you probably will not need it.
If you still think you need it, reconsider your design decisions, you are probably doing something very weird.
Through base class's pointer we can't access derived class specific members
Assigning a Derived class object to Base class pointer is valid and the cornerstone of polymorphism.
Yes, the Derived class may have additional members which cannot be accessed via a base class pointer, which is okay because you, the programmer are doing such an assignment while being fully aware that you cannot access those members. You are doing it purely for the purpose of polymorphism. Which means you will only be calling virtual functions which will use the vtables to call the correct override based on which object the pointer is pointing to.
However, casting a base class pointer back to a derived class pointer is not straightforward. You need type information and it is only possible if the Base class is polymorphic (ie. has virtual functions) and the class you are casting to is derived from the Base class. This exact check is performed by dynamic_cast
. dynamic_cast
checks if the downcast is possible and returns the Derived class pointer if it is valid. Otherwise it returns nullptr
.
Coming to your final question:DerivedClass* objBaseclassC = new BaseClass();
is not valid because accessing any "extra" members in DerivedClass results in a crash.
But when you doBaseClass* derivedPointer = new DerivedClass();
Such a construction will result in a compile time error.
Why does base class pointer always point to base class even if it holds a derived class object address?
An object of a derived class always contains an object of base class type somewhere inside it. This is called a "base class subobject". If p
has type base*
, then when p
is set to point to an object of type derived
, it is actually set to point to the base
subobject of that derived
object.
This behaviour ensures that base class non-static member functions can be called from pointers even when they actually point to an object of the derived class. Because of this, the implementation doesn't have to look up, at runtime, whether p
points to a complete base
object or a complete derived
object; no matter what, it's still pointing to a base
object (which might simply happen to be a base class subobject).
In order to ensure that the derived class's implementation of the function is called even though you used a pointer to base class, the compiler has to generate additional code to look up, at runtime, whether the pointer points to a complete base
object or a complete derived
object. If you want to force it to do this, you mark the function virtual
. If not, then C++ does not force you to pay the cost for virtual calls when you don't need them.
Type of Base class pointer pointing to derived class object
The code should be like this:
class A
{};
class B:public A
{};
int main()
{
A* a= new B();
cout<<typeid(a).name()<<endl;
}
output: class A* .
Because type implies the type of the pointer ( which is base*), not the type of the object to which it points.
Related Topics
Why Does Cudamalloc() Use Pointer to Pointer
What's Faster, Iterating an Stl Vector with Vector::Iterator or with At()
Case Insensitive Std::String.Find()
How to Find the Actual Path Found by Bfs
Random Array Generation with No Duplicates
"The System Cannot Find the File Specified" When Running C++ Program
How to Get a Random Element from a C++ Container
How to Find the Size of an Int[]
A How to Create a Matrix in C++
Number of Combinations (N Choose R) in C++
Measure Execution Time in C++ Openmp Code
How to Convert a Reverse Iterator to a Forward Iterator
C++ Sort Keeping Track of Indices
What Does the Standard Say About How Calling Clear on a Vector Changes the Capacity