Sign in to follow this  
armond

Polymorphism and Virtual Functions

Recommended Posts

armond    168
Hi, why is it that when I have a method in a subclass that is not declared virtual and I do a dynamic_cast. The compiler complains that my class in not polymorphic?
class Animal
{
    //animal methods here
}

class Dog:Animal
{
    void bark();
}


int main(int argc, char* argv)
{
    Animal d = new Dog();

    Dog newDog = dynamic_cast<Dog*>(d);

    return 0;
}




Share this post


Link to post
Share on other sites
Doolwind    213

class Animal
{
//animal methods here
}

class Dog:Animal
{
void bark();
}


int main(int argc, char* argv)
{
Animal *d = new Dog();

Dog *newDog = dynamic_cast<Dog*>(d);

return 0;
}



It must be a pointer otherwise there won't be enough storage for a Dog class.

Doolwind.

Share this post


Link to post
Share on other sites
A better method would be this: -



class Animal
{
public:
// this makes the animal make the 'speaking' noise, a bark for a dog, a meow for a cat etc...
virtual void MakeOralNoise() = 0;
}

class Dog: public Animal
{
public:
void MakeOralNoise();
}

void Dog::MakeOralNoise()
{
cout << "Woof!!!" << endl;
}

int main(int argc, char* argv)
{
Animal *d = new Dog();

// speak...
d->MakeOralNoise();

return 0;
}





Using the above method, it allows all animals that are derived from the 'Animal' class to make an 'oral' noise so to speak. Using derivation etc... like this will allow for you to create more flexible, more intuitive objects.

NOTE: Don't forget the 'public' keyword in your classes. I'm not sure if you meant to leave them out, but it's still always nice to remember [smile]

Share this post


Link to post
Share on other sites
MadKeithV    992
Apart from the pointer issues that were already mentioned:
you did not specify an access modifier when deriving Dog from animal(there's no "public" in class Dog : Animal). This means you are probably defaulting to private inheritance (using VC++ this is what happens, I am not 100% sure what the standard says).

Private inheritance implies that you do not want substitutability - you just want to use the base class functionality. dynamic_cast respects this, and does not allow casts from a privately derived base class. In other words, private inheritance does not imply an "IS-A" relationship, and dynamic_cast fails because a Dog is not an Animal in the example.

Share this post


Link to post
Share on other sites
Conner McCloud    1135
The message is surprisingly accurate: you need a polymorphic type, and you didn't provide one.

Why? Because dynamic_cast relies on a bunch of runtime information that is only generated for polymorphic types. The compiler could generate it anyhow, but most of the time that would be unneccessary overhead.

If you plan on passing around Animal pointers to Dog objects, at the very least you need a virtual destructor, which will solve all your problems.

CM

Share this post


Link to post
Share on other sites
Conner McCloud    1135
Quote:
Original post by MadKeithV
This means you are probably defaulting to private inheritance (using VC++ this is what happens, I am not 100% sure what the standard says).

This is correct, although not applicable in this specific case.

CM

Share this post


Link to post
Share on other sites
armond    168
Thank you for all your answers. I will try those right away. I'm sorry for not putting * in those references. Now if you'll let me correct my code... It was suppose to be...




class Animal
{
//animal methods here
}

class Dog:public Animal
{
void bark();
}


int main(int argc, char* argv)
{
Animal *d = new Dog();

Dog *newDog = dynamic_cast<Dog*>(d);

return 0;
}




Uhmmm... I you don't mind... Please explain how virtual destructor come into play... I've seen this in one of my books... But I kinda wonder why there's only a virtual destructor and no virtual constructor? Thanks a lot!

Share this post


Link to post
Share on other sites
Conner McCloud    1135
Quote:
Original post by armond
Uhmmm... I you don't mind... Please explain how virtual destructor come into play... I've seen this in one of my books... But I kinda wonder why there's only a virtual destructor and no virtual constructor? Thanks a lot!

Look at it first from the point of view of a regular [read: non-destructor] function:

struct Animal
{
void Name() {cout << "Animal" << endl;}
};

struct Dog : Animal
{
void Name() {cout << "Dog" << endl;}
};

int main()
{
Dog d;
d.Name(); //Prints "Dog"
Dog* pd = &d;
d->Name(); //Prints "Dog"
Animal* pa = static_cast<Animal*>(&d);
pa->Name(); //Prints "Animal"
}

See what happened there? Because Name() was not virtual, the compiler had no way of knowing that pa->Name(); should have been redirected to Dog::Name(). It saw a function in the appropriate scope, and called it.

Destructors work the exact same way. If you don't have a virtual destructor, delete pa; [assume pa was allocated via new] isn't going to go looking for Dog::~Dog(), it is just going to call Animal::~Animal(). So only half of your object is going to be gotten rid of.

*edit: Of course, with or without a virtual destructor, delete pd; will work. if it is virtual, the function call will get directed to itself. If it is non-virtual, the function call will be made directly. No problem.

So, why don't you need virtual constructors? Because you always specify exactly which constructor you want to see called. You will never call Animal::Animal() in the hopes that it'll construct a Dog; you call Dog::Dog() directly. So all those issues simply don't exist.

CM

Share this post


Link to post
Share on other sites
MadKeithV    992
Quote:
Original post by Conner McCloud
Quote:
Original post by MadKeithV
This means you are probably defaulting to private inheritance (using VC++ this is what happens, I am not 100% sure what the standard says).

This is correct, although not applicable in this specific case.

CM


It might not be the only thing wrong with the code, and I agree that the lack of a virtual destructor the more obvious problem in this case (and I totally missed it ;-) ), but I believe it is applicable to this case.

In VC2005 the dynamic_cast has seen a few breaking changes. That's why I mentioned it - if just the virtual destructor would be fixed, the code still generates errors in VC2005. In fact, the statement Animal* p_animal = new Dog() fails to compile, because the conversion is inaccessible because of the private inheritance. It's on my "something to watch out for", it can cause some unexpected failures in legacy code ported to VC2005.




Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this