By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
424,961 Members | 1,388 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 424,961 IT Pros & Developers. It's quick & easy.

I don't get it

P: n/a
Jo
class A {

public:

char text_a[100];

A() { *text_a=0; }
~A() {}
};
//-----------------------------------------------------------------------------
class B {

public:

char text_b[100];

B() { *text_b=0; }
~B() {}
};
//-----------------------------------------------------------------------------
class C : public A, public B {

public:

char text_c[100];

C() { *text_c=0; }
~C() {}
};
//-------------------------------------------------------------------------------
void test() {

B *bp1,*bp2;
C c,*cp1,*cp2,*cp3;
void *p;

strcpy(c.text_a,"hello a");
strcpy(c.text_b,"hello b");
strcpy(c.text_c,"hello c");
cp1=&c;
p=cp1;
bp1=cp1; // ok
bp2=(B*)p; // resulting bp2 is WRONG!
cp2=(C*)p; // ok
cp3=(C*)bp2; // resulting cp3 is WRONG! Which is logical because bp2
is already wrong.
}
//-----------------------------------------------------------------------------

So the hot spot is the bp2=(B*)p;

What's wrong with that???

I can imagine someone saying "p is not pointing to a B object".
But if you think about it, conceptually, it does, imho.

So is it more a technical matter of compiling this!?

Maybe i'm stupid and/or missing something essential about C++.

If so, please give me a link to where i can study this right.

Cheers,

Jo

Jun 18 '07 #1
Share this Question
Share on Google+
31 Replies


P: n/a
On Jun 18, 11:17 am, Jo <jo.lan...@telenet.bewrote:
class A {

public:

char text_a[100];

A() { *text_a=0; }
~A() {}};

//-----------------------------------------------------------------------------
class B {

public:

char text_b[100];

B() { *text_b=0; }
~B() {}};

//-----------------------------------------------------------------------------
class C : public A, public B {

public:

char text_c[100];

C() { *text_c=0; }
~C() {}};

//-------------------------------------------------------------------------------
void test() {

B *bp1,*bp2;
C c,*cp1,*cp2,*cp3;
void *p;

strcpy(c.text_a,"hello a");
strcpy(c.text_b,"hello b");
strcpy(c.text_c,"hello c");
cp1=&c;
p=cp1;
bp1=cp1; // ok
bp2=(B*)p; // resulting bp2 is WRONG!
cp2=(C*)p; // ok
cp3=(C*)bp2; // resulting cp3 is WRONG! Which is logical because bp2
is already wrong.}

//-----------------------------------------------------------------------------

So the hot spot is the bp2=(B*)p;

What's wrong with that???

I can imagine someone saying "p is not pointing to a B object".
But if you think about it, conceptually, it does, imho.

So is it more a technical matter of compiling this!?

Maybe i'm stupid and/or missing something essential about C++.

If so, please give me a link to where i can study this right.

Cheers,

Jo
I re-wrote your test function like this -
int main() {

B *bp1,*bp2;
C c,*cp1;
void *p;
int a;

strcpy(c.text_a,"hello a");
strcpy(c.text_b,"hello b");
strcpy(c.text_c,"hello c");
cp1=&c;
p=cp1;
bp2=(B*)p;
std::cout<<p<<"\n";
std::cout<<bp2;
std::cin>>a;
}

I printed the value in p and also in bp2. Both are one and the same.
On my m/c the O/P was -
0x22fe28
0x22fe28

Why do you think, in your program, resulting bp2 is WRONG?

Jun 18 '07 #2

P: n/a
Jo
Bharath wrote:
>On Jun 18, 11:17 am, Jo <jo.lan...@telenet.bewrote:

>>class A {

public:

char text_a[100];

A() { *text_a=0; }
~A() {}};

//-----------------------------------------------------------------------------
class B {

public:

char text_b[100];

B() { *text_b=0; }
~B() {}};

//-----------------------------------------------------------------------------
class C : public A, public B {

public:

char text_c[100];

C() { *text_c=0; }
~C() {}};

//-------------------------------------------------------------------------------
void test() {

B *bp1,*bp2;
C c,*cp1,*cp2,*cp3;
void *p;

strcpy(c.text_a,"hello a");
strcpy(c.text_b,"hello b");
strcpy(c.text_c,"hello c");
cp1=&c;
p=cp1;
bp1=cp1; // ok
bp2=(B*)p; // resulting bp2 is WRONG!
cp2=(C*)p; // ok
cp3=(C*)bp2; // resulting cp3 is WRONG! Which is logical because bp2
is already wrong.}

//-----------------------------------------------------------------------------

So the hot spot is the bp2=(B*)p;

What's wrong with that???

I can imagine someone saying "p is not pointing to a B object".
But if you think about it, conceptually, it does, imho.

So is it more a technical matter of compiling this!?

Maybe i'm stupid and/or missing something essential about C++.

If so, please give me a link to where i can study this right.

Cheers,

Jo


I re-wrote your test function like this -
int main() {

B *bp1,*bp2;
C c,*cp1;
void *p;
int a;

strcpy(c.text_a,"hello a");
strcpy(c.text_b,"hello b");
strcpy(c.text_c,"hello c");
cp1=&c;
p=cp1;
bp2=(B*)p;
std::cout<<p<<"\n";
std::cout<<bp2;
std::cin>>a;
}

I printed the value in p and also in bp2. Both are one and the same.
On my m/c the O/P was -
0x22fe28
0x22fe28

Why do you think, in your program, resulting bp2 is WRONG?
Because i got runtime problems.

So i narrowed down everything to the above code.

Running this on my PC gives a wrong bp2 pointer. I can see it in the
"Watch" panel.

And i also did a similar thing than you: at the end of the test/main
function i did a

OutputDebugString(cp3->text_a);
OutputDebugString(cp3->text_b);
OutputDebugString(cp3->text_c);

This gives rubbish for text a!!!

Compilation is done with Visual C++ 7.1.3088

Jun 18 '07 #3

P: n/a
Jo wrote:
class A {

public:

char text_a[100];

A() { *text_a=0; }
~A() {}
};
//-----------------------------------------------------------------------------

class B {

public:

char text_b[100];

B() { *text_b=0; }
~B() {}
};
//-----------------------------------------------------------------------------

class C : public A, public B {

public:

char text_c[100];

C() { *text_c=0; }
~C() {}
};
//-------------------------------------------------------------------------------

void test() {

B *bp1,*bp2;
C c,*cp1,*cp2,*cp3;
void *p;

strcpy(c.text_a,"hello a");
strcpy(c.text_b,"hello b");
strcpy(c.text_c,"hello c");
cp1=&c;
p=cp1;
bp1=cp1; // ok
bp2=(B*)p; // resulting bp2 is WRONG!
cp2=(C*)p; // ok
cp3=(C*)bp2; // resulting cp3 is WRONG! Which is logical because bp2 is
already wrong.
}
//-----------------------------------------------------------------------------
So the hot spot is the bp2=(B*)p;

