Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > strange multi-inheritance problem

Reply
Thread Tools

strange multi-inheritance problem

 
 
Patricia
Guest
Posts: n/a
 
      09-20-2005

First, I know the following code is bad, but it's from a library I have
to use, and I can't change it.

class A {
// some primitive members
};

class B {
// some primitive members
};

class C : public A, B {
// no members
};

main() {
// I know sizeof(A) = 16, sizeof(B) = 60
C * pc = (C*) new char[sizeof(A) + sizeof(B)];
B * pb = (B*) pc;

char * p1 = (char *) pc;
char * p2 = (char *) pb;

cout << "offset : " << (p2 - p1) << endl;
}

What surprised me is that the offset is 20 instead of 16. Do you know
the possible reason?

Regards.

 
Reply With Quote
 
 
 
 
Patricia
Guest
Posts: n/a
 
      09-20-2005
Also it turns out that sizeof(C) = 80.

 
Reply With Quote
 
 
 
 
=?iso-8859-1?Q?Ali_=C7ehreli?=
Guest
Posts: n/a
 
      09-20-2005
"Patricia" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed) oups.com...
>
> First, I know the following code is bad, but it's from a library I have
> to use, and I can't change it.
>
> class A {
> // some primitive members
> };
>
> class B {
> // some primitive members
> };
>
> class C : public A, B {
> // no members
> };
>
> main() {


The return type of main must be explicitly specified as int:

int main() {

> // I know sizeof(A) = 16, sizeof(B) = 60


Do you also know sizeof(C)?

> C * pc = (C*) new char[sizeof(A) + sizeof(B)];


That is undefined behavior. You cannot assume that sizeof(C) will be equal
to sizeof(A) and sizeof(B). Nor can you assume that there is a C object
there. Those are just a bunch of uninitialized characters. You cannot assume
that they can be readily used as a C.

> B * pb = (B*) pc;


Actually, C inherits from B privately. You can't assume that the C-style
cast above will give you access to the B part a C. If you intended to
inherit publicly from C, then the cast is not needed:

B * pb = pc;

> char * p1 = (char *) pc;
> char * p2 = (char *) pb;
>
> cout << "offset : " << (p2 - p1) << endl;
> }
>
> What surprised me is that the offset is 20 instead of 16. Do you know
> the possible reason?


The reason is that the program's behavior is undefined.

As a side note, you should abandon using the C-style casts. Use one of C++'s
casting operators to make it explicit what you're saying, and to get help
from the compiler when the casting is illegal.

Ali

 
Reply With Quote
 
=?iso-8859-1?Q?Ali_=C7ehreli?=
Guest
Posts: n/a
 
      09-20-2005
"Patricia" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed) oups.com...
>
> First, I know the following code is bad, but it's from a library I have
> to use, and I can't change it.
>
> class A {
> // some primitive members
> };
>
> class B {
> // some primitive members
> };
>
> class C : public A, B {
> // no members
> };
>
> main() {
> // I know sizeof(A) = 16, sizeof(B) = 60
> C * pc = (C*) new char[sizeof(A) + sizeof(B)];
> B * pb = (B*) pc;
>
> char * p1 = (char *) pc;
> char * p2 = (char *) pb;
>
> cout << "offset : " << (p2 - p1) << endl;
> }
>
> What surprised me is that the offset is 20 instead of 16. Do you know
> the possible reason?


The following is a better way of doing what you are trying to do:

#include <iostream>
#include <memory>

class A {
// some primitive members
int a[4];
};

class B {
// some primitive members
int a[15];
};

class C : public A, public B {
// no members
};

int main() {
std::cout << " sizeof(A): " << sizeof(A)
<< " sizeof(B): " << sizeof(B)
<< " sizeof(C): " << sizeof(C)
<< '\n';

// Allocate a buffer
char * buffer = new char[sizeof(C)];

// Use placement new to construct an object on that buffer
C * pc = new (reinterpret_cast<void *>(buffer)) C();

// A valid up-cast
B * pb = pc;

char * p1 = reinterpret_cast<char *>(pc);
char * p2 = reinterpret_cast<char *>(pb);

// WARNING: Undefined behavior below! We cannot subtract two
// pointers unless they point to objects of the same
// array. Nevertheless, the results may be useful on a given system.
std::cout << "offset : " << (p2 - p1) << endl;

// Destroy the C object manually and release the buffer
pc->~C();
delete buffer;
}

Ali

 
Reply With Quote
 
=?iso-8859-1?Q?Ali_=C7ehreli?=
Guest
Posts: n/a
 
      09-20-2005
"Ali Çehreli" <(E-Mail Removed)> wrote in message
news:dgplnh$hap$(E-Mail Removed)...

> // Allocate a buffer
> char * buffer = new char[sizeof(C)];
>
> // Use placement new to construct an object on that buffer
> C * pc = new (reinterpret_cast<void *>(buffer)) C();


[...]

