Link to home
Start Free TrialLog in
Avatar of msjammu
msjammuFlag for United States of America

asked on

Pure Virtual functions and RTTI

This is the most basic question I ever have. Here is the code that when I run on my VC++ VS6.0 SP6, gives me the output given just below that code.

//CODE

#include <iostream>
#include <typeinfo>
using namespace std;


class Shape
{
public:
      Shape()
      {
            cout<<"Constructing Shape"<<endl;
      }
      ~Shape()
      {
            cout<<"Destroying Shape"<<endl;
      }
      virtual void draw() = 0;
};

class Line : public Shape
{
public:
      Line()
      {
            cout<<"Constructing Line"<<endl;
      }
      ~Line()
      {
            cout<<"Destroying Line"<<endl;
      }
      void draw()
      {
            cout << "**********"<<endl;
      }
};

class Rectangle : public Shape
{
public:
      Rectangle()
      {
            cout<<"Constructing Rectangle"<<endl;
      }
      ~Rectangle()
      {
            cout<<"Destroying Rectangle"<<endl;
      }
      void draw()
      {
            cout << "**********\n*        *\n*         *\n**********"<<endl;
      }
};

class Triangle : public Shape
{
public:
      Triangle()
      {
            cout<<"Constructing Triangle"<<endl;
      }
      ~Triangle()
      {
            cout<<"Destroying Triangle"<<endl;
      }
      void draw()
      {
            cout << "*\n* *\n*****"<<endl;
      }
};

class NullShape : public Shape
{
public:
      NullShape()
      {
            cout<<"Constructing NullShape"<<endl;
      }
      ~NullShape()
      {
            cout<<"Destroying NullShape"<<endl;
      }
      void draw()
      {
      }
};

Shape *generate()
{
      switch(rand() % 4)
      {
            case 0:
                  return new NullShape;
            case 1:
                  return new Line;
            case 2:
                  return new Triangle;
            case 3:
                  return new Rectangle;
      }
      return NULL;
}

int main()
{
      int i;
      Shape *shapePtr;
      

      for (i=0;i<10;i++)
      {
            shapePtr = generate();
            //cout << typeid(*shapePtr).name();
            shapePtr->draw();
            if (shapePtr  != NULL)
                  delete shapePtr;
            cout<<endl<<endl;
      }

      return 0;
}

//OUTPUT I GOT

Constructing Shape
Constructing Line
**********
Destroying Shape


Constructing Shape
Constructing Rectangle
**********
*        *
*         *
**********
Destroying Shape


Constructing Shape
Constructing Triangle
*
* *
*****
Destroying Shape


Constructing Shape
Constructing NullShape
Destroying Shape


Constructing Shape
Constructing Line
**********
Destroying Shape


Constructing Shape
Constructing NullShape
Destroying Shape


Constructing Shape
Constructing Triangle
*
* *
*****
Destroying Shape


Constructing Shape
Constructing Triangle
*
* *
*****
Destroying Shape


Constructing Shape
Constructing Triangle
*
* *
*****
Destroying Shape


Constructing Shape
Constructing NullShape
Destroying Shape
-------------------------------------------------

When each time an object of type derived is destroyed, that is pointed to by a base class pointer, I can see from the outut that only base class destructor is called. However, I guess it should be the corresponding derived class destructor becuase the actual object being destructed is of devied type. Why this is so Please explain.

Another thing if I remove comment statement in for loop in main function:

//cout << typeid(*shapePtr).name();

It does not gives me any names of the type of objects being pointed by the base class pointer as I guess it should be. However, it gives me a warning and a run time error:

Warning:

warning C4541: 'typeid' used on polymorphic type 'class Shape' with /GR-; unpredictable behavior may result

Run time Error:

Abnormal Program Termination (Retry, Debug, Teminate)

Please help.
Avatar of msjammu
msjammu
Flag of United States of America image

ASKER

I increase the points
ASKER CERTIFIED SOLUTION
Avatar of Member_2_1001466
Member_2_1001466

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Member_2_1001466
Member_2_1001466

Nice, even though I answered for 250 points already.
/GR- means that in the project options you don't include run time type information (RTTI). In that case you should not expect to get them when running. Change your project options
C/C++ tab and select C++ Language. There check "enable RTTI" and it should work.
Avatar of msjammu

ASKER

I did it already,
> I did it already,
What?

My first comment was only answering the destructor Q (Why only base is called).
The second only the RTTI part.

Or was that comment about the increasing of points?
Avatar of msjammu

ASKER

I already enabled RTTI in my project but gives same warning and run time error,
Avatar of msjammu

ASKER

>>destructor

gives same output even with this as you suggested,

      virtual ~Shape()
      {
            cout<<"Destroying Shape"<<endl;
      }
Avatar of msjammu

ASKER

