?
Solved

Strange class behaviour

Posted on 2003-03-27
29
Medium Priority
?
277 Views
Last Modified: 2010-05-19
Hi,
  Here is the sample code of C++ which I am not able to follow. I am a beginer in C++ development.
The output of the program is "Derived" I want to know the reason for this ? Usually upcasting works but this downcasting is really strange for me. The derived object is not created but still it makes a call to Derived::f2()

#include <iostream.h>
class Base{
public:
        int f2(){
                cout << "base";
                return 0;
        }
};


class Derived: public Base{
        public:
               
                int f2(){
                cout << " derived ";
                return 0;
        }
};
main()
{
        Derived *d = (Derived*)new Base;
       
        d->f2();
 
}
0
Comment
Question by:akalmani
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 9
  • 9
  • 5
  • +4
29 Comments
 
LVL 4

Expert Comment

by:n_fortynine
ID: 8222781
adding the keyword virtual before the function in Base type allows you to cast between parent and child classes.
0
 
LVL 1

Expert Comment

by:beavis_shenzhen
ID: 8223016
Dont cast base clss pointer to derived class type.
something unpredictable will happen after that.
0
 
LVL 4

Expert Comment

by:n_fortynine
ID: 8223072
>> Dont cast base clss pointer to derived class type.
>> something unpredictable will happen after that.
This is not unpredictable. The method wasn't declared virtual, so f2() from Base wasn't invoked but f2() from Derived instead.
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
LVL 2

Expert Comment

by:bkrahmer
ID: 8223077
huh-huh.  Beavis is right.  that wuz cool.

#include <iostream.h>
class Base
{
public:
  virtual int f2()
  {
    cout << "base";
    return 0;
  }
};

class Derived: public Base
{
public:
  int f2() //virtual not needed here
  {
    cout << " derived ";
    return 0;
  }
};
 
main()
{
  Derived d;
  Base *b = &d;

  b->f2();
}
0
 
LVL 1

Expert Comment

by:beavis_shenzhen
ID: 8223114
My understanding:
when "derived *pDer=(derived *)pBase" is compiled ,its not a just static cast operation, all the members and methods that is in derived class exceptional and not exist in base class will be created on the heap.
so when you call pDerived->Function(), the new created method will be called,even if there is no such method in base class.
0
 
LVL 30

Expert Comment

by:Mayank S
ID: 8223209
'Derived * d' is a pointer of the DERIVED class, and so, in the case mentioned above in the question, it will be bound during compile-time itself to the f2 () function in the 'Derived' class, because it will over-ride the f2 () function in the base-class. There is no need to look at it a second time.

How-ever, it would've been more interesting if you asked about something like this:

#include <iostream.h>

class Base
{
public:
  int f2 ()
  {
    cout << "Base. " 
    return 0 ;

  } // end of f2 ()

} ; // class-definition over
 
class Derived : public Base
{
public:
  int f2 ()
  {
    cout << "Derived. " ;
    return 0 ;

  } // end of f2 ()

} ; // class-definition over
 
int main ()
{
  Base * d = ( Base* ) new Derived ;
  d -> f2 () ;

} // end of main ()

In this case, the output is:

Base.

Here also, the binding is done at compile-time itself because d is a pointer of the 'Base' class type, and so, the function-call invokes the function in the base-class. However, to avoid this to happen, and to let it bind dynamically, we can make the function in the base class as virtual.

class Base
{
public:
  virtual int f2 ()
  {
    cout << "Base. " 
    return 0 ;

  } // end of f2 ()

} ; // class-definition over

In this case, the output will be:

Dervied.

Here, the binding is done a run-time (dynamic-binding).

You may also notice that in this case, the function in the base-class is not called at all, so why not altogether remove it from there and let f2 () be only in the derived-class. The answer is no, because if a function f2 () is not there in class 'Base', then d -> f2 () will give a compile-time error as 'd' is after-all a base-class pointer. But if you want that the binding be always dynamic and the base-class function should not be called, you can be given the freedom to remove not the declaration, but the definition of the f2 () function in the base-class, by making it a pure-virtual function as:

virtual int f2 () = 0 ;

In this case, f2 () is a pure-virtual function. But you will not be allowed to instantiate the class 'Base' by objects, then, because Ellis and Stroustrup strengthen the definition of an abstract class (one which cannot have its objects declared) as one which must have at-least one pure-virtual function. However, you can declare pointers of 'Base' type, inherit the base-class by some derived-class, declare objects of the derived-class and allow the base-class pointer to point to it.

