Jump to content
  • Advertisement
Sign in to follow this  
zyrolasting

Polymorphism confusion (C++)

This topic is 3493 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Heya. I'm writing a base abstract class for use in my game, but I'm having a little trouble understanding the structure I put up. Typedefs and the like should not be relevant.
class CIsoDev_Resource
{
public:

	virtual ~CIsoDev_Resource() { Clear(); }

	virtual HRESULT Load	( c_ch File )  = 0; // Load resource
	virtual HRESULT Reset	()  = 0; // Free all associated interfaces, then recreate them with relevant leftover data including strFile.

	virtual b1	Write	()  = 0; // Write resource details to Log
	virtual b1	Export	()  = 0; // Export data that can be used with Load
	virtual b1	Validate()  = 0; // Validate data to ensure the resource can be used.

	virtual void	Init	() { bActive = false; }
	virtual void	Clear	()	= 0; // Wipe data clean for a new load or for destruction.
	virtual void	Draw	()  = 0; // Draw resource to screen.

	b1	bActive;				 // If false, this resource will be completely ignored.
	string	strFile;				 // If the resource has associated with a file, the name will be stored here.
};


What I'm confused about are the "normal" virtual functions like the destructor. How exactly are they inherited? I understand that pure virtual functions are basically huge blanks that prevent making instances of a class unless the blanks are filled with definitions, and that this can never be so for the abstract class. But going off the defined destructor, if I were to have a destructor in the derived class with a different definition, does this one find a way to make use of it's own functionality as well, or does the derived class completely override it? I have another question about this, but I'll wait until the above is cleared up...

Share this post


Link to post
Share on other sites
Advertisement
you declare it virtual so the child-class can overwrite it.

if you cast it to the base type and destruct it. the child destructor wouldn't be called if the parents one wasn't virtual afaik.

In general terms it is always the safe way to make the destructor virtual and prevents a lot of strange behaviour.

Share this post


Link to post
Share on other sites
It's pretty simple: a non-virtual function call is bound based on the static type, a virtual function call is bound based on the dynamic type. This is also the case for the destructor.

Consider:

Base * base = new Derived();
base -> frobnicate();


This calls Base::frobnicate if it's non-virtual, and Derived::frobnicate if it's virtual. Note that Derived::frobnicate always exists, but it might just be a forward to Base::frobnicate if you didn't override it.

Now, same for destructors:

Base * base = new Derived();
delete base;


This calls Base::~Base if it's non-virtual, and Derived::~Derived if it's virtual. This means that, if it wasn't virtual, the constructor for Derived would not have been called.

Last but not least, don't call virtual functions from destructors and constructors: they don't work.

Share this post


Link to post
Share on other sites
Quote:

Last but not least, don't call virtual functions from destructors and constructors: they don't work.


That's pretty much where my next question was heading. So from what I understand, it's good practice to declare a destructor virtual to prevent undefined behaviour (or too much of, if I am thinking in the right context). What I'm hearing here is a non virtual destructor with a derived class may make a scenario where the incorrect one is called. Got it.

But for constructors, Apparently it's just a good idea for me to focus on the immediate members of the class I'm working on, derived or not? In other words, That bActive bool in the code above is something I want to initialize to false for every instance of a class derived from this. Since virtual function calls don't agree with constructors/destructors, I should explicitly write bActive = false for every constructor here and/or down the line?

Thanks to both of you for the info, by the way!

Share this post


Link to post
Share on other sites
Quote:
Original post by zyrolasting
I should explicitly write bActive = false for every constructor here and/or down the line?
Ideally, you'd use an initializer list.

Share this post


Link to post
Share on other sites
So given it inherits the bool member, I use something like...


class CIsoDev_Resource
{
public:
virtual ~CIsoDev_Resource() { Clear(); }

b1 bActive;
};

class Derived : public CIsoDev_Resource
{
public:
Derived() : bActive(false) {}
};




...Right?

By the way, is it possible to use more than one initializer there? Do I separate them with commas, or more colons?

Share this post


Link to post
Share on other sites
U can not call more then one Initalizer without doing it manualy although you can actualy write more then one with function overloading this may be of use to too.

Also check out throwing execption and depeding on what you want to do it may be usefull to through back an da struct for error reporting.

Fyi do not throw a 1 or true from a constructor it is a bad idea and leads to all kinds of wierdness. :) don't use it if you just need to check for a single value because you can check to see if the pointer is valid to see if you can use the object. the throw and catch is for more exact error checkign so you know what went wrong.

Regards Jouei.

Share this post


Link to post
Share on other sites
Quote:
Original post by zyrolasting
...Right?
You can only initialize your own members, not your parent's. However, you can initialize your parent as well:

Base(bool op=true) : active(active) {}
Derived(bool op=true) : Base(op) {}


Quote:
By the way, is it possible to use more than one initializer there? Do I separate them with commas, or more colons?
Commas.

Share this post


Link to post
Share on other sites
Quote:
But going off the defined destructor, if I were to have a destructor in the derived class with a different definition, does this one find a way to make use of it's own functionality as well, or does the derived class completely override it?


Constructors and destructors in a class hierarchy work like a stack. Take the following situation:

class A {};

class B : public A {};

class C : public B {};

When you instantiate a C object:

C foo;

The constructors are called from the root parent down to the most-derived, as such:

A(), B(), C()

Which incrementally builds the final object out of its parent classes and the most-derived class. This can be seen as "pushing" the objects onto a figurative stack.

When foo is destroyed, the destructors are called in the reverse order, "popping" the objects off the stack:

~C(), ~B(), ~A()

However, the program must know which destructor to first call. If the destructors are not virtual and C is declared and destroyed as such:

C *bar = new C();
delete bar;

Then the destructor call is statically bound to ~C(), which is called, followed by ~B() and ~A(). If it's done as the following, however:

A *bar = new C();
delete bar;

Then it is not known that bar is actually a C. If the destructor is not virtual, the destructor call is statically bound to ~A(), meaning that ~C() and ~B() are never called. A's destructor must therefore be declared as virtual, so that the destructor call is dynamically bound to the most-derived destructor, ~C().

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!