Happy Mittal
Happy Mittal

Reputation: 3747

Create Function taking base class pointer but calling derived class function

I have two classes A (base) and B (deriving from A):

class A { };

class B : public A
{
    int data;
public:
    int get_data() { return data; }
};

Now I have a function test which takes base class pointer and calls derived class function :

void test(A * ptr)
{
    ptr->get_data();
}

But problem is ptr may point to A's object or B's object. If it points to B's object, then OK, but if to A's object, then it is a problem.

Moreover, I don't want to make get_data() virtual because data is not property of A's object.

How can I check if ptr points to B's object? One solution which I can think is dynamic_cast and check it for NULL. Is it the best solution or can I have a better solution ?

Upvotes: 3

Views: 4266

Answers (4)

Peter R
Peter R

Reputation: 2985

Inheritance models the is-a relationship. Clearly in your code B is not an A and inheritance is the wrong model. You mention in your comment that you have a vector which is being passed to an enclosing, bigger, function. I would suggest that one of the following would be a better fit:

  1. std::vector<boost::any>
  2. std::vector<boost::variant<A,B>>

Edit Here's an example using boost variant:

class A
{
public:
    void DoIt()
    {
        std::cout << "did it!" << "\n";
    }
};

class B
{
public:
    void GetIt()
    {
        std::cout << "got it!" << "\n";
    }
};

typedef boost::variant<A,B> ab_t;
typedef std::vector<ab_t> list_ab;

void test(list_ab& list)
{
    std::for_each(std::begin(list), std::end(list), [](ab_t& item)
    {
        if(A* a = boost::get<A>(&item))
            a->DoIt();
        if(B* b = boost::get<B>(&item))
            b->GetIt();
    });
}

Upvotes: 0

Andrei
Andrei

Reputation: 5005

If you can change the interface of A and B (including adding virtual functions) and if you can re-shuffle the code in the test function you can use the "visitor pattern". Here's a sample using the better named Base and Derived classes:

class Visitor
{
public:
    void Visit(Base * B)
    {
    }

    void Visit(Derived * D)
    {
        int data = D->get_data();
    }
};

class Base
{
public:
    virtual void Accept(Visitor * V )
    {
        V->Visit(this);
    }
};

class Derived: public Base
{
public:
    int get_data()
    {
        return data;
    }

    virtual void Accept(Visitor * V )
    {
        V->Visit(this);
    }
private:
    int data;
};

This way you can iterate over your vector of Base*, call Accept of each element and know that only for Derived elements the get_data method will be called.

Upvotes: 4

ilomambo
ilomambo

Reputation: 8350

You say that actually you have a vector of elements A or B, then your test function really looks like:

void test( A ** ptr )

Then you can use the overloading C++ capabilities to make a utility function, the right function will be called:

void test( A ** ptr ) {
    A * elem;
    int i=0;
    elem = ptr[i++];
    while(elem) {
        testElement(elem);
        elem = ptr[i++];
    }
}

void testElement( A * ptr ) { }
void testElement( B * ptr ) {
    ptr->get_data();
}

Upvotes: -4

Joseph Mansfield
Joseph Mansfield

Reputation: 110648

This means your test function is lying. It is saying that it will accept a pointer to any A object, even types derived from A, but the function won't actually work for anything other than B. You're much better off taking a B*:

void test(B* ptr)
{
   ptr->get_data();
]

Upvotes: 5

Related Questions