Neil Osbaldeston
Neil Osbaldeston

Reputation: 13

How to use a covariant return to retrieve a derived class from a base class?

The code example is just a simple mock up of my actual program which attempts to hold varying different classes all from a single base class in a base vector. Then using a virtual function call to get *this, return the derived. This way I don't need multiple containers.

    #include "stdafx.h"
#include <iostream>
#include <vector>

class Base
{
public:
    virtual Base* getThis() { return this; }
    virtual void printClass() const { std::cout << "Base" << std::endl; }
};

class Derived : public Base
{
public:
    virtual Derived* getThis() { return this; }
    virtual void printClass() const { std::cout << "Derived" << std::endl; }
};


int main(int argc, _TCHAR* argv[])
{   
    Base Bar;
    Derived Foo;

    typedef std::vector<Base*> vContainer;

    vContainer Objects;
    Objects.push_back(new Derived);

    for (vContainer::iterator it = Objects.begin(); it != Objects.end(); ++it)
    {
        Bar = **it; // works to get Base but not wanted

        // attempts     
        //Foo = it->getThis(); // the pointer selector doesnt work...
        //Foo = static_cast<Derived>(**it);  // utterly confused!       
    }

    Bar.printClass(); // prints base as expected
    //Foo.printClass();  // Mean't to print Derived

    std::cin.get();
    return 0;
}

I've been looking for a better understanding of this for hours now but everyone just talks about clones, which is not what I'm after. Any help would be appreciated.

Neil

Upvotes: 1

Views: 94

Answers (3)

tba
tba

Reputation: 363

What you're trying to achieve, which is dynamic binding of the method regardless of the static type, is the whole purpose of virtual methods. All you had to do is using pointers or references to those objects in your container. Look at the following code:

#include <iostream>
#include <vector>

class Base
{
public:
    virtual void printClass() const { std::cout << "Base" << std::endl; }
};

   class Derived : public Base
{
public:
    Derived(){};
    virtual void printClass() const { std::cout << "Derived" << std::endl; }
};


int main()
{   

    typedef std::vector<Base*> vContainer;

    vContainer Objects;
    Objects.push_back(new Base);
    Objects.push_back(new Derived);


    for (vContainer::iterator it = Objects.begin(); it != Objects.end(); ++it)
    {
         // prints Base on 1st iteration
         // prints Derived on 2nd iteration
        (*it)->printClass();
   }

    return 0;
}

The reason why your attempt didn't work is that Bar was a local variable and not a reference/pointer. This means the size of Bar in the memory is determined at compile time and is sizeof(Base). Assigning a Derived object into it will copy the object by value and automatically delete the extra information stored in a Derived object and making it into a Base object (the extra information simply can't be stored in that amount of memory). If bar was of type Base* and you made it to point to a Derived object then Bar->printClass() would print Derived.

Hope that clarifies it.

Upvotes: 0

VolAnd
VolAnd

Reputation: 6407

Your code

Bar = **it; // works to get Base but not wanted

does not get object from vector to Bar. This is just assignment almost identical to this one (some output added):

class Base
{
public:
    virtual Base* getThis() { return this; }
    virtual void printClass() const { std::cout << "Base" << std::endl; }
    Base& operator=(Base& one) { std::cout << "operator = is working" << std::endl; return *this;}
};

So, if you want to have pointer to object stored in the vector do not try to copy objects, copy pointer (*iterator).

Upvotes: 0

R Sahu
R Sahu

Reputation: 206567

To be safe, use dynamic_cast.

for (vContainer::iterator it = Objects.begin(); it != Objects.end(); ++it)
{
    Bar* basePtr = *it;
    Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);
    if ( derivedPtr ) // Check whether the dynamic_cast was successful.
    {
       // Use derivedPtr
    }
}

Upvotes: 1

Related Questions