views:

399

answers:

5

I was a little surprised that the following code did not work as expected:

#include "stdio.h"

class RetA
{
public:
    virtual void PrintMe () { printf ("Return class A\n"); }
};

class A
{
public:
    virtual RetA GetValue () { return RetA (); }
};

class RetB : public RetA
{
public:
    virtual void PrintMe () { printf ("Return class B\n"); }
};

class B : public A
{
public:
    virtual RetA GetValue () { return RetB (); }
};

int main (int argc, char *argv[])
{
    A instance_A;
    B instance_B;
    RetA ret;

    printf ("Test instance A: ");
    ret = instance_A.GetValue ();
    ret.PrintMe (); // Expected result: "Return class A"

    printf ("Test instance B: ");
    ret = instance_B.GetValue ();
    ret.PrintMe (); // Expected result: "Return class B"

    return 0;
}

So, does virtual methods not work when returning a value? Should I revert to allocating the return class on the heap, or is there a better way?

(In reality I want to do this to let some different classes that inherits from a container class to return different iterator class instances depending on class ...)

A: 

Do get dynamic overloading, you need to have the static type different of the dynamic type. You don't here. Return a reference or a pointer (and pay attention to the live time) instead of a value.

AProgrammer
A: 

Virtual is applicable to pointer or reference declaration of type, So change you code: virtual RetA& GetValue () or virtual RetA* GetValue (), But you issue not about virtual - you use "copy semantic".

Dewfy
+17  A: 

Polymorphic behavior does not work by value, you need to return pointers or references for this to work.

If you return by value you get what is known as "slicing" which means that only the parent part of the object gets returned, so you've successfully striped a child object into a parent object, this is not safe at all.

Take a look at: http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c

Arkaitz Jimenez
A: 

you have to return pointer or address

virtual RetA GetValue () { return RetB (); }

if not, what you get from GetValue is a RetA not RetB.

ufukgun
A: 

Besides the already mentioned 'slicing' problem, the PrintMe method must also be virtual.

struct B 
{
   void print1() const { std::cout << "B" << std::endl; }
   virtual void print2() const { print1(); }
};
struct D : public B
{
   void print1() const { std::cout << "D" << std::endl; }
   virtual void print2() const { print1(); }
};
B& f() 
{
   static D d;
   return d; // the object returned is in fact a D, not a B
}
int main(){
   f().print1(); // B: calling a non-virtual method from a B reference
   f().print2(); // D: the method is virtual will exec B::print2
}
David Rodríguez - dribeas
Oops, of course. It actually was in my test, just a typo in my post.
Jonatan