"this" pointing to different address

Pages: 12
Hello people, this is the first time that I'm posting here and I need some help.

I got a very weird situation. The output of this program:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
#include <vector>

using namespace std;

class Super1
{
// public:
// 	virtual void notify () = 0;
};

class Super2
{
	vector<Super1*> v;
public:
	void m (Super1* ptr) { cout << (this) << endl; }
};

class Sub : public Super1, public Super2
{
};

class Test : public Sub
{
	string s;
	int at1;
	int at2;
public:
	Test (const char* s) { this->s = s; }
	//void notify () {}
};

int main ()
{
	Test obj("DDD");
	
	cout << (&obj) << endl;
	obj.m (&obj);
	
	return ( 0 );
}


is, for example:
0xbfd0a2e8
0xbfd0a2e8
Nice. It's correct. Both addresses are equal.

But, if I uncomment those 3 commented lines, adding the polymorphic abstract method and its implementation in the "Test" sub-class, I get, for example, the output:
0xbffeb164
0xbffeb168
This is wrong! The address pointed by "this" is four bytes ahead the other.

Does anyone have an idea?

Thanks!

P.S.: Sorry about any english erros, I'm from Brazil!
It's not wrong. I try to explain.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Test
{
	Sub
	{
		Super1
		{
			// vtable
		}
		Super2
		{
			vector<Super1*> v;
		}
	}
	string s;
	int at1;
	int at2;
}


Test inherits from Sub so all the data members of Sub will be put at the start of Test. Same with Sub. Super1 doesn't have any data so Super2 will also be placed at the beginning of Sub (which is the beginning of Test). This means that Super2 and Test will be on the same memory location.

When you add the virtual function to Super1, a vtable is added to Super1. The size of the vtable is often the same as the size of a pointer. This means that Super2 will have to be placed after Super1 and that is why you see that the pointers are different.

Note that this is implementation details and is not guaranteed by the C++ standard.
It's the vtable.
http://en.m.wikipedia.org/wiki/Virtual_method_table
How the object is allocated in memory based on the compiler.
Thanks! I guess I understand. So, if I'm supposing that the "m" method could be called by a "Test" object passing the pointer of the object itself; and I would like do throw an exception in this case, I certainly should make the following comparison:
 
if ( ptr == ( (Super1*) this ) - 1 )

or
 
if ( ( (Super2*) ptr ) + 1 == this )

right?
Last edited on
Maybe
1
2
3
4
5
void m (Super1* ptr) { 
   if ( ptr == (Super1*)this){
       // do something 
   }
 }

EDIT: This won't work
Last edited on
EDIT: never mind
Last edited on
The compiler is not obligated to put the vtable pointer anywhere. It could be +1 or -1, it could be +50 if they felt like it.
Oh, I just saw that m is in Super2 and if is not a subclass of Super1 so that wont work. And +/- the pointer value might not work on a different compiler.
Thinking.
Thanks! With all these news I thought about something that was making sense and it worked. I just had to cast "Sub*" to both pointers!
 
if ( (Sub*) ptr == (Sub*) this )
Does this work? Because Super1 & Super2 are unrelated, they could never have the same address. And when you cast to Sub, can you get that to be true in the m function?
I think that's because Sub inherits from both Super1 & Super2, right?
An object of type Sub has an anonymous base class object of type Super1 and another anonymous base class object of type Super2. These are at two different objects at two different offsets within Sub; therefore the base class objects will have two different addresses.

Unless a base class is empty and the compiler applies the 'empty base class optimization'.

Just for fun, try this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>

template< std::size_t N > struct A : A<N-1>
{
    char c[N*4] ;
    A() { std::cout << "A<" << N << ">* this == " << this << '\n' ; }
} ;

template<> struct A<1>
{
    char c[4] ;
    A() { std::cout << "A<1>* this == " << this << '\n' ; }
} ;

template< std::size_t N > struct B : A<N-1>, B<N-1>
{
    char c[N*4] ;
    B() { std::cout << "B<" << N << ">* this == " << this << '\n' ; }
} ;

template<> struct B<1>
{
    char c[4] ;
    B() { std::cout << "B<1>* this == " << this << '\n' ; }
} ;

template< std::size_t N > struct C : A<N>, B<N>, C<N-1>
{
    char c[N*4] ;
    C() { std::cout << "C<" << N << ">* this == " << this << '\n' ; }
} ;

template<> struct C<1> : A<1>, B<1>
{
    char c[4] ;
    C() { std::cout << "C<1>* this == " << this << '\n' ; }
} ;

int main(void)
{
    C<8> cc ;
    std::cout << "size: " << sizeof(cc) << '\n' ;
}




Last edited on
A<1>* this == 0x7fff5fbff340
A<2>* this == 0x7fff5fbff340
A<3>* this == 0x7fff5fbff340
A<4>* this == 0x7fff5fbff340
A<5>* this == 0x7fff5fbff340
A<6>* this == 0x7fff5fbff340
A<7>* this == 0x7fff5fbff340
A<8>* this == 0x7fff5fbff340
A<1>* this == 0x7fff5fbff3d0

Why are the first 8 As are the same address?
EDIT: Because the lowest base class is the address for all other classes derived classes?
Like in the example above C<8>'s address is 0x7fff5fbff340.
Very interesting code. Thanks for the example.
Last edited on
The anonymous base class sub-object can be at an offset of zero bytes within the derived class object.

In fact, every self-respecting compiler will try to place a base class sub object at a zero offset - pointer/reference conversions are then trivial and incur no run-time overhead (check for nullptr, if not null add/subtract the offset).

If there is more than one base class, only one of the base class sub-objects can have an offset of zero.

Somewhat dated, but still well worth a read:
http://www.amazon.com/Inside-Object-Model-Stanley-Lippman/dp/0201834545


matheuscscp, avoid c-style cast in C++.
Here you should prefer:
 
if ( static_cast<Super2*>(ptr) == this )


static_cast job is to convert types compatibles, for example an int to a float or a pointer to another pointer of the same hierarchy. It will do all the offset stuff for you.
C-style cast might work but better use c++ tools
So, based on JLBorges's post with the templates, when you have multiple inheritances the two parent classes will never have the same this pointer unless they inherit from the same base class.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//            A      B
//             \    /
//               C
// ---------> Will never be true?
static_cast<A*>(&C) == static_cast<B*>(&C)  

//
//              A
//            /   \
//           B     C
//            \   /
//              D
// ---------> Could be true?
static_cast<B*>(&D) == static_cast<C*>(&D)  

1
2
3
4
5
6
7
//              A
//            /   \
//           B     C
//            \   /
//              D
// ---------> Could be true?
static_cast<B*>(&D) == static_cast<C*>(&D)  

Also will never be true unless one of them is an empty base class.
Last edited on
one of them is empty base class
Base B, C or A?
Either B or C must be empty for a compiler to be able to apply the empty base class optimization while arriving at the object representation of D.

For either B or C to be empty, its own base class A must obviously be an empty, non-virtual base class.
Last edited on
thank you aquaz, I read about the casting operators and I could solve this problem. it didn't work with static_cast probably because Super1 & Super2 are unrelated, so I had to use dynamic_cast. I can't avoid the overhead, but I guess it won't be a problem...
Pages: 12