What's wrong with that???

I can imagine someone saying "p is not pointing to a B object".
Right.
But if you think about it, conceptually, it does, imho.
No, it doesn't.
>
So is it more a technical matter of compiling this!?
No the code is wrong.
>
Maybe i'm stupid and/or missing something essential about C++.
I doubt you're stupid but you're certainly missing something essential.
>
If so, please give me a link to where i can study this right.
I have no idea why you think that code should be right, that is my problem.

Perhaps you are expecting an ordinary cast to work like a dynamic_cast?
When you write

bp2=(B*)p; // resulting bp2 is WRONG!

you are expecting the program to 'discover' that there a B object hidden
inside the object that p is pointing to?

If so google for dynamic_cast, or better still read a good book.

john
Jun 18 '07 #4

P: n/a
Jo <jo*******@telenet.bewrote in
news:be*********************@phobos.telenet-ops.be:
class A {

public:

char text_a[100];

A() { *text_a=0; }
~A() {}
};
//---------------------------------------------------------------------
-------- class B {

public:

char text_b[100];

B() { *text_b=0; }
~B() {}
};
//---------------------------------------------------------------------
-------- class C : public A, public B {

public:

char text_c[100];

C() { *text_c=0; }
~C() {}
};
//---------------------------------------------------------------------
---------- void test() {

B *bp1,*bp2;
C c,*cp1,*cp2,*cp3;
void *p;

strcpy(c.text_a,"hello a");
strcpy(c.text_b,"hello b");
strcpy(c.text_c,"hello c");
cp1=&c;
p=cp1;
bp1=cp1; // ok
bp2=(B*)p; // resulting bp2 is WRONG!
cp2=(C*)p; // ok
cp3=(C*)bp2; // resulting cp3 is WRONG! Which is logical because bp2
is already wrong.
}
//---------------------------------------------------------------------
--------

So the hot spot is the bp2=(B*)p;

What's wrong with that???
Because you are casting a p back into a pointer type that it wasn't
originally. p was originally a C*. You need to cast it back into a C*
first.
I can imagine someone saying "p is not pointing to a B object".
But if you think about it, conceptually, it does, imho.
Nope. It may be pointing at an A obect.
So is it more a technical matter of compiling this!?

Maybe i'm stupid and/or missing something essential about C++.

If so, please give me a link to where i can study this right.
Standard says you can cast a pointer to object into a void*, but you can
only cast the void* back into the pointer type that it was originally.
Anything else is undefined behaviour.

<Implementation-specific behaviour>
What's probably happening in your case is that the memory layout of your
object is A->B->C. So when you take your C* (which is effectively also
an A*, but is _not_ a B*...), hammer it into a void*, and then re-form it
as a B*, you are effectively casting an A* to a B*. Since they're
unrelated types, who knows what the operations on B do to an A object.

So.. you should cast that void* to a C* first, then assign the C* to a B*
(no cast required). The compiler will know at that point that you're
upcasting and will adjust the pointer value accordingly.

</Implementation-specific behaviour>
Jun 18 '07 #5

P: n/a
Jo
Andre Kostur wrote:
>Jo <jo*******@telenet.bewrote in
news:be*********************@phobos.telenet-ops.be:
>>class A {

public:

char text_a[100];

A() { *text_a=0; }
~A() {}
};
//---------------------------------------------------------------------
-------- class B {

public:

char text_b[100];

B() { *text_b=0; }
~B() {}
};
//---------------------------------------------------------------------
-------- class C : public A, public B {

public:

char text_c[100];

C() { *text_c=0; }
~C() {}
};
//---------------------------------------------------------------------
---------- void test() {

B *bp1,*bp2;
C c,*cp1,*cp2,*cp3;
void *p;

strcpy(c.text_a,"hello a");
strcpy(c.text_b,"hello b");
strcpy(c.text_c,"hello c");
cp1=&c;
p=cp1;
bp1=cp1; // ok
bp2=(B*)p; // resulting bp2 is WRONG!
cp2=(C*)p; // ok
cp3=(C*)bp2; // resulting cp3 is WRONG! Which is logical because bp2
is already wrong.
}
//---------------------------------------------------------------------
--------

So the hot spot is the bp2=(B*)p;

What's wrong with that???


Because you are casting a p back into a pointer type that it wasn't
originally. p was originally a C*. You need to cast it back into a C*
first.
In the real code, i don't know yet what the type is.
I first need to get a B pointer, then i can deduce the target class.

Do i understand it right that this problem would not occur if
>>I can imagine someone saying "p is not pointing to a B object".
But if you think about it, conceptually, it does, imho.


Nope. It may be pointing at an A obect.
That's technically speaking, not conceptually, imo.

If C is derived from A & B, then a C object IS an A and IS a B. At the
same time.

But i understand that at a certain point we must transition from
concepts into practical technology.

What i mean is: in practice: p cannot point to both the A and B instance
at the same time, so you're right about that.

So that's where we come into the implementation specifics.
>>So is it more a technical matter of compiling this!?

Maybe i'm stupid and/or missing something essential about C++.

If so, please give me a link to where i can study this right.


Standard says you can cast a pointer to object into a void*, but you can
only cast the void* back into the pointer type that it was originally.
Anything else is undefined behaviour.

<Implementation-specific behaviour>
What's probably happening in your case is that the memory layout of your
object is A->B->C. So when you take your C* (which is effectively also
an A*, but is _not_ a B*...), hammer it into a void*, and then re-form it
as a B*, you are effectively casting an A* to a B*. Since they're
unrelated types, who knows what the operations on B do to an A object.

So.. you should cast that void* to a C* first, then assign the C* to a B*
(no cast required). The compiler will know at that point that you're
upcasting and will adjust the pointer value accordingly.

</Implementation-specific behaviour>
Cfr above: i don't know that i should go to a C until i got an A first,
because the A class contains some class specific definitions.

Now do i understand it right that the problem comes from the fact that
i'm using a void* ?

Would this be legal then:

// quick recap: class C is derived from both class A and B

A *ap;
B *bp;
C c,*cp;

cp=&c;
ap=cp;
bp=(B*)ap;

or the last line being

bp=static_cast<B*>(ap);

which is the same, right?

(i'm not laisy, i googled about it, but it's still not 100% clear)

Jun 18 '07 #6

P: n/a
Jo
John Harrison wrote:
Jo wrote:
>class A {

public:

char text_a[100];

A() { *text_a=0; }
~A() {}
};
//-----------------------------------------------------------------------------

class B {

public:

char text_b[100];

B() { *text_b=0; }
~B() {}
};
//-----------------------------------------------------------------------------

class C : public A, public B {

public:

char text_c[100];

C() { *text_c=0; }
~C() {}
};
//-------------------------------------------------------------------------------