It will be strange that an object of a class it being destroyed and no destructor function is called, It seems to me a bug if I am not wrong !
If I am can you refer to me some document where the information about the calling sequence of constructors and destructors is given, when using virtual functions? As you can see from the output constructor functons are called for both shape and target type in sequence Base>Derived then why not destructor function called for target type and shape in sequence (Derived>Base) as it will normally do in C++, Please explain
I copied your code and verified your output. But when changing the destructor to virtual I got


Constructing Shape
Constructing Triangle
*
* *
*****
Destroying Triangle
Destroying Shape


Constructing Shape
Constructing NullShape
Destroying NullShape
Destroying Shape
[Only part of the output]
So now the destructors are called as expected: Derived>Base. There must be something else wrong with your code or project settings if your output for virtual destructor doesn't change. Can a fresh install of VC++ solve it? (Test project settings first!)

Avatar of msjammu

ASKER

SteH:
Thanks you,
you are right, and I got the intended output for the calling sequence of the destructors.

but Strange the second thing is still not working, the line

//cout << typeid(*shapePtr).name();

My project settings are default one an I only changed the RTTI to enabled.
Avatar of msjammu

ASKER

SteH,
I more thing, When I missed the keyword virtual before the destructor in base class, Isn't something wrong here. The delete statement was executed successfully but the destructor of the object being destroyed was not called. I suspect if the object was destroyed without the calling of its destructor function? This is not the intended feature in C++, isn't it should flag an error or warning. Suppose I allocated memory in corresponding constructors of the derived types, it would simply left undeleted without my knowledge.
A class must have a virtual destructor if it meets both of the following criteria:

    * You do a delete p.
    * It is possible that p actually points to a derived class.

see detail in http://blogs.msdn.com/oldnewthing/archive/2004/05/07/127826.aspx

welcome to www.fruitfruit.com
Avatar of msjammu

ASKER

I found something in above link:

The statement

The delete p will invoke Sample::~Sample instead of Derived::~Derived, resulting in a leak of the stream m_p.

is wrong. If a delete expression is evaluated on a base class pointer that points to an instance of a derived class, the behavior is undefined; anything can happen.

It is possible that the base class destructor is invoked and the derived clas destructor isn't, but that's just an instance of undefined behavior. Other instances are a program crash or the computer catching fire.
Avatar of msjammu

ASKER

Please anybody give me solution for the warning and run time error problem,

Many Thanks
To your Q about RTTI:

I uncommented the line
cout << typeid(*shapePtr).name();
and get the following output [copied in parts]


Constructing Shape
Constructing Line
class Line**********
Destroying Line
Destroying Shape


Constructing Shape
Constructing NullShape
class NullShapeDestroying NullShape
Destroying Shape


Constructing Shape
Constructing Triangle
class Triangle*
* *
*****
Destroying Triangle
Destroying Shape

Only suggestion I have is add
<< endl; to it:
          cout << typeid(*shapePtr).name() << endl;
to get this on a single line.


For the warning this means that RTTI is disabled in the project settings. Is there a second place to enable? I will have a look. The error during execution a result of this I would say, since I get the code running without any problems here on my box (VC++ 6.0 SP6 on XP SP1).
Avatar of msjammu

ASKER

I have got the whole main function here it is:

int main()
{
      int i;
      Shape *shapePtr;

      for (i=0;i<10;i++)
      {
            shapePtr = generate();
            cout<< typeid(*shapePtr).name() <<endl;
            shapePtr->draw();
            if (shapePtr  != NULL)
                  delete shapePtr;
            cout<<endl<<endl;
      }

      return 0;
}

Also Just for try, If I do this little Change - using alternative form of new I get some warning messages, What that mean and how to resolve them, I increase points for this additional added part,

Shape *generate()
{
      switch(rand() % 4)
      {
            case 0:
                  return new(nothrow) NullShape;
            case 1:
                  return new(nothrow) Line;
            case 2:
                  return new(nothrow) Triangle;
            case 3:
                  return new(nothrow) Rectangle;
      }
      return NULL;
}

D:\Manpreet\My CPP\RTTI1\rtti1.cpp(95) : warning C4291: 'void *__cdecl operator new(unsigned int,const struct std::nothrow_t &)' : no matching operator delete found; memory will not be freed if initialization throws an exception
        d:\program files\microsoft visual studio\vc98\include\new(36) : see declaration of 'new'
D:\Manpreet\My CPP\RTTI1\rtti1.cpp(97) : warning C4291: 'void *__cdecl operator new(unsigned int,const struct std::nothrow_t &)' : no matching operator delete found; memory will not be freed if initialization throws an exception
        d:\program files\microsoft visual studio\vc98\include\new(36) : see declaration of 'new'