Hope you understood the concepts.

Mayank.
0
 
LVL 2

Expert Comment

by:bkrahmer
ID: 8223232
The reason derived is being called is because d is a derived pointer.  You code is explicitly saying call d->f2().  Beavis, I am pretty sure your theory on heap creation of members and methods is incorrect.  The only reason virtual functions work is because of the virtual function table.  Without virtual functions, you are always going to get the function in the most specific class which has that function defined called first.

http://www.parashift.com/c++-faq-lite/virtual-functions.html

brian
0
 
LVL 30

Expert Comment

by:Mayank S
ID: 8223289
>> Without virtual functions, you are always going to get the function in the most specific class which has that function defined called first

Exactly,

Mayank.
0
 
LVL 1

Expert Comment

by:beavis_shenzhen
ID: 8223320
but how to explain this:
class Base
{
public:
Base(){}
~Base(){}
}
class Derive:Base
{
public:
       void DerMethod(void){cout<<"dervied method";}
}
main()
{
 Base *pBase=new Base();
 Derive *PDerive=(Derive*)pBase;
pDerive->DerMethod();
}
0
 
LVL 30

Expert Comment

by:Mayank S
ID: 8223433
It will run, and it will display "derived method", just because its been called with the 'pDerive' pointer. The compiler will just see that 'pDerive' is a dervied-class pointer and irrespective of where it is pointing to, the binding will again be done at compile time with the 'DerMethod ()' method. Also, you will not be able to write 'pBase -> DerMethod () ;' I think you already know that.

Mayank.
0
 
LVL 1

Expert Comment

by:beavis_shenzhen
ID: 8223471
but I didnot construct a Derive class, what I have is a pointer of Base class. and there is no such a method in Base class.
so something of constructing  must happened in cast-style
"Derive *PDerive=(Derive*)pBase;"
0
 
LVL 30

Expert Comment

by:Mayank S
ID: 8223545
Beavis,

When the compiler sees 'pDerive -> DerMethod () ;', all it knows is that the pointer 'pDerive' is calling the 'DerMethod ()' and then it checks to see if it is vaid or not. It checks that 'DerMethod ()' is defined in the 'Derived' class, and that 'pDerive' is a pointer of type 'Derived', and so it concludes that the call is valid, hence it binds it statically here itself. Please note at this time, the binding is not dynamic. So at this time, it does not care while compiling whether 'pDerive' is actually pointing to an object of the class 'Base' or the class 'Derived'. Getting my point??

Mayank.
0
 
LVL 12

Expert Comment

by:Salte
ID: 8223634
This works because the virtual call mechanism isn't used.

The pointer is a pointer to a derived object. Through C-style casting you have placed a Base object in that pointer and a production type program with this error would usually crash.

When you call d -> f2() the compiler see the pointer is a pointer of type Derived and so it will call Derived::f2(). Note that the function f2 is NOT virtual and so there is no vtable entry for it. The compiler will therefore not use the virtual function call mechanism.

Defining f2 as virtual in Base will probably cause the program to call Base::f2 instead since the object is really of type Base. However, it is a serious bug to use C-style cast this way, since Derived typically has more data members than Base has and if you tried to access those you would access undefined memory since the object really is a Base object and doesn't have those members.

If you had used dynamic_cast<> as you should instead of C-style cast the pointer would be a NULL pointer and the code would hopefully work better. It is possible that the compiler would still call Derived::f2 and have a this pointer of NULL but again that is also considered to be 'undefined' by standard C++ and any result or activity in the execution of that function call is more or less random and can not be counted on to work for any purpose.

Alf
0
 
LVL 1

Expert Comment

by:beavis_shenzhen
ID: 8223718
Mayankeagle:
    I understand the validation check of method call.
    but what happened int the binding.The members and
Method of derived class doesnot exist in the base class.
so if there is no explicit construction of derived class.
how does the method could be called correctly.
this is what I cant understand.
   Its weekend in my contry, so I will discuss it two days later.
           Beavis
0
 
LVL 30

Accepted Solution

by:
Mayank S earned 80 total points
ID: 8223824
Beavis,

>> but what happened in the binding

The binding is not dynamic, but static, as I said. So it is done at COMPILE-TIME, meaning that it is not relevant what kind of object the pointer is pointing to.

>> The members and method of derived class does not exist in the base class

Yeah! Let them not exist. It is being called by the derived class pointer. If it was called using the base class pointer as: 'pBase -> DerMethod () ;' then there'd be an error.

>> how does the method could be called correctly