void test() {

B *bp1,*bp2;
C c,*cp1,*cp2,*cp3;
void *p;

strcpy(c.text_a,"hello a");
strcpy(c.text_b,"hello b");
strcpy(c.text_c,"hello c");
cp1=&c;
p=cp1;
bp1=cp1; // ok
bp2=(B*)p; // resulting bp2 is WRONG!
cp2=(C*)p; // ok
cp3=(C*)bp2; // resulting cp3 is WRONG! Which is logical because bp2
is already wrong.
}
//-----------------------------------------------------------------------------
So the hot spot is the bp2=(B*)p;

What's wrong with that???

I can imagine someone saying "p is not pointing to a B object".


Right.
>But if you think about it, conceptually, it does, imho.


No, it doesn't.
>>
So is it more a technical matter of compiling this!?


No the code is wrong.
>>
Maybe i'm stupid and/or missing something essential about C++.


I doubt you're stupid but you're certainly missing something essential.
>>
If so, please give me a link to where i can study this right.


I have no idea why you think that code should be right, that is my
problem.

Imho, if class C is derived from A and B, then C IS an A and a B at the
same time, conceptually.

But i'm understanding that there is a technical implementation problem here.

I thought the compiler could figure that out, but i'm clearly over
estimating the compiler and/or C++.

Anyway, it's off topic to go deeper in this. Although that would be a
nice discussion.

So i'll focus on the practical problem here.
Perhaps you are expecting an ordinary cast to work like a
dynamic_cast? When you write

bp2=(B*)p; // resulting bp2 is WRONG!

you are expecting the program to 'discover' that there a B object
hidden inside the object that p is pointing to?

If so google for dynamic_cast, or better still read a good book.

I understand that a dynamic_cast cannot do a base-to-derived conversion,
so that's not the solution here.
And a static_cast doesn't seem to make any difference, because i'm
already doing static casts right, just in the 'old' way.

So i'm still looking for the solution for this situation

class A {

public:
virtual long GetClassID() { return(' A'); }
};
class B {

public:
// some class data & funx
};

class C : public class A, public class B {

public:
virtual long GetClassID() { return(' C'); }
};
foo1(C *cp) {
...
foo2(cp);
...
}

foo2(A *ap) {

if (ap->GetClassID==' C') {
C *cp=(C*)ap; // IS THIS LEGAL and PORTABLE C++ ?
}
}

Jun 18 '07 #7

P: n/a
Jo
class A {

public:

char text_a[100];

A() { *text_a=0; }
~A() {}
};
//-----------------------------------------------------------------------------
class B {

public:

char text_b[100];

B() { *text_b=0; }
~B() {}
};
//-----------------------------------------------------------------------------
class C : public A, public B {

public:

char text_c[100];

C() { *text_c=0; }
~C() {}
};
//-----------------------------------------------------------------------------
In the context of pointer casting: Does it matter for ansi C++ if class
C is derived from A then B, or from B then A ?

Jun 18 '07 #8

P: n/a
>If so google for dynamic_cast, or better still read a good book.


I understand that a dynamic_cast cannot do a base-to-derived conversion,
so that's not the solution here.
dynamic_cast does do base to derived conversion, that's it's main use.
Where are you getting your information?

john
Jun 18 '07 #9

P: n/a
//-----------------------------------------------------------------------------
>
class C : public A, public B {

public:

char text_c[100];

C() { *text_c=0; }
~C() {}
};
//-----------------------------------------------------------------------------

In the context of pointer casting: Does it matter for ansi C++ if class
C is derived from A then B, or from B then A ?
No it doesn't matter.

john
Jun 18 '07 #10

P: n/a
Jo wrote:
>
Imho, if class C is derived from A and B, then C IS an A and a B at the
same time, conceptually.

But i'm understanding that there is a technical implementation problem
here.

I thought the compiler could figure that out, but i'm clearly over
estimating the compiler and/or C++.
The compiler can, indeed, figure out the relation between a C*, the
corresponding A*, and the corresponding B*. The problem is that you're
not dealing with a C*, but with a void*. The code tells the compiler
that p points to untyped data, and that it should treat the address of
that untyped data as the address of an object of type B. The compiler
did exactly what you told it to do. The only valid conversion you can
apply to that void* is to cast it back to the type of the original
pointer. (C*)p, as you've seen, works just fine. (B*)(C*)p would give
you the address of the B subobject.

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
Jun 18 '07 #11

P: n/a
Jo wrote:
Imho, if class C is derived from A and B, then C IS an A and a B at the
same time, conceptually.

But i'm understanding that there is a technical implementation problem
here.
In the OP you tried to get a B* from a void*. A void pointer is everything
and nothing, the compiler cannot know what type of object it points to.
So avoid void*. Avoid c-style cast, too. With C++ style cast, the compiler
can warn you whats wrong using them.
I thought the compiler could figure that out, but i'm clearly over
estimating the compiler and/or C++.

Anyway, it's off topic to go deeper in this. Although that would be a
nice discussion.
When it's a language thing, it is on-topic.
>Perhaps you are expecting an ordinary cast to work like a
dynamic_cast? When you write

bp2=(B*)p; // resulting bp2 is WRONG!

you are expecting the program to 'discover' that there a B object
hidden inside the object that p is pointing to?

If so google for dynamic_cast, or better still read a good book.


I understand that a dynamic_cast cannot do a base-to-derived conversion,
so that's not the solution here.
dynamic_cast can, and is exactly the right tool, to do base-to-derived
conversion. It's like static_cast with an explicit runtime type check.
So i'm still looking for the solution for this situation

class A {

public:
virtual long GetClassID() { return(' A'); }
};
[...]
class C : public class A, public class B {

public:
virtual long GetClassID() { return(' C'); }
};
foo1(C *cp) {
...
foo2(cp);
...
}

foo2(A *ap) {

if (ap->GetClassID==' C') {
C *cp=(C*)ap; // IS THIS LEGAL and PORTABLE C++ ?
}
}
When you know that an A* points to a C object, you can static_cast it to a
C*. But why inventing your own RTTI? You can use typeid and dynamic_cast
instead.

--
Thomas
http://www.netmeister.org/news/learn2quote.html
Jun 18 '07 #12

P: n/a
Jo
John Harrison wrote:
>
>>If so google for dynamic_cast, or better still read a good book.

I understand that a dynamic_cast cannot do a base-to-derived
conversion, so that's not the solution here.


dynamic_cast does do base to derived conversion, that's it's main use.
Where are you getting your information?

I got that from http://www.cplusplus.com/doc/tutorial/typecasting.html

But i was just reading another webpage where they indeed use
base-to-derived dynamic casts...

Did i misunderstand the cplusplus page?

Anyway, i prefer not to use RTTI.

Jun 18 '07 #13

