Click to See Complete Forum and Search --> : Some questions of Inheritance
ctyuang
July 8th, 2002, 02:52 AM
I define my classes as follow
class A
{
};
class B : public A
{
public:
void functionB(void);
};
main()
{
A* objectB=new B;
objectB->functionB(); // this should be OK
A* objectA=new A;
((B*)objectA)->functionB(); // this is OK too, why?
}
suppose functionB() is the member of class B, it shouldnt be right to be called by A object. However I did it no problem, why?
Any side effect if I do this?
dude_1967
July 8th, 2002, 03:20 AM
An interesting example ctuyang.
It is improper use of inheritance and, in my opinion, not OK. It will compile on some compilers and maybe run sometimes for some classes with certain functions and characteristics. But it is not a correct use of inheritance.
The problem is that you have casted an object of type A into an object of type B. This can not work since the casted object (of true type A*) will not have all the members which it needs to be a B*.
Study the following code example. You see that in the third function call, the behavior is maybe not what you expected.
Chris.
#include <iostream>
using namespace std;
class A
{
private:
int m_na;
public:
A()
{
m_na = 1;
}
public:
void functionA(void)
{
cout << m_na << endl;
cout << "AAA" << endl;
}
};
class B : public A
{
private:
int m_nb;
public:
B()
{
m_nb = 2;
}
void functionB(void)
{
cout << m_nb << endl;
cout << "BBB" << endl;
}
};
int main(int argc, char* argv[])
{
A* object_true_A = new A;
object_true_A->functionA(); // this is definitely OK
B* objectB = new B;
objectB->functionB(); // this should be OK
A* objectA = new A;
((B*)objectA)->functionB(); // this is OK too, why?
return 1;
}
:)
Alexis Moshinsky
July 8th, 2002, 03:27 AM
Hi,
both of cases are not Ok. But there is difference between them.
In first case You will get compilation error - something like
'functionB is not a member of class A'.
The second case is more complicated. Please imagine structs
with only data, no functions like:
struct A
{
char ch;
};
struct B
{
struct A;
...
};
What will happen when You access memory after instance of A ?
The result is undefined.
It could be access violation/segmentation fault exception,
You can overwrite useful data,
it can be nothing wrong.
Exactly the same will happen when You cast real A to B.
The code will pass compilation, but run-time result is undefined.
Pete Bourner
July 8th, 2002, 03:28 AM
Hi,
This is actually nothing to do with inheritance, but rather a problem with casting. The problem is basically the need for C++ to be back-compatible with C. In C there is only one way of expressing a cast from one type to another, even though there are several different types of casting.
When C++ was created, these different types of casting were given their own names, creating the following:
static_cast<newType>( ... )
dynamic_cast<newType>( ... )
reinterpret_cast<newType>( ... )
const_cast<newType>( ... )
Where '...' specifies what is to be cast, and 'newType' is the type to cast to.
Unfortunately, in order to make C++ compatible with legacy C code, the old style,
(newType)( ... )
was kept. This can lead to problems, as the old style casts can mean any of the four types. The compiler simply chooses the type which allows the code to be compiled.
In your code, in the line
((B*)objectA)->functionB(); // this is OK too, why?
the compiler chooses to use reinterpret_cast. This means it considers 'objectA' to be of type B* regardless of what it actually is in reality. As it now thinks it is B*, it can call functionB() on it. However, the effect this will actually have when you run your program will likely be unpredictable and could have extremely nasty side-effects. And I mean extremely nasty. As an example of quite how nast reinterpret_cast can be, try compiling the following:
int* number = new int;
((B*)number)->function();
which will compile, but god knows what it would do!
I would recommend that you never use old style casts, but pick the one you need from the list above. I would also recommend that you never use reinterpret_cast or const_cast either.
Hope this helps,
Pete
cup
July 8th, 2002, 03:36 AM
I'm surprised that
objectB->functionB(); // this should be OK
builds with a C++ compiler. I can understand it building with a C compiler but not with a C++ compiler. With the older C compilers, members were just offsets so you could do something silly like
struct AA
{
int a;
int b;
} abc;
struct BB
{
int c;
} def;
def.b = 9;
This shouldn't work on the newer compilers but some of the pre-ANSI C compilers will take it.
As Pete Bourner says, use the new style casts. It is a lot more typing but it makes you think about what you are doing instead of just forcing the coercion.
Graham
July 8th, 2002, 03:40 AM
They're also easier to find after you've done the redesign to remove the need for them.
A* objectB=new B;
objectB->functionB(); // this should be OK
Absolutely not - functionB is not a member of A, so this should not compile.
A* objectA=new A;
((B*)objectA)->functionB(); // this is OK too, why?
A lucky accident from the way the compiler works. Don't rely on it - it's even more wrong than the previous (if you can have "even more wrong").
dude_1967
July 8th, 2002, 03:57 AM
Dear ctyuang,
All of the comments up to this point are correct. I like to think of it this way: Although you can cast an object to or use an object as less than it is (B calls stuff from A), you can can never cast an object to more than it is (A calls stuff from B).
It is important for you to understand so I have supplied more example code, also addressing the good comments up to this point.
By the way, the original code that you suppled can not compile. My original comments were pertaining to the cast of A* to B*.
OK then, the sample code:
// This is definitely OK
A* object_true_A = new A;
object_true_A->functionA();
// This definitely OK
B* object_true_B = new B;
object_true_B->functionB();
// This is also OK since B can access "all of A".
object_true_B->functionA();
A* object_A_casted_to_B = new A;
// Now uses modern cast.
// It will compile, but it's absolutely not OK!!!
reinterpret_cast<B*>(object_A_casted_to_B)->functionB();
Chris.
:)
ctyuang
July 8th, 2002, 04:00 AM
thank you, this example reveals clearly my problem. Its true the following code doesnt work:
// List 1
/////////
A* objectA = new A;
((B*)objectA)->functionB();
// functionB() is not A object member.
however this code work.
// List 2
//////////
A* objectA = new B;
((B*)objectA)->functionB();
// function B is B object member
List 2 code work because objectA is just a pointer, it point to its children object, which is (*objectA).
Pete Bourner
July 8th, 2002, 04:20 AM
// List 2
//////////
A* objectA = new B;
((B*)objectA)->functionB();
// function B is B object member
The above code works because the casting is now performing a dynamic_cast. This is used to convert from base class pointers to derived class pointers. In the example above, although 'objectA' is of type A*, it actually points to an object of type B, which is derived from A. Therefore, casting objectA to B* using a dynamic_cast is a valid operation.
I think this shows the importance of using the new, explicit cast syntax, as this would make it clear what the above is doing. In this case you should write:
A* objectA = new B;
(dynamic_cast<B*>( objectA ))->functionB();
Pete
Alexis Moshinsky
July 8th, 2002, 04:22 AM
The code like
A* a = new A;
((B*)a)->functionB();
does not cause a crash becouse of the same reason as follow code won't cause crash:
class B
{
public:
void fun() {}
};
B* b = 0;
b->fun();
Addressing to an object is an high-level abstraction. Compiler
simply put the address of 'b' as function parameter.
So far this->not_access_illegal_address_in_memory, Your
application will continue to run.
ctyuang
July 8th, 2002, 04:48 AM
I tried dynamic_cast. Its too bad, compile error!
I have tried static_cast, and reinterpret_cast, they are OK, but not dynamic_cast and const_cast.
I will do further reading on casting.
Thank you to all of you. HAVE A NICE DAY!!!!
Pete Bourner
July 8th, 2002, 04:58 AM
A* objectA = new B;
(dynamic_cast<B*>( objectA ))->functionB();
Whoops - the dynamic_cast doesn't work does it? My mistake - you can only use dynamic_cast to convert from classes which have virtual functions ( also called polymorphic classes ).
In the example given here, A is not polymorphic as it has no functions at all, let all virtual ones.
static_cast will work, and in this case will be valid as objectA really a B. reinterpret_cast will also work for the same reason, but should still be avoided as there is no compile time protection against bad usage.
Thanks for pointing out my error,
Pete.
Graham
July 8th, 2002, 04:59 AM
If you're using VC++, then you have to turn on RTTI before you can use dynamic_cast. (Project/Settings... C/C++ tab, select "C++ Language" in teh drop down, and put a check mark beside "Enable run-time type information").
codeguru.com
Copyright Internet.com Inc., All Rights Reserved.