Like I said, it is static-binding. That is why. The derived class pointer calling a derived class method will always be legal during compiling.

I suggest you should also read some more books on this matter if you're still unclear.

Have a nice weekend.

Mayank.
0
 
LVL 12

Expert Comment

by:Salte
ID: 8223861
beavis said:

Mayankeagle:
   I understand the validation check of method call.
   but what happened int the binding.The members and
Method of derived class doesnot exist in the base class.
so if there is no explicit construction of derived class.

[my emphasis]
    how does the method could be called correctly.
    this is what I cant understand.

  Its weekend in my contry, so I will discuss it two days later.
          Beavis

I say:

That is exactly the point.  It isn't called correctly, it is called very wrongly.

so there isn't really anything to understand, the code is bogus and might work on one computer and might work with exactly the classes as described but in general code like this will NOT work and will crash very badly.

Alf
0
 
LVL 30

Expert Comment

by:Mayank S
ID: 8223883
>> the code is bogus

Very correct.

Also, thos very code might create more problems if the two classes have some data-members and 'DerMethod ()' happens to access a data-member defined in the derived class (but not in the base class).

I never use derived-class pointers to access base-class objects. The results maybe erroneous! My teacher had very strong opinions in these matters and always told me that its ok to access dervied-class objects with base-class pointers, but not vice-versa.

Cheers,

Mayank.
0
 
LVL 12

Expert Comment

by:andrewjb
ID: 8224258
The original would work even if Derived isn't actually derived from base

i.e. (below)

You're simply doing a completely and utterly illegal and wrong cast.

In fact, you don't even need to define f2() in Base

The following works, 'by luck'

Try accessing some member variables - that'll screw it up.

class Base{
};

class Derived
{       public:
               
               int f2(){
               cout << " derived ";
       }
};

Derived *d = (Derived*)new Base;
d->f2();

0
 
LVL 30

Expert Comment

by:Mayank S
ID: 8224272
>> Try accessing some member variables - that'll screw it up.

yeah, it will. That's what I said in my last comment too..
0
 
LVL 12

Expert Comment

by:Salte
ID: 8224340
which is (one of the reasons for) why it is bogus.

In other words the question is phrased wrong.

It is not a mystery on how it can be correct when it isn't correct in the first place, so the question is not how it can be correct but IF it is correct and the answer to that "if" question is clearly "it isn't".

Alf
0
 
LVL 1

Expert Comment

by:beavis_shenzhen
ID: 8235640
I agree with Salte's explain.
The method is called correctly by luck.
and the question is one that cant be answered correctly.
0
 
LVL 12

Expert Comment

by:Salte
ID: 8237140
>> The method is called correctly by luck.

no, the method is NOT called correctly. Neither by luck nor by any other way. It simply is called wrongly.

There's nothing correct about that call.

If you by "correct" mean that you intend to call Derived::f2 and that is the function that is called then that might be "correct" in that sense but that isn't of much help. You can do something like this also:

void func(int k)
{
   cout << "func(int) called" << endl;
}

void func(double x)
{
   cout << "func(double) called" << endl;
}

void do_something()
{
   double y = 3.14;

   func(*(int *)& y);
}

Guess what? The func(int) called is the text you get out, but the function is NOT called correctly, if you tried to actually print out that 'int' value you would get some value being equal to whatever the bits of the double would map into, it would be very different from 3 or whatever.

Now, it would be even worse if you did it the other way around:

void do_something_else()
{
   int k = 3;
   func(*(double *) & k);
}

Here you would take sizeof(int) bytes from the integer k which has the value 3 and sizeof(double)-sizeof(int) bytes from undefined portion of your stack as the value for the double. You would get random garbage if you tried to print out that double value in the function.

Still, it would be the func(double) that was called and if you call that "correct" then it is correct but in my vocabulary there's nothing correct about that call.

Your previous code is exactly the same except you use pointers to classes and it might obscure the issue but it really is the same error.

Alf
0
 
LVL 1

Expert Comment

by:beavis_shenzhen
ID: 8237424
For Satle:
    Not by luck?but how to explain its correct output,even if it access some data member of derived class.
    Pointer is wrongly casted , after that error,method is called and correctly executed, I cant understand,so I have to say "oh,lucky call".
0
 
LVL 30

Expert Comment

by:Mayank S
ID: 8237435
>> how to explain its correct output,even if it access some data member of derived cla

When did that happen? I think we said that it'll be wrong to do so and I don't know what the result will be because I've never done such an illegal thing!

Mayank.
0
 
LVL 1

