views:

304

answers:

6

How to access the derived class's data from one of its instances' member function that receives a pointer to the base class? I have:

    class Base{
        public:
            virtual void compare(Base*)=0;
    };

    class A: public Base{
        int x;
        public:
            A();
            void compare(Base*);
    };

    class B: public Base{
        char c;
        public:
            void compare(Base*);
    };

    A::A(){
        x = 1;
    };

    void A::compare(Base *p){
        cout<< "A's x is " << x << "\n";
        cout<< "Argument's x is " << p->x;
    };

    void B::compare(Base* p){
        cout<< "B's compare() is running\n";
    };

    int main(const int argc, const char *argv[]){
        A a1 = A();
        A a2 = A();
        B b = B();
        a1.compare(&a2);
    }

which won't work since Base doesn't have data member x. While I was writing this it came to me that this would not be such a good idea to access a data member of the derived class through a pointer to the Base interface class.

In my problem I need to compare different derived classes with the same interface provided by Base class. Base's interface must include compare(Base*) member function. The way instances are compared may vary among derived classed. The logic of comparison should apparently be coded inside those classes' member functions. How should I implement this model?

+1  A: 

You can use dynamic_cast to cast a base pointer to one of the derived types. Just be sure to properly handle the case where dynamic_cast can't cast the pointer.

I should add that this is a smelly way to do this. If the objects really are comparable, they should be comparable at the base class level.

Mark Ransom
+1  A: 

You might limp home with this, depending on your object heirarchy:

bool A::compare(Base *p){
    A *pa = dynamic_cast<A*>(p);
    if (!pa) return false;
    return x == pa->x;
};

Actually, there are serious difficulties with implementing polymorphic comparisons. Not the least problem is that if a is an instance of A, and c is an instance of some derived class C of A, then using this technique you could easily end up with a.compare(c) being true, but c.compare(a) being false.

You might be better either:

  • to make your comparisons non-polymorphic, and put it on the caller to compare them as type Base only if all they care about is that they're equal "as far as Base can tell". But this is less useful for the caller, and completely pointless with Base having no data members as in this example.
  • Have a non-virtual comparison in Base, which checks whether the two objects have equal typeid, returns false if they don't, and calls a virtual comparisonImpl function on the two of them if they are. Then A::comparisonImpl knows that its parameter is an instance of A, and can static_cast it:

.

class Base {
public:
    bool compare(Base *p) {
        if (typeid(*this) != typeid(*p)) return false;
        return compareImpl(p);
    }
private:
    virtual bool compareImpl(Base *p) = 0;
};

class A : public Base {
    int x;
private:
    bool compareImpl(Base *p) {
        return x == static_cast<A*>(p)->x;
    }
};
Steve Jessop
One way to avoid the asymmetry is to first check that the typeids are equal.
Eclipse
Yes, I was just editing to say so :-)
Steve Jessop
Once you check the typeid, you can go ahead and use `static_cast` as the cast is safe and you don't need to 'repay' for the type information (`dynamic_cast` is safer but slower than `static_cast` if the types are known to match, then they are just as safe and the later will be faster).
David Rodríguez - dribeas
Yep. My way takes advantage of that too. The non-virtual function avoids duplicating the 'orrible typeid comparison in each derived class, but I suppose it does open up the possibility that some miscreant code elsewhere in `Base` might call `compareImpl` directly, passing in the wrong parameter.
Steve Jessop
+1  A: 

dynamic_cast.

struct Base
{
 virtual bool compare(const Base*) = 0;
};

struct A : Base
{
 A()
 {
  x = 1;
 }
 virtual bool compare(const Base* other)
 {
  const A* rhs(dynamic_cast<const A*>(other));
  if(other == NULL) { return false; }
  return x == rhs->x;
 }
private:
 int x;
};

struct B : Base
{
 virtual bool compare(const Base* other)
 {
  const B* rhs(dynamic_cast<const B*>(other));
  if(other == NULL) { return false; }
  return c == rhs->c;
 }
private:
 char c;
};
DrPizza
+4  A: 

Unless your derived classes share some common data that you want to compare on which you could access through the base class, there is no simple solution for that.
For example dynamically casting in every derived class is redundant, slow and error prone unless you have a very small number of derived classes.

One solution for larger type hierarchies is double dispatch.

Georg Fritzsche
Double dispatch does not really scale that well in C++, does it? Can you implement it without adding a virtual function prototype to the base class for each new derived type? If you don't need to, can you post code?
David Rodríguez - dribeas
No, it doesn't scale well - but then C++ is mainly supposed to be a statically typed language. I personally don't know any better general version of DD.
Georg Fritzsche
+1  A: 

I had to fight my way through that some time before. In my case the specific requirements demanded that two element would compare equal only when they actually were of the same exact type. That is, you only want a positive match when comparing apples to apples, but at the most derived level (golden apples to golden apples, not to royal gala apples.

class base {
public:
   virtual bool compare( base const & rhs ) = 0;
};
class derived : public base {
public:
   virtual bool compare( base const & rhs ) {
      if ( typeid(*this) != typeid(rhs) ) return false;
      return *this == static_cast<derived const &>(rhs);
   }
};
bool operator==( derived const & lhs, derived const & rhs );

Note that, we ended up using typeid + static_cast in place of the more common idiom of dynamic casts as to force both types to be exactly the same. If we had used dynamic_cast then symmetry could break and:

class rederived : public derived {
public:
   virtual bool compare( base const & rhs ) {
      rederived const * p = dynamic_cast<rederived const *>(&rhs);
      return p && *this == *p;
   }
};
void test() {
   derived d;
   rederived rd;
   d.compare( rd ); // compare if derived subobject is equal, possibly true
   rd.compare( d ); // always false, d cannot be dyncamically casted to rederived
}

That again, is just due to our particular constraints that required exactly the same type for both objects. If you just want to check whether they are 'compatible' in one level of the hierarchy (which is an asymmetrical operation in itself as the left-hand-side determines what level of the hierarchy you want to perform the comparison at).

David Rodríguez - dribeas
A: 

How about this?

int main(const int argc, const char *argv[]){
    Base a1 = new A();
    Base a2 = new A();
    Base b = new B();
    a1.compare(&a2); }
Viraj