> // Destroy the C object manually and release the buffer
> pc->~C();
> delete buffer;
> }


And that would be a bug of course. Should be:

delete[] buffer;

Ali

 
Reply With Quote
 
Patricia
Guest
Posts: n/a
 
      09-20-2005
Sorry, I just want to give a rough idea of the problem. As I said, I
have to use the old library, and can't change it. The old library
should look like below.

class C : public A, public B {
// no members
};

main() {
// sizeof(A) = 16, sizeof(B) = 60, but sizeof(C) = 80
C * pc = (C*) new char[sizeof(A) + sizeof(B)];
new (pc) C();
B * pb = (B*) pc;

char * p1 = (char *) pc;
char * p2 = (char *) pb;

cout << "offset : " << (p2 - p1) << endl;

delete [] (char *)pc;
}

It's strange that sizeof(C) > sizeof(A) + sizeof(B). The thing is when
I declare my own A, B, C classes, it turns out sizeof(C) = sizeof(A) +
sizeof(B).

Is it because of a compiler option. By the way, I compile the program
with CC on Solaris 8.

 
Reply With Quote
 
=?iso-8859-1?Q?Ali_=C7ehreli?=
Guest
Posts: n/a
 
      09-20-2005
"Patricia" <(E-Mail Removed)> wrote in message
news:(E-Mail Removed) oups.com...

> class C : public A, public B {
> // no members
> };
>
> main() {
> // sizeof(A) = 16, sizeof(B) = 60, but sizeof(C) = 80
> C * pc = (C*) new char[sizeof(A) + sizeof(B)];


That is plain wrong. You can fit a C only on an area that has the size of at
least sizeof(C). Please use placement new to construct a C on a
pre-allocated memory. (I've already posted an example of this on this
thread.)

> It's strange that sizeof(C) > sizeof(A) + sizeof(B).


The compiler is free use padding bytes between parts of objects.

Ali

 
Reply With Quote
 
Patricia
Guest
Posts: n/a
 
      09-20-2005
Sorry, the above code is simplified. The real situation is there is a
class D which derives from B. Although C derives from A and B, they
used a memory block to hold A and D as below:

C * pc = (C*) new char[sizeof(A) + sizeof(D)];
new (pc) C();
B * pb = (B*) pc;

char * p1 = (char *) pc;
char * p2 = (char *) pb;

cout << "offset : " << (p2 - p1) << endl;

The code looks weird, but this is the real implemenation which was
written between 1995 and 1999.

Since A's size is 16, I don't know why the offset is 20.

 
Reply With Quote
 
John Harrison
Guest
Posts: n/a
 
      09-20-2005
Patricia wrote:
> First, I know the following code is bad, but it's from a library I have
> to use, and I can't change it.
>
> class A {
> // some primitive members
> };
>
> class B {
> // some primitive members
> };
>
> class C : public A, B {
> // no members
> };
>
> main() {
> // I know sizeof(A) = 16, sizeof(B) = 60
> C * pc = (C*) new char[sizeof(A) + sizeof(B)];
> B * pb = (B*) pc;
>
> char * p1 = (char *) pc;
> char * p2 = (char *) pb;
>
> cout << "offset : " << (p2 - p1) << endl;
> }
>
> What surprised me is that the offset is 20 instead of 16. Do you know
> the possible reason?


The compiler has adding padding between the A part of C and the B part of C.

Object C is

16 bytes of A
4 bytes of padding
60 bytes of B

Compilers are allowed to do this, which is why you should not assume
that sizeof(C) = sizeof(A) + sizeof(B)

john
 
Reply With Quote
 
Old Wolf
Guest
Posts: n/a
 
      09-20-2005
Ali Çehreli wrote:
> char * buffer = new char[sizeof(C)];
>
> C * pc = new (reinterpret_cast<void *>(buffer)) C();
> B * pb = pc;
> char * p1 = reinterpret_cast<char *>(pc);
> char * p2 = reinterpret_cast<char *>(pb);
>
> // WARNING: Undefined behavior below! We cannot subtract two
> // pointers unless they point to objects of the same
> // array. Nevertheless, the results may be useful on a given system.
> std::cout << "offset : " << (p2 - p1) << endl;


p1 and p2 both point to within the object 'buffer' points to,
so I think the behaviour is defined.

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
AVG Email Scanner activating at strange times with strange IP addresses dennispublic@hotmail.com Computer Support 1 08-26-2006 04:27 AM
strange problem John Wireless Networking 4 02-14-2005 05:40 PM
Problem installing modules on redhat 9 - strange connection problem championsleeper Perl Misc 0 01-26-2005 12:19 PM
Strange error! Compiler problem? Or code problem?!!!!!!! Pedro Miguel Carvalho C++ 2 10-25-2004 02:06 PM
Question About Strange 'C' Code Syntax ( Well strange to me anyway ) Harvey Twyman C Programming 8 10-25-2003 05:54 AM



Advertisments