Virtual Functions, polymorphism and dynamic_cast?

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

Virtual Functions, polymorphism and dynamic_cast?

Postby RoadieRich » Fri May 04, 2012 7:15 pm UTC

If I've got the following C++ data classes:
Code: Select all
class Base
{
    virtual string toString()
    {
        return "Base";
    }
}
class Child : public Base
{
    string toString()
    {
        return "child";
    }
}
 

And I've got a tree that works perfectly storing one type, what do I need to change to get the following code to do what I expect?
Code: Select all

Tree
<int, Parent> t;

Child c;
Parent p;

t.add(1,c);

cout << t.get(1).toString() << endl; //prints "Base", I want it to print "child"
 


I suspect it's something to do with dynamic_cast, but I can't work out where I need to put it.

+Edit Ctrl+S doesn't do what you expect on forums *closes "save page" window /yet again/*.
roband wrote:Mav is a cow.

UniJam 2012: Inter-university Games Jam hosted by Nottingham Trent University DevSoc.
nlug: Nottingham Linux User Group
DevSoc: The Nottingham Trent University Software Development Society
User avatar
RoadieRich
The Black Hand
 
Posts: 1030
Joined: Tue Feb 12, 2008 11:40 am UTC
Location: Somewhere only we know

Re: Virtual Functions, polymorphism and dynamic_cast?

Postby jaap » Fri May 04, 2012 7:41 pm UTC

(BTW I assume that Base and Parent are meant to be the same class)

I think you may have an object slicing problem. The tree only has room for storing Parent instances. Any extra data that the Child has will be lost as there simply isn't any memory reserved for it in the tree.

Therefore I don't think a dynamic cast will work in a situation like this. It cannot convert it back to a Child instance, because it never stored a complete Child instance in the tree.
(I don't know if it still fails in this particular case because Child does not actually have any extra member variables or functions compared to Parent, but I am assuming this is just a toy example to illustrate the problem. Even if it isn't, it is probably still undefined behaviour to forcibly cast it back to Child.)

What you actually want to do is have a tree that stores pointers rather than whole objects, which allows the tree to indirectly store complete objects of various sizes (each derived type below Parent may have a different size, but pointers to them are all the same size), and allows a dynamic cast to work because it can retrieve the extra data. I'm sure the Boost library has various ways to more easily handle a tree or other collections with such indirect memory storage, but I haven't much experience with that.
User avatar
jaap
 
Posts: 1720
Joined: Fri Jul 06, 2007 7:06 am UTC

Re: Virtual Functions, polymorphism and dynamic_cast?

Postby RoadieRich » Fri May 04, 2012 8:22 pm UTC

Ok, it's now storing pointers, but it's still not behaving. Am I doing the cast wrong?
Code: Select all

Tree
<int, Base*> t;

Child* c = new Child//ignoring the memory leak here. 

t.add(1,c);

cout << dynamic_cast<Base>(t.get(1))->toString() << endl; //still prints "Base"
cout << t.get(1)->toString() << endl;  //also prints "Base"
 
roband wrote:Mav is a cow.

UniJam 2012: Inter-university Games Jam hosted by Nottingham Trent University DevSoc.
nlug: Nottingham Linux User Group
DevSoc: The Nottingham Trent University Software Development Society
User avatar
RoadieRich
The Black Hand
 
Posts: 1030
Joined: Tue Feb 12, 2008 11:40 am UTC
Location: Somewhere only we know

Re: Virtual Functions, polymorphism and dynamic_cast?

Postby jaap » Fri May 04, 2012 8:33 pm UTC

Shouldn't you use dynamic_cast<Child> instead of dynamic_cast<Base> ?
User avatar
jaap
 
Posts: 1720
Joined: Fri Jul 06, 2007 7:06 am UTC

Re: Virtual Functions, polymorphism and dynamic_cast?

Postby RoadieRich » Fri May 04, 2012 8:39 pm UTC

jaap wrote:Shouldn't you use dynamic_cast<Child> instead of dynamic_cast<Base> ?

The problem is that in the real app, there's 6 different Child classes spread over three levels of inheritance, so I can't guarantee which type it'll be a runtime, unless I store the type somehow, but that seems messy and hackish.