P: n/a
Jo
Pete Becker wrote:
Jo wrote:
>>
Imho, if class C is derived from A and B, then C IS an A and a B at
the same time, conceptually.

But i'm understanding that there is a technical implementation
problem here.

I thought the compiler could figure that out, but i'm clearly over
estimating the compiler and/or C++.

The compiler can, indeed, figure out the relation between a C*, the
corresponding A*, and the corresponding B*. The problem is that you're
not dealing with a C*, but with a void*. The code tells the compiler
that p points to untyped data, and that it should treat the address of
that untyped data as the address of an object of type B. The compiler
did exactly what you told it to do. The only valid conversion you can
apply to that void* is to cast it back to the type of the original
pointer. (C*)p, as you've seen, works just fine. (B*)(C*)p would give
you the address of the B subobject.

So the very problem was the void*, right?

So is all this correct then: (supposing the same class hierarchy as
before, where C is derived from A and B)

C *cp=new C;
B *bp=cp;
C *cp2=(C*)bp;
A *ap=(A*)bp;

Jun 18 '07 #14

P: n/a
Jo <jo*******@telenet.bewrote in
news:XV*********************@phobos.telenet-ops.be:
Andre Kostur wrote:
>>Jo <jo*******@telenet.bewrote in
news:be*********************@phobos.telenet-ops.be:
[snip example. Important point: There is a class C which inherits from A
& B. A & B are otherwise unrelated]
>>>So the hot spot is the bp2=(B*)p;

What's wrong with that???


Because you are casting a p back into a pointer type that it wasn't
originally. p was originally a C*. You need to cast it back into a
C* first.

In the real code, i don't know yet what the type is.
I first need to get a B pointer, then i can deduce the target class.

Do i understand it right that this problem would not occur if
if... ?
>>>I can imagine someone saying "p is not pointing to a B object".
But if you think about it, conceptually, it does, imho.


Nope. It may be pointing at an A obect.

That's technically speaking, not conceptually, imo.

If C is derived from A & B, then a C object IS an A and IS a B. At the
same time.
Sure... within the constraints of the language. Means you can pass a C
to something that takes an A, or to something that takes a B. But you
went and removed all type data from the object when it became a void*.
You need to get that pointer back onto the right track first.
But i understand that at a certain point we must transition from
concepts into practical technology.

What i mean is: in practice: p cannot point to both the A and B
instance at the same time, so you're right about that.

So that's where we come into the implementation specifics.
>>>So is it more a technical matter of compiling this!?

Maybe i'm stupid and/or missing something essential about C++.

If so, please give me a link to where i can study this right.


Standard says you can cast a pointer to object into a void*, but you
can only cast the void* back into the pointer type that it was
originally. Anything else is undefined behaviour.

<Implementation-specific behaviour>
What's probably happening in your case is that the memory layout of
your object is A->B->C. So when you take your C* (which is
effectively also an A*, but is _not_ a B*...), hammer it into a void*,
and then re-form it as a B*, you are effectively casting an A* to a
B*. Since they're unrelated types, who knows what the operations on B
do to an A object.

So.. you should cast that void* to a C* first, then assign the C* to a
B* (no cast required). The compiler will know at that point that
you're upcasting and will adjust the pointer value accordingly.

</Implementation-specific behaviour>

Cfr above: i don't know that i should go to a C until i got an A
first, because the A class contains some class specific definitions.

Now do i understand it right that the problem comes from the fact that
i'm using a void* ?

Would this be legal then:

// quick recap: class C is derived from both class A and B

A *ap;
B *bp;
C c,*cp;

cp=&c;
ap=cp;
bp=(B*)ap;
Casting an A* into a B*. Bad. A & B are unrelated.
or the last line being

bp=static_cast<B*>(ap);

which is the same, right?
Nope. Compiler should complain about that since A and B are unrelated.
Jun 18 '07 #15

P: n/a
Jo <jo*******@telenet.bewrote in
news:fa*********************@phobos.telenet-ops.be:
John Harrison wrote:
>Jo wrote:
[snip]
>Perhaps you are expecting an ordinary cast to work like a
dynamic_cast? When you write

bp2=(B*)p; // resulting bp2 is WRONG!

you are expecting the program to 'discover' that there a B object
hidden inside the object that p is pointing to?

If so google for dynamic_cast, or better still read a good book.


I understand that a dynamic_cast cannot do a base-to-derived
conversion, so that's not the solution here.
No, that's exactly what dynamic_cast is for, _if_ the pointer is pointing
to an object that actually is of the derived type.
And a static_cast doesn't seem to make any difference, because i'm
already doing static casts right, just in the 'old' way.

So i'm still looking for the solution for this situation

class A {

public:
virtual long GetClassID() { return(' A'); }
};
class B {

public:
// some class data & funx
};

class C : public class A, public class B {

public:
virtual long GetClassID() { return(' C'); }
};
foo1(C *cp) {
...
foo2(cp);
...
}

foo2(A *ap) {

if (ap->GetClassID==' C') {
C *cp=(C*)ap; // IS THIS LEGAL and PORTABLE C++
?
}
}
In theory that would work... of course so would:

class A {
virtual ~A() {};
};

class B {
virtual ~B() {};
};

class C : public class A, public class B {
virtual ~C() {};

void cfn() {};
};

foo1(C *cp) {
fooA(cp);
}

fooA(A *ap) {
C * cp = dynamic_cast<C*>(ap);

if (cp != NULL) {
cp->cfn();
}
}

No "getclassid" function required. However, note that there's no mention
of B anywhere in there (other than the inheritance).... so how does B
get involved in this question?
Jun 18 '07 #16

P: n/a
Jo
Thomas J. Gritzan wrote:
>Jo wrote:

>>Imho, if class C is derived from A and B, then C IS an A and a B at the
same time, conceptually.

But i'm understanding that there is a technical implementation problem
here.


In the OP you tried to get a B* from a void*. A void pointer is everything
and nothing, the compiler cannot know what type of object it points to.
OK, understood.
>So avoid void*. Avoid c-style cast, too. With C++ style cast, the compiler
can warn you whats wrong using them.
OK, will take this into account.
>>I thought the compiler could figure that out, but i'm clearly over
estimating the compiler and/or C++.

Anyway, it's off topic to go deeper in this. Although that would be a
nice discussion.


When it's a language thing, it is on-topic.
The point was: When C is derived from A and B, then C is an A and C is a
B at the same time.

I would have been surprised if C++/the compiler would not be able to do
the up/down casts in this family, even when multiple inheritance is into
play.

(i immediately thought of a system how this would be no problem)

For a moment, i thought John said that C is not a B. But i
misunderstood. Now i understand he said that the void* p is not pointing
to the B object (in the given example) which is right because it's a void*.

If the C++ compiler can figure out the A/B/C up/down casts, then of
course i keep on being a happy C++ -er.

