If you don't care about comparisons of type A to type B, or B to C, etc. then you can simply implement an overloaded equality operator for each class:
class A {
    public: int data;
    bool operator==(const A& rhs) {
        return (data == rhs.data);
    }
};
class B : public A {
    public: float more_data; bool something_else;
    bool operator==(const B& rhs) {
        return (A::operator==( data ) &&
                more_data == rhs.more_data &&
                something_else == rhs.something_else);
    }
};
That's dangerous though, because if you derive a new class D from B or C, you're going to have problems.
Otherwise you need to implement some comparators with a lot of dynamic_cast<>-ing to really do it right. Alternatively you could implement a function to create a hash code for each object and leverage that, e.g.
class A {
    public: int data;
    virtual long getHashCode() const {
        // compute something here for object type A
    }
    // virtual here just in case you need to overload it in B or C
    virtual bool equals( const A& obj ) const {
        return (typeid(*this) == typeid(obj) &&
                getHashCode() == obj->getHashCode());
    }
};
class B : public A {
    public: float more_data; bool something_else;
    virtual long getHashCode() const {
        // compute something here for object type B
    }
};
class C : public A {
    public: double more_data;
    virtual long getHashCode() const {
        // compute something here for object type C
    }
};
If you incorporate the object's type into the hash code in some fashion (not shown above) then you can also dispense with the silly typeid() comparisons above.