There's also this, from http://www.cplusplus.com/doc/tutorial/typecasting/
Code: Select all
pb = dynamic_cast<CBase*>(&d);     // ok: derived-to-base
pd = dynamic_cast<CDerived*>(&b);  // wrong: base-to-derived
roband wrote:Mav is a cow.

UniJam 2012: Inter-university Games Jam hosted by Nottingham Trent University DevSoc.
nlug: Nottingham Linux User Group
DevSoc: The Nottingham Trent University Software Development Society
User avatar
RoadieRich
The Black Hand
 
Posts: 1030
Joined: Tue Feb 12, 2008 11:40 am UTC
Location: Somewhere only we know

Re: Virtual Functions, polymorphism and dynamic_cast?

Postby Yakk » Fri May 04, 2012 8:43 pm UTC

Code: Select all
cout << dynamic_cast<Base>(t.get(1))->toString() << endl; //still prints "Base"

This code shouldn't compile at all. You dynamic cast'd a pointer to an instance of base?!

I suspect you aren't showing the code that actually did what you said it did.

The problem is probably in the guts of Tree, which you haven't shown us.

Also, try this simple test:
Code: Select all
Child* child = new Child();
std::cout << child->toString() << std::endl;
Base* base = child;
std::cout << base->toString() << std::endl;

as a sanity check.


... Base qualifies under "When a class is polymorphic", as it has a virtual member function. So that clause doesn't apply.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.
User avatar
Yakk
 
Posts: 10039
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: Virtual Functions, polymorphism and dynamic_cast?

Postby RoadieRich » Fri May 04, 2012 8:53 pm UTC

Yakk wrote:
Code: Select all
cout << dynamic_cast<Base>(t.get(1))->toString() << endl; //still prints "Base"

This code shouldn't compile at all. You dynamic cast'd a pointer to an instance of base?!

I suspect you aren't showing the code that actually did what you said it did.

The problem is probably in the guts of Tree, which you haven't shown us.

Also, try this simple test:
Code: Select all
Child* child = new Child();
std::cout << child->toString() << std::endl;
Base* base = child;
std::cout << base->toString() << std::endl;

as a sanity check.

Yes, I'm not showing you my entire code as the data classes are just plain ugly (the base class constructor takes 7 arguments). I had (the equivalent of) Base* as the template argument, and typo'd when I was translating.

But that may be irrelevant: turns out the problem is higher up the chain : the sanity check prints "Base" twice.

(And ctrl+s again...)
roband wrote:Mav is a cow.

UniJam 2012: Inter-university Games Jam hosted by Nottingham Trent University DevSoc.
nlug: Nottingham Linux User Group
DevSoc: The Nottingham Trent University Software Development Society
User avatar
RoadieRich
The Black Hand
 
Posts: 1030
Joined: Tue Feb 12, 2008 11:40 am UTC
Location: Somewhere only we know

Re: Virtual Functions, polymorphism and dynamic_cast?

Postby Jplus » Fri May 04, 2012 9:10 pm UTC

Just for the record: once you get the sanity right, it should work without a dynamic cast. The whole point of dynamic binding, after all, is that you don't need to know the type of the object in advance.

Code: Select all
Tree<int, Base*> t;

t.add(1, new Child);
t.add(2, new Base);

cout << t.get(1)->toString() << endl;  // should print "Child"
cout << t.get(2)->toString() << endl;  // should print "Base"  
Hey, like coding? Perhaps you should check out the red spider project.
Feel free to call me Julian. J+ is just an abbreviation.
User avatar
Jplus
 
Posts: 1091
Joined: Wed Apr 21, 2010 12:29 pm UTC

Re: Virtual Functions, polymorphism and dynamic_cast?

Postby RoadieRich » Fri May 04, 2012 9:34 pm UTC

I've got absolutely no idea what's causing the sanity check to fail.

Here's the relevant bits of code, reduced into a compilable whole:
Code: Select all
#include <string>
#include <iostream>

class Vessel
{
public:
   virtual std::string toXML() const
    
{
        return "base";
    }
};

class SurfaceVessel :
        public Vessel
{
public:
    virtual std::string toXml()
    {
        return "intermediate";
    }
};

class Destroyer :
    public SurfaceVessel
{
public:
    virtual std::string toXml() const
    
{
        //snip actual code
        return "Destroyer";
    }
};
 