I know all this is pure essential.
I'm a bit amazed/ashamed that i ran into this problem (i.e. using the
void*).
I didn't know that when using a void*, you explicitly abandon all type
info, except for the source type.
>>>Perhaps you are expecting an ordinary cast to work like a
dynamic_cast? When you write

bp2=(B*)p; // resulting bp2 is WRONG!

you are expecting the program to 'discover' that there a B object
hidden inside the object that p is pointing to?

If so google for dynamic_cast, or better still read a good book.

I understand that a dynamic_cast cannot do a base-to-derived conversion,
so that's not the solution here.


dynamic_cast can, and is exactly the right tool, to do base-to-derived
conversion. It's like static_cast with an explicit runtime type check.
I understand.
>>So i'm still looking for the solution for this situation

class A {

public:
virtual long GetClassID() { return(' A'); }
};

[...]

>>class C : public class A, public class B {

public:
virtual long GetClassID() { return(' C'); }
};
foo1(C *cp) {
...
foo2(cp);
...
}

foo2(A *ap) {

if (ap->GetClassID==' C') {
C *cp=(C*)ap; // IS THIS LEGAL and PORTABLE C++ ?
}
}


When you know that an A* points to a C object, you can static_cast it to a
C*. But why inventing your own RTTI? You can use typeid and dynamic_cast
instead.
It's indeed about having a 'little private' RTTI system.

I would like to not use RTTI because i heard about the overhead it brings.

Not sure if that is memory or cpu overhead, or both?

The application i'm working on is a multi-threaded realtime application
where part of the code runs at interrupt level.

And that code must be fast, and may not do any dynamic memory allocs!

So that's why i want to play it safe.

Jun 18 '07 #17

P: n/a
On 2007-06-18 22:17, Jo wrote:
John Harrison wrote:
>>
>>>If so google for dynamic_cast, or better still read a good book.

I understand that a dynamic_cast cannot do a base-to-derived
conversion, so that's not the solution here.


dynamic_cast does do base to derived conversion, that's it's main use.
Where are you getting your information?


I got that from http://www.cplusplus.com/doc/tutorial/typecasting.html

But i was just reading another webpage where they indeed use
base-to-derived dynamic casts...

Did i misunderstand the cplusplus page?
Yes, it seems to me like they perform a base to derived cast there
(second example), though their example are perhaps a bit more
complicated than needed.

--
Erik Wikström
Jun 18 '07 #18

P: n/a
Andre Kostur wrote:
>Jo <jo*******@telenet.bewrote in
news:XV*********************@phobos.telenet-ops.be:
>>Andre Kostur wrote:
>>>Jo <jo*******@telenet.bewrote in
news:be*********************@phobos.telenet-ops.be:

[snip example. Important point: There is a class C which inherits from A
& B. A & B are otherwise unrelated]
I see.
>>>>So the hot spot is the bp2=(B*)p;

What's wrong with that???


Because you are casting a p back into a pointer type that it wasn't
originally. p was originally a C*. You need to cast it back into a
C* first.
In the real code, i don't know yet what the type is.
I first need to get a B pointer, then i can deduce the target class.

Do i understand it right that this problem would not occur if


if... ?
If i wouldn't use that void*
>>>>I can imagine someone saying "p is not pointing to a B object".
But if you think about it, conceptually, it does, imho.


Nope. It may be pointing at an A obect.
That's technically speaking, not conceptually, imo.

If C is derived from A & B, then a C object IS an A and IS a B. At the
same time.


Sure... within the constraints of the language. Means you can pass a C
to something that takes an A, or to something that takes a B. But you
went and removed all type data from the object when it became a void*.
You need to get that pointer back onto the right track first.
Yes, using the void* was the very error.
>>But i understand that at a certain point we must transition from
concepts into practical technology.

What i mean is: in practice: p cannot point to both the A and B
instance at the same time, so you're right about that.

So that's where we come into the implementation specifics.
>>>>So is it more a technical matter of compiling this!?

Maybe i'm stupid and/or missing something essential about C++.

If so, please give me a link to where i can study this right.


Standard says you can cast a pointer to object into a void*, but you
can only cast the void* back into the pointer type that it was
originally. Anything else is undefined behaviour.

<Implementation-specific behaviour>
What's probably happening in your case is that the memory layout of
your object is A->B->C. So when you take your C* (which is
effectively also an A*, but is _not_ a B*...), hammer it into a void*,
and then re-form it as a B*, you are effectively casting an A* to a
B*. Since they're unrelated types, who knows what the operations on B
do to an A object.

So.. you should cast that void* to a C* first, then assign the C* to a
B* (no cast required). The compiler will know at that point that
you're upcasting and will adjust the pointer value accordingly.

</Implementation-specific behaviour>
Cfr above: i don't know that i should go to a C until i got an A
first, because the A class contains some class specific definitions.

Now do i understand it right that the problem comes from the fact that
i'm using a void* ?

Would this be legal then:

// quick recap: class C is derived from both class A and B

A *ap;
B *bp;
C c,*cp;

cp=&c;
ap=cp;
bp=(B*)ap;


Casting an A* into a B*. Bad. A & B are unrelated.
Indeed, i fully understand now.

And this also is a nice example why using static_cast is better because

bp=(B*)ap; // just works
bp=static_cast<B*>(ap); // compiler error which is a good thing
>>or the last line being

bp=static_cast<B*>(ap);

which is the same, right?


Nope. Compiler should complain about that since A and B are unrelated.

--

http://www.mutools.com

Jun 18 '07 #19

P: n/a
Jo

Andre Kostur wrote:
>Jo <jo*******@telenet.bewrote in
news:fa*********************@phobos.telenet-ops.be:
>>John Harrison wrote:
>>>Jo wrote:

[snip]
>>>Perhaps you are expecting an ordinary cast to work like a
dynamic_cast? When you write

bp2=(B*)p; // resulting bp2 is WRONG!

you are expecting the program to 'discover' that there a B object
hidden inside the object that p is pointing to?

If so google for dynamic_cast, or better still read a good book.

I understand that a dynamic_cast cannot do a base-to-derived
conversion, so that's not the solution here.


No, that's exactly what dynamic_cast is for, _if_ the pointer is pointing
to an object that actually is of the derived type.
>>And a static_cast doesn't seem to make any difference, because i'm
already doing static casts right, just in the 'old' way.

So i'm still looking for the solution for this situation

class A {

public:
virtual long GetClassID() { return(' A'); }
};
class B {

public:
// some class data & funx
};

class C : public class A, public class B {

public:
virtual long GetClassID() { return(' C'); }
};
foo1(C *cp) {
...
foo2(cp);
...
}

foo2(A *ap) {

if (ap->GetClassID==' C') {
C *cp=(C*)ap; // IS THIS LEGAL and PORTABLE C++
?
}
}


In theory that would work... of course so would:

class A {
virtual ~A() {};
};

class B {
virtual ~B() {};
};

