3

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 ?

12
  • 2
    The better solution is to declare test as void test(B * ptr). Then it cannot be abused. Commented Apr 20, 2013 at 12:33
  • 2
    this code can't compile Commented Apr 20, 2013 at 12:34
  • dynamic_cast won't work unless you have at least one virtual function. But you seem reluctant to use the obvious solution. Could you explain why you don't want to use virtual? The reason you have given doesn't seem to be much of a reason. Commented Apr 20, 2013 at 12:35
  • @Kerrek :No, Actually test is a part of bigger function which takes A*. Moreover, it exactly not takes A*, instead it is a vector of A*'s, in which some point to A objects, some to B, so I can't actually change the interface. Commented Apr 20, 2013 at 12:36
  • Remember that in C++ you can overload functions, so you can have two functions with the same name but different arguments. Commented Apr 20, 2013 at 12:37

4 Answers 4

5

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();
]
Sign up to request clarification or add additional context in comments.

2 Comments

OK, actually I made my problem simpler to ask question which is costing me now :). Actually, test takes a vector of A*'s, now I have to call get_data only for those pointers which point to B object.
@HappyMittal: That doesn't make sense. If you want a vector of things that are the same, then they must have a common interface. Use a virtual function. There's no cost, since you already have a virtual destructor anyway.
4

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.

4 Comments

Thanks Andrei, I liked your solution most, so accepting it :D.
-1, this design creates a cyclic dependency between all 3 classes. Moreover, it will imply to repeat the same code within the Accept method of all derived classes.
@DocBrown How is Base dependent on Derived? As for repeating the code in Accept, this is how the Visitor pattern works in order to implement double dispatch.
Base depends on Visitor, Visitor depends on Derived, and Derived obviously on Base.
0

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();
    });
}

6 Comments

B is-a A, or at least it can be. It's just that B has an additional data field.
Why B is not an A. It has all properties of A (which I haven't written here to keep question short), along with extra data. For example : a football player is a player, but NUM_GOALS only is a property of football_player, not in general player.
@HappyMittal Okay, so why are you passing a vector of Player*s to a function that only works with football_player*s?
@sftrabbit: Actually I am not passing, I am kind of given the vector i.e. test is a big function which requires all player's info, along with some special info like total num of goals. Anyway, thanks for the informative reply.
"Clearly in your code B is not an A" - you cannot deduce the correctness (or incorrectness) of such a relationship from names like A and B. You need at least the full class names and the intended semantics.
|
-4

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();
}

6 Comments

testElement( B * ptr ) will never be called.
@CaptainObvlious Why do you say that?, ptr should be polymorphic and the type of the element pointed by elem should be resolved at run time.
You are calling testElement and passing a variable of type A*. How exactly do you expect it to call testElement(B*)? You are relying on the variable type and signature of function to determine which one is called not the polymorphic behavior of A and B. That comes later inside of testElement.
@CaptainObvlious Isn't that what polymorphism is about? Having an array of Animals, but where each element can be a derived class Lion, Monkey or Turtle? So of course all elements are A*, but individual elements can be B*, which can only be know at runtime. Polymorphism does not mean all derived classes support the same set of methods (unless the methods are declared in the base class).
Your answer is wrong. When you call testElement (a free function) the compiler determines which version to use at compile time, not runtime. Since elem is declared as type A* the testElement(B*) version never gets called. If instead you did elem->get_data() in the while loop and it was declared virtual in A then subtype polymorphism would be kick in. Run the code, it will not work like you are expecting.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.