Inheritance in C ++ and problems with reboot variations ()

3

First I use C ++ 11 . I have a base class called Area that has an undefined pure virtual function called restart ().

Then I have two derived classes, one called AreaRectangular and another called AreaCircular .

The problem is that these, in addition to implementing their own definition of restart () , each has an additional restart () different from the other derived class: p>

AreaCircular& reiniciar(Posicion2D pos, double radio); // de AreaCircular

AreaRectangular& reiniciar(Posicion2D pos, Tamanyo2D tamanyo); // de AreaRectangular

The problem I have is that I can not invoke any of these two functions using a pointer to the base class, since the only version it recognizes is that of restart () without parameters.

I had thought about putting these two versions of restart () in the base class as well and making them pure virtual but then the derived classes inherit a method that I do not want because they force an object to return a different derived class.

In summary. How can I use a pointer to Area to invoke the reboot version () that I want depending on the type of object being pointed?

Error message:

error: no matching function for call to 'Area::reiniciar(Posicion2D&, Tamanyo2D&)'
note: candidate: virtual Area& Area::reiniciar()
note: candidate expects 0 arguments, 2 provided

I add the declaration of the base class:

// PredeClarar Clases
class AreaRectangular;
class AreaCircular;

// Clase Base
class Area
{
public:
    virtual ~Area() = 0;
    virtual Area& reiniciar() = 0;
    virtual bool colisiona(Posicion2D pos) const = 0;
    virtual bool colisiona(AreaRectangular pos) const = 0;
    virtual bool colisiona(AreaCircular pos) const = 0;
    virtual int getX() const = 0;
    virtual int getY() const = 0;

};
    
asked by candidatopardo 06.04.2018 в 20:53
source

2 answers

3

Several things:

The interfaces better in structures

Ok that in C ++ there is no concept of interface ... but note that Area is a pure virtual class ... it has no implementation of any kind. Since all its members are public, it is preferable to use struct , whose default visibility is public:

struct Area
{
    virtual ~Area() = 0;
    virtual Area& reiniciar() = 0;
    virtual bool colisiona(Posicion2D pos) const = 0;
    virtual bool colisiona(AreaRectangular pos) const = 0;
    virtual bool colisiona(AreaCircular pos) const = 0;
    virtual int getX() const = 0;
    virtual int getY() const = 0;
};

Use polymorphism

struct Area
{
    virtual bool colisiona(AreaRectangular pos) const = 0;
    virtual bool colisiona(AreaCircular pos) const = 0;
};

That's MAL . It is assumed that AreaCircular and AreaRectangular inherit from Area ... then having these two functions implies that the base class has to know the classes that inherit from it ... And when you have more types of areas what are you going to do?

The solution is to use polymorphism:

virtual bool colisiona(Area const& pos) const = 0;

This function is perfectly capable of replacing the previous two. As you receive a reference (similar to a pointer), you access the original object and, within the function, you can ask for its specific type.

  

The problem I have is that I can not invoke any of these two functions through a pointer to the base class, since the only version it recognizes is the restart () without parameters.

Obviously you will not be able to ... when you declare a function as virtual the compiler has to create a table (which remains hidden from the programmer) with information about the class and pointers to the virtual functions. This table is updated in the derived classes and this is how the virtual functions work (instead of making a direct call to the function the function pointer is called in this table).

The solution goes through, as mentioned @Trauma , by converting the types, which by the way is also You can do with dynamic_cast :

Area *ptr = ... // puntero a la clase base.

if( auto tmp = dynamic_cast<AreaRectangular*>(ptr) ) {
  // Ya podemos llamar a la función que queramos.
  tmp->reiniciar( ...argumentos... );
} else if( auto tmp = dynamic_cast<AreaCircular*>(ptr) {
  // Ya podemos llamar a la función que queramos.
  tmp->reiniciar( ...argumentos... );
}

By the way ... you have not put the implementation of any reiniciar ... but it does not make much sense that you return a reference because ... reference to which object?

  • If it is to itself it does not make sense because you are already handling the object yourself, then you do not need the reference:

    AreaCircular area;
    Area& area2 = area.reiniciar(); // area2 apunta a area... no es necesario
    
  • If it turns out that reiniciar creates an internal object and returns it ... being a reference will be erased just after the return and before the execution leaves the function ... then you will get a reference to an object that is not going to be valid never :

    AreaCircular& AreaCircular::reiniciar()
    {
      AreaCircular area2;
      return area2;
    } // Al llegar este punto se destruye area2
    
    AreaCircular area;
    AreaCircular& area2 = area.reiniciar(); // referencia a objeto destruido
    

I do not see any need to justify that function should return nothing ... but as I say I do not see the implementation so just notice the possible adverse effects (readability or invalid access) you can get when leaving the function just as it is.

    
answered by 07.04.2018 / 09:37
source
3
  

How can I use a pointer to Area to invoke the version of reiniciar() that I want depending on the type of object targeted?

You can not.

C ++ is a strong typing language, in broad strokes this means (among many other things) that a data of one type is not (and can not be) a data of another type. So if you have a pointer to Area : you can only call the public methods of Area no more, no less.

So if your goal is to call the methods of a class derived from Area what you need is a data (instance, pointer or reference) to that class; luckily it is possible to convert data from base class to derived class (and vice versa):

Area *a = new AreaCircular{};
static_cast<AreaCircular *>(a)->reiniciar(Posicion2D{}, .0);

It's easy and straightforward, but it can be problematic if in the pointer a we would not have had a AreaCircular , luckily we can use a dynamic conversion instead of a static one:

Area *a = new AreaCircular{};
if (AreaRectangular *ar = dynamic_cast<AreaRectangular *>(a))
{
    ar->reiniciar(Posicion2D{}, Tamanyo2D{});
}

The dynamic conversion ( dynamic_cast ) will return a null pointer if the conversion is not possible (or will launch std::bad_cast if we are converting references), in this way we can avoid the problem of converting pointers that should not be convertible.

Therefore your question can be answered with the following code:

// Función de ayuda, para escribir menos
template <typename D, typename B>
D *b2d(B &b) { return dynamic_cast<D *>(&b); }

Area &reiniciame(Area &a)
{
    if (auto ac = b2d<AreaCircular>(a))
        return ac->reiniciar(Posicion2D{}, .0); // de AreaCircular
    else if (auto ar = b2d<AreaRectangular>(a))
        return ar->reiniciar(Posicion2D{}, Tamanyo2D{}); // de AreaRectangular
}

The problem of dynamic conversion is that it usually has an appreciable impact on the performance of the program, so it is usually advised to avoid it if it is not strictly necessary ... which leads me to:

Council.

Forget the polymorphism in this context, from my point of view it is a sobreengineering ; the polymorphism is a tool to treat in a generic way (base) specific types (derivatives) and in your case you do not need generic treatments if not specific ergo: you are using the wrong tool for your problem; On the other hand, your objects are extremely simple:

struct AreaCircular {
    Posicion2D pos{};
    double radio{}
};

struct AreaRectangular {
    Posicion2D pos{};
    Tamanyo2D tamanyo{};
};

Allow the compiler to work for you to select the right collision type, using overload (which is a kind of polymorphism) of free functions:

// Punto contra rectángulo
bool colisiona(const Posicion2D &pos, const AreaRectangular &ar);
// Punto contra círculo
bool colisiona(const Posicion2D &pos, const AreaCircular &ac);
// Punto rectángulo contra círculo
bool colisiona(const AreaRectangular &ar, const AreaCircular &ac);
// ...
    
answered by 09.04.2018 в 09:57