class C : public class A, public class B {
virtual ~C() {};

void cfn() {};
};

foo1(C *cp) {
fooA(cp);
}

fooA(A *ap) {
C * cp = dynamic_cast<C*>(ap);

if (cp != NULL) {
cp->cfn();
}
}

No "getclassid" function required.
Indeed.

But i prefer not to use RTTI for the reasons mentioned in the earlier post.
>However, note that there's no mention
of B anywhere in there (other than the inheritance).... so how does B
get involved in this question?
I guess i was confused:

Because i ran into the casting problem (due to the use of the void*), i
started thinking that it had to do with the multiple inheritance...

I really didn't know that using a void* abandons all the class relation
info. (and that after all these years of programming, damn damn damn)

Thanks a lot for your help!

A dummy got a bit less dummy :)

Jun 18 '07 #20

P: n/a
On Jun 19, 7:20 am, Jo <jo.lan...@telenet.bewrote:
If C is derived from A & B, then a C object IS an A and IS a B. At the
same time.
Yes, but an A is not a B. The two are unrelated
except in that they are both part of a C. If
you are pointing to an A, then the only way you
are going to be able to point to a related B is
if you know you are actually pointing to an A
sub-object of a C. Which is why this doesn't work
if you are using a pointer to void, which contains
no type information.
// quick recap: class C is derived from both class A and B

A *ap;
B *bp;
C c,*cp;

cp=&c;
ap=cp;
bp=(B*)ap;
This is a reinterpret-cast, because A and B are unrelated
types (see above). The pointer 'ap' has lost the information
that it is actually pointing to an A that's a subobject
of a C.
or the last line being

bp=static_cast<B*>(ap);
Should be a compliation error for the above reason.

You can perform the conversion successfully:
bp = dynamic_cast<B *>(ap);

which tells the compiler to find the biggest object
that *ap is a sub-object of, and then try to find a
B object within that object. This is sometimes
called cross-casting.

Note, I'm not entirely sure about this, but I think
this only works if A and B are both polymorphic
classes, that is, they both contain at least one
virtual function (which will usually be the virtual
destructor). You might just get a null pointer if
this requirement is not met.

Jun 18 '07 #21

P: n/a
Jo
Old Wolf wrote:
>On Jun 19, 7:20 am, Jo <jo.lan...@telenet.bewrote:

>>If C is derived from A & B, then a C object IS an A and IS a B. At the
same time.


Yes, but an A is not a B. The two are unrelated
except in that they are both part of a C. If
you are pointing to an A, then the only way you
are going to be able to point to a related B is
if you know you are actually pointing to an A
sub-object of a C. Which is why this doesn't work
if you are using a pointer to void, which contains
no type information.
>>// quick recap: class C is derived from both class A and B

A *ap;
B *bp;
C c,*cp;

cp=&c;
ap=cp;
bp=(B*)ap;


This is a reinterpret-cast, because A and B are unrelated
types (see above). The pointer 'ap' has lost the information
that it is actually pointing to an A that's a subobject
of a C.
>>or the last line being

bp=static_cast<B*>(ap);


Should be a compliation error for the above reason.

You can perform the conversion successfully:
bp = dynamic_cast<B *>(ap);

which tells the compiler to find the biggest object
that *ap is a sub-object of, and then try to find a
B object within that object. This is sometimes
called cross-casting.
That's powerful!

But at what cost?

Do you know how heavy the RTTI overhead is

-on memory
-on cpu

?

If it's similar to using virtual functions, then it's ok.
>Note, I'm not entirely sure about this, but I think
this only works if A and B are both polymorphic
classes, that is, they both contain at least one
virtual function (which will usually be the virtual
destructor). You might just get a null pointer if
this requirement is not met.

Jun 18 '07 #22

P: n/a
Jo <jo@mutools.comwrote in
news:PI*********************@phobos.telenet-ops.be:
>
Andre Kostur wrote:
>>Jo <jo*******@telenet.bewrote in
news:fa*********************@phobos.telenet-ops.be:
[more snip as the issue has been resolved]
>>

No "getclassid" function required.

Indeed.

But i prefer not to use RTTI for the reasons mentioned in the earlier
post.
Next item.... measure, measure, measure. How do you know that your own
RTTI method is ligher-weight than the one that the compiler is using?
>>However, note that there's no mention
of B anywhere in there (other than the inheritance).... so how does B
get involved in this question?

I guess i was confused:

Because i ran into the casting problem (due to the use of the void*),
i started thinking that it had to do with the multiple inheritance...

I really didn't know that using a void* abandons all the class
relation info. (and that after all these years of programming, damn
damn damn)

Thanks a lot for your help!

A dummy got a bit less dummy :)
Ask questions... that's what we're here for :)
Jun 18 '07 #23

P: n/a
Jo
Andre Kostur wrote:
>Jo <jo@mutools.comwrote in
news:PI*********************@phobos.telenet-ops.be:

>>Andre Kostur wrote:
>>>Jo <jo*******@telenet.bewrote in
news:fa*********************@phobos.telenet-ops.be:


[more snip as the issue has been resolved]
>>>No "getclassid" function required.
Indeed.

But i prefer not to use RTTI for the reasons mentioned in the earlier
post.


Next item.... measure, measure, measure. How do you know that your own
RTTI method is ligher-weight than the one that the compiler is using?
True.

That's also why i asked about the cost of RTTI.

At the moment i don't have a clue.
>>>However, note that there's no mention
of B anywhere in there (other than the inheritance).... so how does B
get involved in this question?
I guess i was confused:

Because i ran into the casting problem (due to the use of the void*),
i started thinking that it had to do with the multiple inheritance...

I really didn't know that using a void* abandons all the class
relation info. (and that after all these years of programming, damn
damn damn)

Thanks a lot for your help!

A dummy got a bit less dummy :)


Ask questions... that's what we're here for :)

Jun 18 '07 #24

P: n/a
Jo
Jo wrote:
Andre Kostur wrote:
>Jo <jo@mutools.comwrote in
>>But i prefer not to use RTTI for the reasons mentioned in the earlier
post.


Next item.... measure, measure, measure. How do you know that your
own RTTI method is ligher-weight than the one that the compiler is
using?


True.

That's also why i asked about the cost of RTTI.

At the moment i don't have a clue.

Well, i've been experimenting a bit with dynamic_cast and must say it's
a nice feature.

Why is it that i read al these warnings about RTTI, not only about the
overhead, but also about "it's better not to use it".

Didn't find some explicit reasoning yet.

Jun 18 '07 #25

P: n/a
Jo
Jo wrote:
Jo wrote:
>Andre Kostur wrote:
>>Jo <jo@mutools.comwrote in

But i prefer not to use RTTI for the reasons mentioned in the earlier
post.

Next item.... measure, measure, measure. How do you know that your
own RTTI method is ligher-weight than the one that the compiler is
using?

True.