D:\Manpreet\My CPP\RTTI1\rtti1.cpp(99) : warning C4291: 'void *__cdecl operator new(unsigned int,const struct std::nothrow_t &)' : no matching operator delete found; memory will not be freed if initialization throws an exception
        d:\program files\microsoft visual studio\vc98\include\new(36) : see declaration of 'new'
D:\Manpreet\My CPP\RTTI1\rtti1.cpp(101) : warning C4291: 'void *__cdecl operator new(unsigned int,const struct std::nothrow_t &)' : no matching operator delete found; memory will not be freed if initialization throws an exception
        d:\program files\microsoft visual studio\vc98\include\new(36) : see declaration of 'new'
D:\Manpreet\My CPP\RTTI1\rtti1.cpp(115) : warning C4541: 'typeid' used on polymorphic type 'class Shape' with /GR-; unpredictable behavior may result
Linking...
>>>> return new(nothrow) NullShape;

MSDN says:
----------------------------------------------------------------------------------------------------------
nothrow:

  This is a __declspec extended attribute which can be used in the declaration of functions
----------------------------------------------------------------------------------------------------------

That means you cannot use it with new operator.

>>> warning C4541: (RTTI)
>>> cout << typeid(*shapePtr).name();

That also works on my VC6 system after i enabled RTTI. MSDN says that this warning means that RTTI isn't enabled. So, please check that you really have enabled RTTI for 'Win32 Debug' Configuration and that 'Win32 Debug' is your active configuration.

>>>> If a delete expression is evaluated on a base class pointer that points to an instance
>>>> of a derived class, the behavior is undefined

That is definitively wrong. The ability to delete instances by deleting baseclass pointers is one of the key features of C++.

Regards, Alex


         
Avatar of msjammu

ASKER

Alex,

>>nothrow:
>>This is a __declspec extended attribute which can be used in the declaration of functions

That use is something else' I mean this only:
http://www.informit.com/guides/content.asp?g=cplusplus&seqNum=168

also standard C++ says if a function throws no exception or u want to resctrict it to only throw some type of exceptions then use this form

void fun() throw(int,float, fla,fla ....)
{
}

that nothrow is something microsoft specific. isn't it?
Avatar of msjammu

ASKER

Thanks all,
That was problem with my project settings (RTTI). I was doing something stupid.

Also with the nothrow form of new I get same warnings. but my program runs well.
I must read some stuff and come back here again for this nothrow problem.

Also I am increasing points as I promissed.

Thank you SteH,
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi Alex,

>> If a delete expression is evaluated on a base class pointer that points to an instance
>> of a derived class, the behavior is undefined

>That is definitively wrong. The ability to delete instances by deleting baseclass pointers is one of the key features of C++.

Is that the case even for a non virtual destructor? I tested it on VC++ 6 and using a non virtual destructor a delete via base class pointer only called the base class destructor as I would expect. Making it virtual gave the desired result.
>> Is that the case even for a non virtual destructor?

You have to differ between deallocation and calling the destructor. If you are deleting a pointer of a baseclass that has a non virtual destructor, the destructor(s) of the derived class(es) isn't called, *but* the allocation was freed correctly as long as the baseclass pointer is the same as the pointer of the derived class. So, as long as there is no multiple derivation nothing goes wrong beside of memory leaks of derived classes.

The following gives Debug assertions in VC6:

class XShape : virtual public NullShape, virtual public Triangle
{
public:
     XShape()
     {
          cout<<"Constructing XShape"<<endl;
     }
     ~XShape()
     {
          cout<<"Destroying XShape"<<endl;
     }
     void draw()
     {
     }
};

int main()
{
     XShape* x = new XShape;
     Shape* s  = (NullShape*)x;
     delete s;
     return 0;
}

That is because the delete operator couldn't free the extra memory that comes from baseclass Triangle. I think, the statement "undefined behavior" is correct for that.

Regards, Alex
Avatar of msjammu

ASKER

Hi Alex,

This works here, Thank you,
But I thought things are as simple that they should be

void __cdecl operator delete(void * p, const std::nothrow_t&)
{
    try
    {
        delete p;
    }
    catch(...)
    {
    }
}

Regards,
Manpreet
Avatar of msjammu

ASKER

But one more thing I add here,

To my surprize the following stuff works well without any warning or errors

#include <iostream>
using namespace std;

int main()
{
      char *str = new(nothrow) char;

      delete str;

      return 0;
}

..............????? I can't get it why this is so ?
I think this is now getting a bit off topic in relation to the first question and its title. Would you mind putting this into a new Q and closing this one?

The idea of EE is to use closed questions as a searchable database to find solutions easily. When the question now shows no relation to its title it will not be properly found (or used if found) as search result. In this case it is better to start a new thread with the additional points instead of increasing the points of a (more or less) unrelated question.
Avatar of msjammu

ASKER

I honor,

Thanks You all