Jump to content
  • Advertisement
Sign in to follow this  
SuperVGA

Call to child becomes a call to parent

This topic is 2902 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

Hello everyone!

I have a class that contains several virtual functions.
-They all return bools. "false" by default.

Now, this class should function as an interface, as to where
other classes can inherit from, and implement other versions
of the functions, that may return "true" instead.

The base class is called interactor.

The derived class inheriting from this class is called camera.

I have a global function calling one of the implemented functions from the interface, in a camera-based object.

Yet the original function is called. I wonder why?

The interactor class:

class interactor
{
private:
std::string id;

public:
virtual ~interactor() {}

virtual bool key_normal_pressed(int c) { return false; }
};





The camera class:

class camera : public view, public interactor
{
public:
bool key_normal_pressed(int c);
};




The key_normal_pressed(int c) is implemented in a file including the header file for the camera class. Please help. I can't find the source of the problem?

[Edited by - SuperVGA on July 8, 2010 8:44:01 AM]

Share this post


Link to post
Share on other sites
Advertisement
Right... so your car's engine is making funny noises, so you've carefully removed the door panels and sent them to the shop for inspection [smile]

We're going to need to see the actual code that demonstrates the problem.

Share this post


Link to post
Share on other sites
This code may be the carburettor or the exhaust, but it's definitively not the door panels.
That aside: excellent analogy.

This is the code calling the function.


std::vector< interactor > interactor_list;

int main()
{
camera mycam;
interactor_list.push_back(mycam);
return EXIT_SUCCESS;
}


.. And a little later:

void check_interactor_knp(unsigned i)
{
if( interactor_list.key_normal_pressed(c) )
{
break;
}
}

Share this post


Link to post
Share on other sites
You're making a call to a camera class instance by value. Virtual functions only work if the object is a pointer or reference, otherwise the compiler can optimise it away.

Your interactor_list vector contains instances of interactor's, not camera's. You'll need to store pointers to interactor's in the vector instead.

Share this post


Link to post
Share on other sites
You're experiencing what is known as "object slicing."

I won't dig too far into the technical details here as you can learn plenty about why slicing occurs from The Googles; suffice it to say that this is the root of your problem:

std::vector< interactor > interactor_list;

This stores a vector of interactor objects - i.e. just the member variables of the interactor class itself. Any other associated data from the original object, including the v-table (which tracks what virtual functions you need to call to get the correct behaviour) and any members from other multiply-inherited classes, will be lost. Essentially you are telling the compiler to throw away all the information that isn't in interactor. Since there's a lot of important information from camera involved - most notably the virtual dispatch table - you're getting bogus results.

In general, to avoid slicing, you need to store a pointer to the originally created object itself. (You can technically avoid slicing and retain virtual dispatch semantics with just references, but you're then limited a lot - for instance you can't make a container of references.) The general best-practice solution here is to store a container of smart pointers, such as std::vector<boost::shared_ptr<interactor> >.

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
You're making a call to a camera class instance by value. Virtual functions only work if the object is a pointer or reference, otherwise the compiler can optimise it away.

Your interactor_list vector contains instances of interactor's, not camera's. You'll need to store pointers to interactor's in the vector instead.


Alright! Thanks Steve, I didn't know about that! Also thanks Apoch, for the explanation.
I'll remember to take that into considerations when doing virtual stuff again.

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
You're making a call to a camera class instance by value. Virtual functions only work if the object is a pointer or reference, otherwise the compiler can optimise it away.

Your interactor_list vector contains instances of interactor's, not camera's. You'll need to store pointers to interactor's in the vector instead.

You're being really confusing here.

Isn't what's really happening is that the std::vector is creating a new interactor via the copy constructor or assignment operator? Since camera is a subclass of interactor, it can be the argument to interactor(const interactor&) or operator=(const interactor&). Thus, you end up with a new interactor object that is not of any derived class at all.

I presume that when you say that virtual functions don't work on values, you mean something like this:

#include <stdio.h>
#include <windows.h>

class Dog
{
public:
virtual void bark()
{
printf("<Dog Bark>!\n");
};
};

class Labrador : public Dog
{
public:
virtual void bark()
{
printf("<Labrador Bark>!\n");
}
};

int main(int argc, char* argv[])
{
Labrador myPet;
Dog someDog(myPet);
myPet.bark();
someDog.bark();
system("pause");
return 0;
}



The output is:
<Labrador Bark>!
<Dog Bark>!
Press any key to continue . . .


If that's what you meant, I'd disagree. someDog is not a Labrador, even if it is constructed using one. The virtual function call resolved to match its type exactly.

If you meant something else, then could you elaborate?

[Edited by - Slavik81 on July 8, 2010 9:11:44 PM]

Share this post


Link to post
Share on other sites
Quote:
Virtual functions only work if the object is a pointer or reference, otherwise the compiler can optimise it away.


Nice info and description. +rep
I would probably have run into problems later on my octree class had I not read that :D

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.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!