Expert Comment

by:beavis_shenzhen
ID: 8237475
the following code is executed correctly:
class A
{
public:
A(){};
~A(){};
}
//note: class B has nothing to do with class A
class B
{
public:
int m_iCount;
void Fook(void){printf("the value is:%d",m_iCount);}
}
main()
{
B *pB;
A *pA=new A();
pB=(B*)pA;
pB->m_iCount=10;
pB->Fook();
}
0
 
LVL 12

Expert Comment

by:Salte
ID: 8237539
The output may be 'correct' due to some random implementation details of your compiler that causes some random output that happen to coincide with what you expect. That doesn't make it any more correct than if it had printed something else. It isn't the proper output due to correct code. The code is wrong and any output from that program is 'undefined' and whatever output it gives is wrong even if it by some random coincidence happened to produce the correct result.

If you made a program to compute a + b and you compute it by this method:

int aplusb = a * 2 + b;

Then you might discover that if a == 0 and b == any value the result is 'correct' but the result is still wrong, something you can see if you give a value of a that is non-zero.

Just because it gives you the result you expect in one platform on one compiler doesn't mean it is correct. If the code is wrong the result it produces is 'undefined' and when talking about programming languages and definitions of them and standards etc the term 'undefined' is very well defined. It means that whatever output that program produces it might be what you expect or it might be something completely different - in EITHER case it is wrong and you can NEVER trust the output from such a program. If it happened to be what you expect it is still not correct. In a different implementation the program might crash or cause some other unexpected error.

It is fairly easy to write code that does not produce undefined behavior so it is very uninteresting to dicuss features of programs that do produce undefined behavior and it is therefore usually assumed that code is always well defined when talking about 'correct' or 'not correct'. Since your code is undefined the issue about correctness doesn't even come into the picture since a precondition for dicsussing correct/not correct code is not present.

So, to give you the short explanation, your code is wrong even if it is right. I.e. even if the code produces the output you expect it is still wrong.

To make the code right you must remove the things that causes it to give undefined behavior. Accessing non-existent members etc is one cause of undefined behavior, C-style casting an object to a type which it cannot possible be cast to is another. This is one of the reasons why C-style casting should be used with caution in a C++ program. In C you can essentially cast a pointer of any type to a pointer of any other type even if it doesn't make sense to do so. If you create a Base object and then cast it to a Derived type that is one of those situations. If you happen to have a Derived object and a Base pointer pointing to it you CAN make such a cast and that is why both C++ and C allows you to do so in certain situations but the code you provide isn't one of those situations.

Consider:

Base * b = new Derived;

Derived * d = (Derived *)b;

The code above IS valid C and does NOT produce undefined result. This is the reason why C allows such a cast (from Base * to Derived *). C++ also allows it but if possible you should use the dynamic_cast instead which is safer. dynamic_cast uses the vtable of an object to verify that it really is of the proper type. You must have at least one virtual funciton in the baseclass to use dynamic_cast:

class Base {
public:
   virtual ~Base() {} // at least one virtual function

   ...
};

class Derived : public Base {
public:
  ....
};


Base * b = new Derived;

then if you at some later point do:

Derived * d = dynamic_cast<Derived *>(b);

You will either get a pointer of type Derivd * d pointing to b if b really is of a type that is Derived or derived from it or if b isn't (for example if it is of type Base) the value will be a null pointer.

In your case - using dynamic_cast<> you would get a NULL pointer instead of a pointer to the wrong type.

So dynamic_cast is safer than regular C style cast. However, it require that you have at least one virtual function in the base class (so that objects derived from this class has a vtable pointer). Dynamic cast uses that vtable pointer to check the type.

Alf
0
 
LVL 3

Author Comment

by:akalmani
ID: 8289098
Hi Everybody,
    First of all sorry for accepting the answers late. I also searched over some books and on the internet and had a discussion and found that Mayank's comment was right.

It is decided in the compile time and when u try to access the data members it will screw up and crash.

So Mayank thanks.
0
 
LVL 1

Expert Comment

by:beavis_shenzhen
ID: 8289263
AKALMANI:
    I suggest you give some points to salte.He did a lot to your question.
0
 
LVL 30

Expert Comment

by:Mayank S
ID: 8289871
You really should give some points to Alf (Salte) too.
0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction This article is the first in a series of articles about the C/C++ Visual Studio Express debugger.  It provides a quick start guide in using the debugger. Part 2 focuses on additional topics in breakpoints.  Lastly, Part 3 focuses on th…
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.
Suggested Courses

764 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question