That's also why i asked about the cost of RTTI.

At the moment i don't have a clue.

Well, i've been experimenting a bit with dynamic_cast and must say
it's a nice feature.

Why is it that i read al these warnings about RTTI, not only about the
overhead, but also about "it's better not to use it".

I'm sorry, must be more precise: the warnings i read is more like
"better not to use it, unless really necessary".
>
Didn't find some explicit reasoning yet.
Jun 18 '07 #26

P: n/a
Jo <jo@mutools.comwrote in
news:6G*********************@phobos.telenet-ops.be:
Jo wrote:
>Jo wrote:
>>Andre Kostur wrote:

Jo <jo@mutools.comwrote in

But i prefer not to use RTTI for the reasons mentioned in the
earlier post.

Next item.... measure, measure, measure. How do you know that your
own RTTI method is ligher-weight than the one that the compiler is
using?

True.

That's also why i asked about the cost of RTTI.

At the moment i don't have a clue.

Well, i've been experimenting a bit with dynamic_cast and must say
it's a nice feature.

Why is it that i read al these warnings about RTTI, not only about
the overhead, but also about "it's better not to use it".


I'm sorry, must be more precise: the warnings i read is more like
"better not to use it, unless really necessary".
Performance-wise there is a certain cost to using it, but I don't think
it would be any heavier than attempting to implement your own. I think
this is really a QoI (Quality of Implementation) issue with whatever
compiler you're using.

However, a necessity to using dynamic_cast<tends to indicate a
potential flaw in your design. You should really be asking yourself why
you need to know the derived type. Couldn't it be better implemented as
a virtual function in the base class? So if the function you're
attempting to call in C is already virtual in A, then you don't need
explicitly know that you're working with a C object. Just call the
function through the A* and let the compiler follow the virtual dispatch
to call the real function.
Jun 18 '07 #27