int main
()
{
    Destroyer *testDD = new Destroyer();
    std::cout << testDD->toXML() << std::endl;
    Vessel *testVessel = testDD;
    std::cout << testVessel->toXML() << std::endl;
    std::cin.get();
    delete testDD;
    return 0;
}
 

This also fails the sanity check, but I can't for the life of me figure out why. Where am I being an idiot?
roband wrote:Mav is a cow.

UniJam 2012: Inter-university Games Jam hosted by Nottingham Trent University DevSoc.
nlug: Nottingham Linux User Group
DevSoc: The Nottingham Trent University Software Development Society
User avatar
RoadieRich
The Black Hand
 
Posts: 1030
Joined: Tue Feb 12, 2008 11:40 am UTC
Location: Somewhere only we know

Re: Virtual Functions, polymorphism and dynamic_cast?

Postby jaap » Fri May 04, 2012 9:59 pm UTC

The const is part of the function signature. Therefore
virtual std::string SurfaceVessel ::toXml()
does not match
virtual std::string Vessel::toXML() const
so the former does not override the latter.

ETA: There is also the difference in case: toXml and toXML, so all three functions are different.
Last edited by jaap on Fri May 04, 2012 10:06 pm UTC, edited 1 time in total.
User avatar
jaap
 
Posts: 1720
Joined: Fri Jul 06, 2007 7:06 am UTC

Re: Virtual Functions, polymorphism and dynamic_cast?

Postby RoadieRich » Fri May 04, 2012 10:06 pm UTC

jaap wrote:The const is part of the function signature. Therefore
virtual std::string SurfaceVessel ::toXml()
does not match
virtual std::string Vessel::toXML() const
so the former does not override the latter.

No, but it is correctly overridden in Destroyer, which is the class I'm instantiating. And fixing that doesn't change anything.
roband wrote:Mav is a cow.

UniJam 2012: Inter-university Games Jam hosted by Nottingham Trent University DevSoc.
nlug: Nottingham Linux User Group
DevSoc: The Nottingham Trent University Software Development Society
User avatar
RoadieRich
The Black Hand
 
Posts: 1030
Joined: Tue Feb 12, 2008 11:40 am UTC
Location: Somewhere only we know

Re: Virtual Functions, polymorphism and dynamic_cast?

Postby jaap » Fri May 04, 2012 10:14 pm UTC

RoadieRich wrote:No, but it is correctly overridden in Destroyer, which is the class I'm instantiating. And fixing that doesn't change anything.

You may have missed the edit of my post above, simultaneous with your post.
User avatar
jaap
 
Posts: 1720
Joined: Fri Jul 06, 2007 7:06 am UTC

Re: Virtual Functions, polymorphism and dynamic_cast?

Postby EvanED » Fri May 04, 2012 10:22 pm UTC

FWIW, if you can afford to use C++11, you can use the override keyword to detect this problem:

Code: Select all
$ cat override.cpp
class Vessel
{
public:
   virtual void toXML() const
    {
    }
};

class SurfaceVessel :
        public Vessel
{
public:
    virtual void toXml() override  // <--- error!
    {
    }
};

$ g++-4.7.0 -std=c++0x -fsyntax-only override.cpp
override.cpp:13:18: error: ‘virtual void SurfaceVessel::toXml()’ marked override, but does not override


Note that override is the unusual C++11 feature that GCC was really slow in adopting, especially considering how easy it seems like it'd be to do; you need 4.7 to have it. MSVC supports it since VC 2010.

Personally, I have a preprocessor macro CPP11_OVERRIDE that I #define to either override or empty depending on compiler.
EvanED
 
Posts: 3767
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI

Re: Virtual Functions, polymorphism and dynamic_cast?

Postby RoadieRich » Fri May 04, 2012 10:45 pm UTC

I can't believe it was something that simple.

Now to figure out the rest of the program.
roband wrote:Mav is a cow.

UniJam 2012: Inter-university Games Jam hosted by Nottingham Trent University DevSoc.
nlug: Nottingham Linux User Group
DevSoc: The Nottingham Trent University Software Development Society
User avatar
RoadieRich
The Black Hand
 
Posts: 1030
Joined: Tue Feb 12, 2008 11:40 am UTC
Location: Somewhere only we know


Return to Coding

Who is online

Users browsing this forum: No registered users and 10 guests