Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Abstract class and operator!= in c++

Tags:

c++

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

like image 203
Alessandro Teruzzi Avatar asked Jan 12 '11 14:01

Alessandro Teruzzi


2 Answers

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.

Resolution

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.

like image 154
Thomas Matthews Avatar answered Nov 15 '22 00:11

Thomas Matthews


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_);
    }
};
like image 45
DaDummy Avatar answered Nov 14 '22 23:11

DaDummy