I have problem implementing the operator!= in a set class deriving from an abstact one. The code looks like this:
class Abstract
{
public:
//to make the syntax easier let's use a raw pointer
virtual bool operator!=(const Abstract* other) = 0;
};
class Implementation
{
SomeObject impl_; //that already implement the operator!=
public:
bool operator!=(const Abstract* other)
{
return dynamic_cast<Implementation*>(other)->impl_ != this->impl_;
}
};
This code works but it has the drawback to use dynamic_cast and I need to handle error in casting operation.
This is a generic problem that occur when a function of a concrete class it is trying to using some internal information (not available at the abstract class level) to perform a task.
Is there any better way to solve this kind of problem?
Cheers
You don't want to implement equality operators, ==
or !=
, in a base class. The base class has no idea how many or the content of the descendants.
For example, using the Shape
class example:
struct Shape
{
virtual bool equal_to(const Shape& s) const = 0; // Makes Shape an abstract base class.
bool operator==(const Shape& s) const
{
return equal_to(s);
}
bool operator!=(const Shape& s) const
{
return !equal_to(s);
}
};
struct Square : public Shape
{
bool equal_to(const Shape& s) const;
};
struct Circle : public Shape
{
bool equal_to(const Shape& s) const;
};
struct Flower : public Shape
{
bool equal_to(const Shape& s) const;
};
struct Cloud : public Shape
{
bool equal_to(const Shape& s) const;
};
In order to satisfy the equality operators of the Shape class, each descendant must implement the equal_to
method. But wait, how does Square
know what type the other Shape
is?
In this example, the Square
class would need to use dynamic_cast
on the reference to cast to a Square
object. This will fail when the argument is a Circle
, Flower
, Cloud
or some other yet to be defined shape.
The following is a valid concept that you have to watch out for:
Square my_square;
Cloud my_cloud;
Shape * p_shape_1 = &my_square; // Square is-a Shape, so this is legal.
Shape * p_shape_2 = &my_cloud; // Cloud inherits from Shape, so this is legal.
if (*p_shape_1 == *p_shape_2) // Legal syntax because of Shape::operator==().
{ //???}
The comparison above invokes nasty behavior. This could come about in generic functions that operate only on shapes.
Change the Design. You should never put a public comparison operator, that compares to a base class, in the base class. Nasty.
Descendents compare to Descendants. Period. Squares to Squares, Flowers to Flowers and Circles to Circles. Implement comparison operators in descendant classes.
Compare Base Class Content: If you have shared content in the base class, implement a protected method to compare the base class methods only:
struct Shape
{
protected:
bool equal_shape_content(const Shape& s) const;
};
This will make your program more robust because it compares only the Shape's content with another Shape. This is about all that you can guarantee. See also Base Class Slicing.
Or if that isn't possible (eg because SomeObject isn't known when the Abstact class is defined) you'll have to fully handle it..
class Abstract
{
public:
//to make the syntax easier let's use a raw pointer
virtual bool operator!=(const Abstract* other) = 0;
};
class Implementation : public Abstract
{
SomeObject impl_; //that already implement the operator!=
public:
bool operator!=(const Abstract* other)
{
// remember that dynamic cast can't remove a 'const' qualifier
const Implementation* o = dynamic_cast<const Implementation*>(other);
return (o == NULL || o->impl_ != this->impl_);
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With