I was writing an operator== between two kinds of smart pointer and thought I should run a quick sanity check. I'm suprised by the result...
In the snippet below how is it that all variants of f and b end up with the same value?
struct Foo {
int x;
};
struct Bar : public Foo {
int y;
};
#include <iostream>
int main ()
{
Bar bar;
Foo * f = &bar;
Bar * b = &bar;
std :: cout << f << " " << b << " " << (f == b) << "\n";
void * fv = f;
void * bv = b;
std :: cout << fv << " " << bv << " " << (fv == bv) << "\n";
int fi = reinterpret_cast <int> (f);
int bi = reinterpret_cast <int> (b);
std :: cout << fi << " " << bi << " " << (fi == bi) << "\n";
}
Explanation: A base class pointer can point to a derived class object, but we can only access base class member or virtual functions using the base class pointer because object slicing happens when a derived class object is assigned to a base class object.
By default base class pointer can hold the address of base class as well as derived call we all know. But if we don't use virtual keyword in base class then the base class pointer will call methods based on the type of pointer not on based on the value it holding.
It is true that a pointer of one class can point to other class, but classes must be a base and derived class, then it is possible.
The base pointer is conventionally used to mark the start of a function's stack frame, or the area of the stack managed by that function. Local variables are stored below the base pointer and above the stack pointer.
About the only time that a base class object won't have the same address as its subclass object is when multiple inheritance is involved.
In the above example memory probably looks like this:
/ --------- \ / | x | > This is the Foo portion of bar This is the whole Bar object < --------- / \ | y | \ ---------
Both views of the object have the same starting point, so a pointer to either view will have the same value.
In multiple inheritance, things get more complicated. Say you have:
struct Foo1{ int x; };
struct Foo2{ int y; };
struct Bar : public Foo1, public Foo2 { int z; };
Bar bar;
Now the memory will have to be laid out something like this:
/ --------- \ / | x | > This is the Foo1 portion of bar / --------- / \ This is the whole Bar object < | y | > This is the Foo2 portion of bar \ --------- / \ | z | \ ---------
So &bar
and (Foo1*)&bar
will have the same value, while (Foo2*)&bar
will have a different value, since it the Foo2
portion of the object starts at a higher address.
This is the typical memory layout you will end up with:
Bar starts Foo starts x starts / ends Foo ends y starts / ends Bar ends
| | | | | | | |
Sice there is nothing between Bar start and Foo start, the offset of Foo can be zero, therefore the pointers for Bar, Foo and x might be identical.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With