Solved

Pure Virtual functions and RTTI

Posted on 2004-10-18
28
920 Views
Last Modified: 2011-10-03
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.
0
Comment
Question by:msjammu
  • 15
  • 9
  • 3
  • +1
28 Comments
 
LVL 6

Author Comment

by:msjammu
ID: 12338084
I increase the points
0
 
LVL 13

Accepted Solution

by:
SteH earned 400 total points
ID: 12338093
No.
~Shape is not virtual. Therefore only the baseclass version is called when pointed via base class pointer. You need to change the destructor declaration to
virtual ~Shape ();
0
 
LVL 13

Expert Comment

by:SteH
ID: 12338101
Nice, even though I answered for 250 points already.
0
 
LVL 13

Expert Comment

by:SteH
ID: 12338136
/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.
0
 
LVL 6

Author Comment

by:msjammu
ID: 12338162
I did it already,
0
 
LVL 13

Expert Comment

by:SteH
ID: 12338186
> 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?
0
 
LVL 6

Author Comment

by:msjammu
ID: 12338211
I already enabled RTTI in my project but gives same warning and run time error,
0
 
LVL 6

Author Comment

by:msjammu
ID: 12338243
>>destructor

gives same output even with this as you suggested,

      virtual ~Shape()
      {
            cout<<"Destroying Shape"<<endl;
      }
0
 
LVL 6

Author Comment

by:msjammu
ID: 12338297
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
0
 
LVL 13

Expert Comment

by:SteH
ID: 12338330
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!)

0
 
LVL 6

Author Comment

by:msjammu
ID: 12338382
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.
0
 
LVL 6

Author Comment

by:msjammu
ID: 12338444
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.
0
 
LVL 12

Expert Comment

by:OnegaZhang
ID: 12338502
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
0
 
LVL 6

Author Comment

by:msjammu
ID: 12338677
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.
0
How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

 
LVL 6

Author Comment

by:msjammu
ID: 12338692
Please anybody give me solution for the warning and run time error problem,

Many Thanks
0
 
LVL 13

Expert Comment

by:SteH
ID: 12338724
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.


0
 
LVL 13

Expert Comment

by:SteH
ID: 12338742
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).
0
 
LVL 6

Author Comment

by:msjammu
ID: 12338809
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...
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 12340477
>>>> 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


         
0
 
LVL 6

Author Comment

by:msjammu
ID: 12344703
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?
0
 
LVL 6

Author Comment

by:msjammu
ID: 12344796
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,
0
 
LVL 39

Assisted Solution

by:itsmeandnobodyelse
itsmeandnobodyelse earned 100 total points
ID: 12344947
>>>> that nothrow is something microsoft specific. isn't it?

Yes, MSDN coming with VC6 doesn't tell about std::nothrow. But it seems Microsoft had learned better in

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcstdlib/html/vcsampsamplenewoperatorstlsample.asp

>>>> warning C4291

The include file <new> coming with VC6 contains an operator new(size_t, const std::nothrow_t&) but no equivalent destructor. I got rid of the warning (and a linker error) by defining a delete operator like that:

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

Regards, Alex


0
 
LVL 13

Expert Comment

by:SteH
ID: 12345316
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.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 12345686
>> 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
0
 
LVL 6

Author Comment

by:msjammu
ID: 12346171
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
0
 
LVL 6

Author Comment

by:msjammu
ID: 12346236
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 ?
0
 
LVL 13

Expert Comment

by:SteH
ID: 12346295
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.
0
 
LVL 6

Author Comment

by:msjammu
ID: 12346320
I honor,

Thanks You all
0

Featured Post

Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

Join & Write a Comment

What is C++ STL?: STL stands for Standard Template Library and is a part of standard C++ libraries. It contains many useful data structures (containers) and algorithms, which can spare you a lot of the time. Today we will look at the STL Vector. …
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

743 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

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now