1

I have two classes, one derived from the other. I want a function that returns an object (not pointer or reference) of the base class that calls the derived virtual function.

#include <iostream>
using namespace std;

class Base
{
public:
    virtual void print() { cout << "Base" << endl; }
};

class Derived : public Base
{
public:
    void print() { cout << "Derived" << endl; }
};

Base go()
{
    Derived *derived = new Derived();

    Base *base = derived;
    base->print();

    // (*base).print(); // prints "Derived" !
    // Base base2 = (*base);
    // base2.print(); // prints "Base" !

    return *base;
}

int main()
{
    Base base = go();
    base.print();
    return 0;
}

This prints out

Derived
Base

So in the go() function I managed to convert to Base and the print works. But when I return the object the print is using the base function!

I know this can work if you return a pointer or a reference but I really need to return an object. Is this possible? Why does my code not work?

As you can see, I've commented out code in go() that de-references the upcast pointer. Strangely, it prints correctly! And if I do a conversion to an object, it doesn't!

Any insight into why this is all happening would be very much appreciated.

Karl Penzhorn
  • 375
  • 1
  • 3
  • 12
  • 1
    "but I really need to return an object".... why? – tinkertime Mar 03 '19 at 20:31
  • @tinkertime I'm eventually going to be overloading the assignment operator and that doesn't work with pointers / references... especially if you're trying to assign something like a float – Karl Penzhorn Mar 04 '19 at 00:46

2 Answers2

2

When you return an actual object (and not a refernce or a pointer) it calls its copy ctor. In this case you return a Base class, so it calls its copy constructor and creates a new Base class object.

#include <iostream>
using namespace std;

class Base
{
public:
    Base() = default;
    Base(const Base & base) { std::cout << "COPY BASE" << std::endl; }
    virtual void print() { cout << "Base" << endl; }
};

class Derived : public Base
{
public:
    Derived() = default;
    Derived(const Derived & derived) { std::cout << "COPY DERIVED" << std::endl; }
    void print() { cout << "Derived" << endl; }
};

Base go()
{
Derived *derived = new Derived();

Base *base = derived;
base->print();

// (*base).print(); // prints "Derived" !
// Base base2 = (*base);
// base2.print(); // prints "Base" !

return *base;
}

int main()
{
    Base base = go();
    base.print();
    return 0;
}

In this case the output will be: Derived, COPY BASE, Base

ProjectZix
  • 36
  • 2
1

By returning the base object itself, you're dropping any reference to it having started off as derived and subsequently upcasted (and copied).

So effectively, what you're after will not work.


If you really "need this to return an object", I recommend a thin wrapper

struct karls_return_object {
    // ... stuff ...

    Base *b;
};
tinkertime
  • 2,972
  • 4
  • 30
  • 45