I have written a sample program.
If I print the address of pa and pb both are different.
Can you let me know why is this happening ?
#include<iostream>
using namespace std;
class A {
int x;
};
class B {
int y;
};
class C: public A, public B {
int z;
};
int main()
{
C c;
A *pa;
B *pb;
pa = &c;
pb = &c;
cout<<pa<<endl;
cout<<pb<<endl;
}
As Kerrek SB put it, pa and pb in your example don't actually point to c, but rather to the A and B subobjects of c.
With multiple inheritance, the data from the base classes is essentially stacked one after another. Base-typed pointers are simply offset to the data for that base class. Because of this, pa and pb point at different offsets into c.
#include<iostream>
using namespace std;
class A {
public:
int x;
};
class B {
public:
int y;
};
class C: public A, public B {
public:
int z;
};
int main()
{
C c;
cout << " &c: " << &c << endl << endl;
cout << "(A*)&c: " << (A*)&c << endl;
cout << "(B*)&c: " << (B*)&c << endl << endl;
cout << " &c.x: " << &c.x << endl;
cout << " &c.y: " << &c.y << endl;
cout << " &c.z: " << &c.z << endl << endl;
}
Result:
&c: 0x7ffdfeb26b20
(A*)&c: 0x7ffdfeb26b20
(B*)&c: 0x7ffdfeb26b24
&c.x: 0x7ffdfeb26b20
&c.y: 0x7ffdfeb26b24
&c.z: 0x7ffdfeb26b28
So you can see that C is laid out like this:
---------------
0x7ffdfeb26b20 | x | class A data
---------------
0x7ffdfeb26b24 | y | class B data
---------------
0x7ffdfeb26b28 | z | class C data
---------------
If you add some virtual methods to this example, you'll see that the same things happens with the subclass vtables.
--------- <------pa = &c
| x |
--------- <------pb = &c
| y |
---------
| z |
---------
it depends on the memory model of the class, above is an object of class C in memory
Back in the good old days of plain c, there were just structs.
struct Base{
int baseMember;
}
struct Child{
struct Base parent;
int someMoreMembers;
}
this way, dereferencing a pointer to a Child as if it were a base pointer, would result in a perfectly good pointer to the Base, because it is the first member.
In c++ this still works the same way (usually). But if you now inherit two classes, you cannot put them both as first member in your struct. So C++ converts a pointer to a base into a pointer to the child by adding the offset the specific base data is at.
Related
I'm a brand new in C++ and I'm experimenting with Polymorphism. I have the following code:
#include <iostream>
class Base1
{
protected:
int b1;
public:
int m() { return 1; }
};
class Base2
{
protected:
int b2;
public:
int n() { return 2; }
};
class Der : public Base1, public Base2
{
protected:
int d1;
public:
int m() { return 11; }
int n() { return 21; }
};
int main()
{
Der *ptr = new Der();
Base1 *b1 = ptr;
Base2 *b2 = ptr;
std::cout << "d: " << ptr << ", m: " << ptr->m() << ", n: " << ptr->n() << "\n";
std::cout << "b1: " << b1 << ", m: " << b1->m() << "\n";
std::cout << "b2: " << b2 << ", n: " << b2->n() << "\n";
delete ptr;
return 0;
}
When i run this code the interesting thing is that b2 is shifted by 4 bytes, here my output:
d: 0x564eab6cbeb0, m: 11, n: 21
b1: 0x564eab6cbeb0, m: 1
b2: 0x564eab6cbeb4, n: 2
Why this happens only with b2? I guess it's related on how things are stored in memory because if I remove the int field in b1 b2 is not affected. Is there a way to see easily stack and heap? I would like to see what happens. (I'm using Virtual Studio Code)
The sample of OP (simplified a bit)
struct Base1 {
int b1;
};
struct Base2 {
int b2;
};
struct Der: Base1, Base2 { };
may result in the following memory layout:
// start of Der
// start of Base1
0000: Base1::b1 // type int
// start of Base2
0004: Base2::b2 // type int
So, when struct Der is instanced part of its contents is an instance of struct Base2 but it doesn't start at the same address of the Der instance.
With
Der *ptr = new Der();
the initialization
Base2 *b2 = ptr;
doesn't result in a plain copy of the address in ptr to b2. There is also an implicit conversion from Der* to Base2* involved. The compiler is aware of the relation of classes Der and Base2. Hence, the conversion results in a silent addition of the offset of Base2 in Der.
To show this in action, I made a small demo. (I'm not sure how convincing it is.):
#include <iostream>
struct Base1 {
int b1 = 1;
};
struct Base2 {
int b2 = 2;
};
struct Der: Base1, Base2 { };
int main()
{
Der *ptr = new Der;
Base2 *b2;
std::cout << "ptr:" << ptr << ", ptr1->b2: " << ptr->b2 << '\n';
b2 = ptr;
std::cout << "b2: " << b2 << ", b2->b2: " << b2->b2 << '\n';
}
Compiled with gcc 4.1.2, you can find the following code where the actual assignment happens:
mov %rax, QWORD PTR [%rbp-24] # %rbp-24 <- storage of ptr on stack
add %rax, 4
mov QWORD PTR [%rbp-32], %rax # %rbp-32 <- storage of b2 on stack
Live Demo on CompilerExplorer
Note:
Compiling with most modern compiler version will emit a similar add command but also a lot of other stuff which is not as easy to decipher (by eyes) than the generated code of the older version. Hence, I've chosen the oldest gcc I could find.
Yes, it's related on how things are stored in memory.
Class Der includes Base1 and Base2 as a subobject.
Look this derived_class.
I'm new to c++ and have been experimenting with virtual inheritance. But there is something that really confuses me.
#include <iostream>
using namespace std;
struct A {int m = 5005;};
struct B : A {};
struct C : virtual B {};
struct D : virtual B {int m = 6006;};
struct E : C, D {};
int main () {
E e;
e.m = 303;
cout << "e.A::m = " << e.A::m << endl;
cout << "e.D::m = " << e.D::m << endl;
cout << "e.m = " << e.m << endl;
}
The output of this is:
e.A::m = 5005
e.D::m = 303
e.m = 303
Now, what confuses me is e.D::m = 303. Shouldn't it be 6006? I know there are already quite a few questions to virtual inheritance but no one really explained why this is happening.
I also think I've found another program that shows the same "problem".
#include <iostream>
using namespace std;
struct S {int m = 101;};
struct A : virtual S {int m = 202;};
struct B : virtual S {int m = 303;};
struct C : virtual A, virtual B {int m = 404;};
struct D : C {};
struct E : virtual A, virtual B, D {};
int main () {
E e;
e.m = 10;
cout << "e.S::m = " << e.S::m << endl;
cout << "e.A::m = " << e.A::m << endl;
cout << "e.B::m = " << e.B::m << endl;
cout << "e.C::m = " << e.C::m << endl;
cout << "e.m = " << e.m << endl;
}
Where the output is
e.S::m = 101
e.A::m = 202
e.B::m = 303
e.C::m = 10
e.m = 10
And also here e.C::m=10 confuses me. Can someone please explain what's going on here? I acually thought I understood the principle of virtual inheritance.
I think the paragraph here explains this behavior:
Either way, when examining the bases from which the class is derived, the following rules, sometime referred to as dominance in virtual inheritance, are followed:
A lookup set is constructed, which consists of the declarations and
the subobjects in which these declarations were found.
Using-declarations are replaced by the members they represent and type
declarations, including injected-class-names are replaced by the types
they represent. If C is the class in whose scope the name was used, C
is examined first. If the list of declarations in C is empty, lookup
set is built for each of its direct bases Bi (recursively applying
these rules if Bi has its own bases). Once built, the lookup sets for
the direct bases are merged into the lookup set in C as follows
if the set of declarations in Bi is empty, it is discarded
if the lookup set of C built so far is empty, it is replaced by the lookup set of Bi
if every subobject in the lookup set of Bi is a base of at least one of
the subobjects already added to the lookup set of C, the lookup set of
Bi is discarded.
if every subobject already added to the lookup set of
C is a base of at least one subobject in the lookup set of Bi, then
the lookup set of C is discarded and replaced with the lookup set of
Bi
otherwise, if the declaration sets in Bi and in C are different,
the result is an ambiguous merge: the new lookup set of C has an
invalid declaration and a union of the subobjects ealier merged into C
and introduced from Bi. This invalid lookup set may not be an error if
it is discarded later.
otherwise, the new lookup set of C has the
shared declaration sets and the union of the subobjects ealier merged
into C and introduced from Bi
The example helps illustrate the logic here:
struct X { void f(); };
struct B1: virtual X { void f(); };
struct B2: virtual X {};
struct D : B1, B2 {
void foo() {
X::f(); // OK, calls X::f (qualified lookup)
f(); // OK, calls B1::f (unqualified lookup)
// C++11 rules: lookup set for f in D finds nothing, proceeds to bases
// lookup set for f in B1 finds B1::f, and is completed
// merge replaces the empty set, now lookup set for f in C has B1::f in B1
// lookup set for f in B2 finds nothing, proceeds to bases
// lookup for f in X finds X::f
// merge replaces the empty set, now lookup set for f in B2 has X::f in X
// merge into C finds that every subobject (X) in the lookup set in B2 is a base
// of every subobject (B1) already merged, so the B2 set is discareded
// C is left with just B1::f found in B1
// (if struct D : B2, B1 was used, then the last merge would *replace* C's
// so far merged X::f in X because every subobject already added to C (that is X)
// would be a base of at least one subobject in the new set (B1), the end
// result would be the same: lookup set in C holds just B1::f found in B1)
}
};
TL;DR: Because e.m = 303; is an unqualified lookup, the compiler will recursively look up the inheritance tree for matching declarations. In this case I think it would first find A::m, but would replace this with D::m after seeing that D has A as an indirect base class. So e.m ends up resolving to e.D::m.
I am answering you to your first question. You have just over write the value of m in your class D.
E e;
e.m = 303;//you just over write the value 6006, just comment out this line and check
cout << "e.A::m = " << e.A::m << endl;
cout << "e.D::m = " << e.D::m << endl;
cout << "e.m = " << e.m << endl;
Here B inherited A and B has been inherited to C and D virtually so only one copy of B will be available in E finally. So you can access data member m of class A through A::m and your e.m and e.D::m is accessing the same data member i.e. m of class D.
Now see below some interesting results:-
//e.m = 303;
cout << "e.A::m = " << e.A::m << endl;
cout << "e.D::m = " << e.D::m << endl;
cout << "e.m = " << e.m << endl;
e.m = 303;//over write D::m;
cout << "e.A::m = " << e.A::m << endl;
cout << "e.D::m = " << e.D::m << endl;
cout << "e.m = " << e.m << endl;
e.E::m = 101;//over write D::m
cout << "e.A::m = " << e.A::m << endl;
cout << "e.D::m = " << e.D::m << endl;
cout << "e.m = " << e.m << endl;
e.B::m = 202;//over write A::m through B
cout << "e.A::m = " << e.A::m << endl;
cout << "e.D::m = " << e.D::m << endl;
cout << "e.m = " << e.m << endl;
The virtual classes 'bubble up' to the top of the hierarchy, but they are considered after the non-virtual classes during lookup.
Example 1
struct E : C, D {}; becomes (virtual classes in round brackets):
E
C
D
(B) - bubbled up to E from other classes.
Next we look at what is in C and D:
E
C <-- continue lookup
(B) <-- will not continue to look for m here yet
D
(B) <-- will not continue to look for m here yet either
m <-- m found during unqualified lookup
(B) <-- may continue to look for m here, but already found it above
Example 2
struct E : virtual A, virtual B, D {}; becomes:
E
D - first non-virtual class at top level
(A)
(B)
(S) - bubbled up from other classes.
Next we look at what is in D:
E
D <-- continue lookup
C <-- continue lookup
m <-- m found during unqualified lookup
(A) <-- no further lookup, m already found
(B) <-- no further lookup, m already found
(S) <-- no further lookup, m already found
I have a mother class and a derived daughter class. I am trying to access the protected variable 'familystuff' in the derived class. Both ways that I am trying to access it aren't working. When I compile and run it, I get the following output:
5 3 1
1
Familie 32768
FOO 32767
class Mother
{
private:
int motherstuff;
protected:
int familystuff;
public:
int everyonesstuff;
void SetStuff(int a, int b, int c){
motherstuff = a;
familystuff = b;
everyonesstuff = c;
}
void Show(){
cout << motherstuff << " " << familystuff << " " <<everyonesstuff << endl;
}
};
class Daughter : public Mother
{
public:
Daughter()
{
a = familystuff + 1;
}
void Show(){
cout << "Familie " << a << endl;
}
int foo() { return familystuff;}
private:
int a;
};
int main(){
Mother myMum;
myMum.SetStuff(5,3,1);
myMum.Show();
cout << myMum.everyonesstuff << endl;
Daughter myDaughter;
myDaughter.Show();
cout << "FOO " << myDaughter.foo() << endl;
}
You don't have a clear concept in object oriented programming. When you create two objects, then they are completely different from each other. They do not interact with each other until they are forced.So,
myMum and myDaughter are seperate objects and they do not share the values of their variables.
The last two outputs are basically garbage values. You have not initialized myDaughter's familystuff
So, if you want to access protected members from derived class, you need to write the following :
int main()
{
Daughter myDaughter(5,3,1);
myDaughter.Show();
cout << "FOO " << myDaughter.foo() << endl;
}
Change the Daughter's constructor to the following :
Daughter(int x,int y,int z)
{
SetStuff(x,y,z);
a = familystuff + 1;
}
You will get the desired output!!
There are several things wrong here:
myDaughter and myMum are different objects. You imply some kind of relationship between them, but there is none.
Your code has undefined behaviour, because your Daughter constructor uses the uninitialised member variable familystuff in an addition operation.
You should initialise your data members like this:
Mother::Mother() : motherstuff(0), familystuff(0), everyonesstuff(0) {}
Daughter::Daugher() : a(familystuff + 1) {}
This just jumped into my head and I can't figure it out.
If I have a code like this:
struct A { char x[100]; };
struct B { int data; };
struct C : A, B {};
#include <iostream>
using namespace std;
B* get_me_some_stuff()
{
static int x = 0;
if (++x % 2 == 0)
return new B();
else
return new C();
}
int main()
{
B* x = get_me_some_stuff();
B* y = get_me_some_stuff();
x->data = 10;
y->data = 20;
cout << x->data << " " << y->data << endl;
return 0;
}
How does the compiler determine the memory location of the data member?
Your x and y pointers actually point to the B subobject of the B and C objects (using the most-derived type).
Example:
int main() {
C c;
B *pb = &c; // converting the pointer here actually adjusts the address
void *pvc = &c; // doesn't adjust, merely type conversion
void *pvb = pb; // doesn't adjust, merely type conversion
cout << boolalpha;
cout << (pvc == pvb) << '\n'; // prints false
cout << (&c == pb) << '\n'; // prints true!
// (&c is converted to a B* for the comparison)
C *pc = static_cast<C*>(pb); // this static_cast, which is UB if pb doesn't
// actually point to a C object, also adjusts the address
cout << (pc == &c) << '\n'; // prints true
return 0;
}
The moral of this story? Use static_cast from void* (not reinterpret_cast), and you can only convert from void to the exact same type that was originally used to convert to void:
reinterpret_cast<C*>(pvb) // undefined behavior
Try this:
C* x = new C();
B* y = x;
cout << x << " " << y << endl;
Output for me:
0x1ae2010 0x1ae2074
The act of casting from C* to B* (or in the other direction) includes the necessary pointer arithmetic.
In book named "Using C++" by Rob McGregor there is following example of using pointer-to-member operator
class mycls
{
public:
int member;
int *ptr;
};
void main()
{
mycls MyClass;
// Derive a pointer type to the non-pointer class member
int mycls::*member = &mycls::member;
MyClass.ptr = new int;
mycls* pMyClass = &MyClass;
pMyClass->*member = 5;
*MyClass.ptr = 10;
cout << "pMyClass->*member = " << pMyClass->*member << "\n"; // pMyClass->*member = 5
cout << "MyClass.*member = " << MyClass.*member << "\n"; // MyClass.*member = 5
cout << "*MyClass.ptr = " << *MyClass.ptr << "\n"; // *MyClass.ptr = 10
cout << "*pMyClass->ptr = " << *pMyClass->ptr << "\n"; // *pMyClass->ptr = 10
delete MyClass.ptr;
}
In this example I don't understand why member variable mycls::member becomes maybe a pointer after (guessing) this line of code:
int mycls::*member = &mycls::member;
What this does?
Suppose you had a local variable:
int member;
You could make a pointer to it with:
int *ptr = &member;
To get the pointer to member syntax, we just append mycls:: in the appropriate places:
int mycls::*member = &mycls::member;
It might be clearer with an example that shows how the pointer can switch between any members of the class that are of the correct type:
class C
{
public:
int a;
int b;
};
void main()
{
// make pointer to member, initially pointing to a
int C::*ptrToMember = &C::a;
C x;
C *ptrToObj = &x; // make pointer to object x
ptrToObj->*ptrToMember = 2; // store in a;
ptrToMember = &C::b; // change pointer to b
ptrToObj->*ptrToMember = 3; // store in b;
}
Note how we create the pointer to the member a before we've created an object of type C. It's only a pointer to a member, not a pointer to the member of a specific object. In the 'store' steps, we have to say which object as well as which member.
Update
In the comments the OP asked if this is the same:
int *ptr = &(ptrToObj->a);
No, it's not. That is a pointer to any int, anywhere in memory.
The easiest way to understand this is to think of what it means technically. A "pointer" is an absolute location in memory: where to find an object. A "pointer-to-member" is a relative location, sometimes called an offset: where to find an object within the storage of an outer object. Internally they are just numbers. A pointer-to-member has to be added to an ordinary pointer to make another pointer.
So if you have a pointer to an object (an int is an object!), you can use it to change what is stored at that absolute location in memory:
*ptr = 123;
But if you have a pointer-to-member, it is not a memory location. It is an offset, an amount to be added to a memory location. You cannot use it by itself. You must "add" it to an object pointer:
ptrToObj->*ptrToMember = 132;
This means: go to the location in memory ptrToObj, then move along by the distance ptrToMember.
He called the class member member and the pointer-to-member member, confusing the issue. Does renaming them like this help?
Class:
class SampleClass
{
public:
int m_data;
int* m_pointer;
};
Usage:
int main()
{
SampleClass sample;
// Derive a pointer type to the non-pointer class member
int SampleClass::*pointerToMember = &SampleClass::m_data;
sample.m_pointer = new int;
SampleClass* pSample = &sample;
pSample->*pointerToMember = 5;
*sample.m_pointer = 10;
// pSample->*pointerToMember = 5
cout << "pSample->*pointerToMember = "
<< pSample->*pointerToMember << "\n";
// sample.*pointerToMember = 5
cout << "sample.*pointerToMember = "
<< sample.*pointerToMember << "\n";
// *sample.m_pointer = 10
cout << "*sample.m_pointer = "
<< *sample.m_pointer << "\n";
// *pSample->m_pointer = 10
cout << "*pSample->m_pointer = "
<< *pSample->m_pointer << "\n";
delete sample.m_pointer;
}
[Code]
Edit: Re "I'm still currious whether int *ptr = &(ptrToObj->a); is the same as using pointer to a member like in the book":
It's not a pointer-to-member. It is just a normal pointer to memory that happens to be in an object.
You can see the syntax in use here:
Class:
class C
{
public:
int a;
int b;
};
Usage:
int main()
{
// make pointer to member, initially pointing to a
int C::*ptrToMember = &C::a;
C x = {10, 11};
C *ptrToObj = &x; // make pointer to object x
cout << "a initial value: " << ptrToObj->*ptrToMember << endl;
ptrToObj->*ptrToMember = 2; // store in a;
cout << "a after change: " << ptrToObj->*ptrToMember << endl;
ptrToMember = &C::b; // change pointer to b
cout << "b initial value: " << ptrToObj->*ptrToMember << endl;
ptrToObj->*ptrToMember = 3; // store in b;
cout << "b after change: " << ptrToObj->*ptrToMember << endl;
int* ptr = &(ptrToObj->a);
cout << "ptr to a: " << *ptr << endl;
ptr = &(ptrToObj->b);
cout << "ptr to b: " << *ptr << endl;
}
[Code]
The asterisk character in the declaration is part of the type.
Your declaration breaks down into
Type: int mycls::*
Variable name: member
Initialiser: = &mycls::member
This new variable member points is a member pointer to an int held in a mycls. It doesn't have to point to a mycls::member.