P: n/a
On Jun 18, 6:17 pm, Jo <jo.lan...@telenet.bewrote:
class A {
public:
char text_a[100];
A() { *text_a=0; }
~A() {}};
//-----------------------------------------------------------------------------
class B {
public:
char text_b[100];
B() { *text_b=0; }
~B() {}};
//-----------------------------------------------------------------------------
class C : public A, public B {
public:
char text_c[100];
C() { *text_c=0; }
~C() {}};
//-------------------------------------------------------------------------------
void test() {
B *bp1,*bp2;
C c,*cp1,*cp2,*cp3;
void *p;

strcpy(c.text_a,"hello a");
strcpy(c.text_b,"hello b");
strcpy(c.text_c,"hello c");
cp1=&c;
p=cp1;
bp1=cp1; // ok
bp2=(B*)p; // resulting bp2 is WRONG!
And why should it be otherwise? You do something illegal,
specified as undefined behavior, and you get undefined behavior.

p is a void*. About the only things you can do with a void*
(except copying it) is cast it back to the *original* type, or
to a character type to access the underlying raw memory. Since
the original type was C*, casting it back to anything but a C*
is undefined behavior, and anything can happen.

bp2 = (B*)(C*)p ;

should work.

[...]
So the hot spot is the bp2=(B*)p;
What's wrong with that???
It's forbidden by the language.

[...]
If so, please give me a link to where i can study this right.
Not a link, but in the standard:

§4.10 Pointer conversions (paragraph 2):

An rvalue of type "pointer to cv T ," where T is an
object type, can be converted to an rvalue of type
"pointer to cv void". The result of converting a
"pointer to cv T" to a "pointer to cv void" points to
the start of the storage location where the object of
type T resides, as if the object is a most derived
object (1.8) of type T (that is, not a base class
subobject). The null pointer value is converted to the
null pointer value of the destination type.

And §5.2.9 Static cast (paragraph 11):

An rvalue of type "pointer to cv1 void" can be converted
to an rvalue of type "pointer to cv2 T", where T is an
object type and cv2 is the same sv-qualifiation as, or
greater cv-qualifiation than, cv1. The null pointer
value is converted to the null pointer value of the
destination type. A value of type pointer to object
converted to "pointer to cv void" and back, possibly
with different cv-qualifiation, shall have its original
value.

You'll note, in that last paragraph, that nothing is said
about the value when converting back to a different type;
the results are not defined by the standard, and can be
anything.

--
James Kanze (GABI Software, from CAI) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Jun 19 '07 #28

P: n/a
Jo
Andre Kostur wrote:
>Jo <jo@mutools.comwrote in
news:6G*********************@phobos.telenet-ops.be:
>>Jo wrote:
>>>Jo wrote:

Andre Kostur wrote:

>Jo <jo@mutools.comwrote in
>
>
>
>>But i prefer not to use RTTI for the reasons mentioned in the
>>earlier post.
>>
>>
>
>Next item.... measure, measure, measure. How do you know that your
>own RTTI method is ligher-weight than the one that the compiler is
>using?
>
>

True.

That's also why i asked about the cost of RTTI.

At the moment i don't have a clue.

Well, i've been experimenting a bit with dynamic_cast and must say
it's a nice feature.

Why is it that i read al these warnings about RTTI, not only about
the overhead, but also about "it's better not to use it".

I'm sorry, must be more precise: the warnings i read is more like
"better not to use it, unless really necessary".


Performance-wise there is a certain cost to using it, but I don't think
it would be any heavier than attempting to implement your own. I think
this is really a QoI (Quality of Implementation) issue with whatever
compiler you're using.

However, a necessity to using dynamic_cast<tends to indicate a
potential flaw in your design. You should really be asking yourself why
you need to know the derived type. Couldn't it be better implemented as
a virtual function in the base class? So if the function you're
attempting to call in C is already virtual in A, then you don't need
explicitly know that you're working with a C object. Just call the
function through the A* and let the compiler follow the virtual dispatch
to call the real function.
Thought 1: If every very base class should foresee all virtual functions
that are coming down the hierarchic tree (or do they say "up" the tree?
still confused), then these base classes get much too much cluttered
with non-relevant virtual funcs!

Thought 2: If class C is derived from class A and B, both having virtual
funcs, and i have an A pointer to a C object, and need to call a B func
of the C object, then do you propose to copy all the virtuals of B in A?
I'm confident the answer is no.

So these are two common situations where i need to know what kind of
object i'm dealing with.

In fact, the two thoughts can be summarized to this common reason to use
RTTI: to avoid "virtual clutter".

My conclusion: If you think about it pure conceptually, i don't see
what's wrong with using RTTI.Using virtuals to avoid RTTI is one good
strategy. Using RTTI to avoid virtuals is another good strategy. It are
two equivalent techniques, imo, one better in situation a, the other
better in situation b.

Jun 19 '07 #29

P: n/a
On Jun 18, 10:17 pm, Jo <j...@mutools.comwrote:
John Harrison wrote:
[...]
dynamic_cast does do base to derived conversion, that's it's main use.
Where are you getting your information?
I got that fromhttp://www.cplusplus.com/doc/tutorial/typecasting.html
But i was just reading another webpage where they indeed use
base-to-derived dynamic casts...
Did i misunderstand the cplusplus page?
You didn't read the sentence completely. It says: "The second
conversion in this piece of code would produce a compilation
error since base-to-derived conversions are not allowed with
dynamic_cast UNLESS the base class is polymorphic." Note that
final clause, with the unless. In their example, the base class
didn't have any virtual functions, so using dynamic_cast to the
derived would be a compiler error.

Generally speaking, you should avoid using void* as much as
possible, and use dynamic_cast exclusively for moving between
classes in a hierarchy. When you can't avoid void*, e.g.
because of legacy or C interfaces, always cast back to exactly
the type of pointer you originally had, then go from there.
Note that often, this means casting to a specific type before
casting to void*. For example (using pthread_create as an
example, but it could be any C API function that takes a pointer
to function and a void*):

class Thread
{
public:
virtual ~Thread() {}
virtual void* run() = 0 ;
} ;

extern "C"
void*
threadStarter( void* p )
{
Thread* t = static_cast< Thread* >( p ) ;
return t->run() ;
}

void
f()
{
// MyThread derives from Thread...
pthread_t t ;
pthread_create( &t, NULL, &threadStarter, new MyThread ) ;
}

As written, this is undefined behavior. The last code line must
be:

pthread_create(
&t, NULL, &threadStarter, static_cast< Thread* >( new
MyThread ) ) ;

(The problem is, that as it is undefined behavior, it might seem
to work some of the time anyway. On most implementations, for
example, it will seem to work as long as only single inheritance
is involved.)

Note that the solution here does NOT involve dynamic_cast; you
don't want a pointer to the most derived type, but to the type
to which the void* will ultimately be converted. (The
documentation of "threadStarter" should specify this.)

--
James Kanze (GABI Software, from CAI) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Jun 19 '07 #30

P: n/a
Jo
James Kanze wrote:
>On Jun 18, 10:17 pm, Jo <j...@mutools.comwrote:

>>John Harrison wrote:


[...]

>>>dynamic_cast does do base to derived conversion, that's it's main use.
Where are you getting your information?



>>I got that fromhttp://www.cplusplus.com/doc/tutorial/typecasting.html



>>But i was just reading another webpage where they indeed use
base-to-derived dynamic casts...



>>Did i misunderstand the cplusplus page?


You didn't read the sentence completely. It says: "The second
conversion in this piece of code would produce a compilation
error since base-to-derived conversions are not allowed with
dynamic_cast UNLESS the base class is polymorphic." Note that
final clause, with the unless. In their example, the base class
didn't have any virtual functions, so using dynamic_cast to the
derived would be a compiler error.
I understand.
>Generally speaking, you should avoid using void* as much as
possible, and use dynamic_cast exclusively for moving between
classes in a hierarchy. When you can't avoid void*, e.g.
because of legacy or C interfaces, always cast back to exactly
the type of pointer you originally had, then go from there.
Note that often, this means casting to a specific type before
casting to void*. For example (using pthread_create as an
example, but it could be any C API function that takes a pointer
to function and a void*):

class Thread
{
public:
virtual ~Thread() {}
virtual void* run() = 0 ;
} ;

extern "C"
void*
threadStarter( void* p )
{
Thread* t = static_cast< Thread* >( p ) ;
return t->run() ;
}

void
f()
{
// MyThread derives from Thread...
pthread_t t ;
pthread_create( &t, NULL, &threadStarter, new MyThread ) ;
}

As written, this is undefined behavior. The last code line must
be:

pthread_create(
&t, NULL, &threadStarter, static_cast< Thread* >( new
MyThread ) ) ;

(The problem is, that as it is undefined behavior, it might seem
to work some of the time anyway. On most implementations, for
example, it will seem to work as long as only single inheritance
is involved.)
Indeed, and that's why i was so confused, because i've been using this
many times. (too many it seems now!)

I guess that, by coincidence, i never ran into the combination of using
a void* and multiple inherited objects, and so that's why i only
encountered the errors now.

I'm happy that this has been sorted out, and i completely understand the
reasons beyond it.

All sounds logical.

I'll avoid using void* as much as possible!

And i should use more static_cast / dynamic_cast instead of the old
c-style casts, because they're more error-conscious.
(a pity of that quircky syntax though)

Jun 19 '07 #31

P: n/a
Jo <jo@mutools.comwrote in
news:fY*********************@phobos.telenet-ops.be:
Andre Kostur wrote:
>>Jo <jo@mutools.comwrote in
news:6G*********************@phobos.telenet-ops.be:
[snip]
>>However, a necessity to using dynamic_cast<tends to indicate a
potential flaw in your design. You should really be asking yourself
why you need to know the derived type. Couldn't it be better
implemented as a virtual function in the base class? So if the
function you're attempting to call in C is already virtual in A, then
you don't need explicitly know that you're working with a C object.
Just call the function through the A* and let the compiler follow the
virtual dispatch to call the real function.

Thought 1: If every very base class should foresee all virtual
functions that are coming down the hierarchic tree (or do they say
"up" the tree? still confused), then these base classes get much too
much cluttered with non-relevant virtual funcs!
Like most trees in computing, they're usually thought of as growing
downwards. So the most base class is usually written at the top, then
the next derived class below it, etc. Thus the terms "upcasting" and
"downcasting" through an inheritance hierarchy. Upcasting is free,
downcasting requires a cast.

Note that I said "tends to indicate a potential flaw". There are cases
where it may make sense. I'm not sure I can explain how you'd know when
to use it.

Perhaps you really wanted composition where you're using inheritance?

Thought 2: If class C is derived from class A and B, both having
virtual funcs, and i have an A pointer to a C object, and need to call
a B func of the C object, then do you propose to copy all the virtuals
of B in A? I'm confident the answer is no.
No, but whatever function that's going to use the B functionality should
take a B* (or B&). Or perhaps the A class should really have a B as a
member, not as a parent. If you really need to access it through an A*,
perhaps the design is flawed.
So these are two common situations where i need to know what kind of
object i'm dealing with.

In fact, the two thoughts can be summarized to this common reason to
use RTTI: to avoid "virtual clutter".

My conclusion: If you think about it pure conceptually, i don't see
what's wrong with using RTTI.Using virtuals to avoid RTTI is one good
strategy. Using RTTI to avoid virtuals is another good strategy. It
are two equivalent techniques, imo, one better in situation a, the
other better in situation b.
You're blurring the idea of Object Oriented programming (not to say that
you must use OO, C++ supports multiparadigm programming...). The idea
behind inheritance and virtual functions is that it should be eliminating
the need to know exactly which derived class you're dealing with.
Jun 19 '07 #32

This discussion thread is closed

Replies have been disabled for this discussion.