msjammu
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.
//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.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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.
C/C++ tab and select C++ Language. There check "enable RTTI" and it should work.
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?
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?
ASKER
I already enabled RTTI in my project but gives same warning and run time error,
ASKER
>>destructor
gives same output even with this as you suggested,
virtual ~Shape()
{
cout<<"Destroying Shape"<<endl;
}
gives same output even with this as you suggested,
virtual ~Shape()
{
cout<<"Destroying Shape"<<endl;
}
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
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!)
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!)
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.
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.
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.
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
* 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
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.
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.
ASKER
Please anybody give me solution for the warning and run time error problem,
Many Thanks
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.
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).
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...
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
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
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
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
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
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
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?
>>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?
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,
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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.
>> 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
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
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
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
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 ?
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.
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.
ASKER
I honor,
Thanks You all
Thanks You all
ASKER