2
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
using namespace std;

class figura{
   protected: 
    string nombre;
    int lados;
   public:
    virtual int area();    

};

class cuadrado:public figura{

    public:
        cuadrado(){
        lados=4;
    }
    virtual int area(){
    return lados*lados;
    }
};

class triangulo:public figura{
    int a,b;
    public:
        triangulo(){
        lados=3;
    }
    void pedir(){
    cout<<"Base: "<<endl;
    cin>>b;
    cout<<"Altura: "<<endl;
    cin>>a;
    }
    virtual int area(){
    return ((b*a)/(2));
    }
};



main(){
    int op;
    int *punt;
    int v;
    cout<<"FIGURAS"<<endl;
    cout<<"1. Cuadrado\n2. Triangulo"<<endl;
    cin>>op;
    if(op==1){
        cuadrado ob1;
        v=ob1.area();
        punt=&v;
    }
    else{
        triangulo ob1;
        ob1.pedir();
        ob1.area();
    }
    system("pause");
}
Juan Salvador Portugal
  • 6,565
  • 8
  • 19
  • 41

2 Answers2

2

Tu problema está aquí:

class figura {
...
public:
  virtual int area( );
};

Declaras tu función-miembro como virtual, pero no tienes el cuerpo de la función, ni la has declarado como virtual pura.

El compilador, cuando encuentra una función-miembro virtual, tiene libertad para implementarla a su gusto; la mayoría (por no decir todos) usan un mecanimo, VPTR + VTABLE.

Digamos que consiste en añadir un puntero oculto a tu clase, que contiene la dirección de las funciones virtuales de la misma; sería algo así:

// El compilador general algo como esto

class figura;

struct figuraVTABLE {
  int ( *area )( figura *this );
};

class figura {
  struct figuraVTABLE *VPTR;
  ...
  // Las cosas reales de tu clase ...
  ...
};

Eso es un mecanismo oculto y transparente para tí; no es necesario que trates con ello. El compilador se encarga de eso sin que tú tengas siquiera que saberlo.

Además de lo anterior, C++ trabaja con unidades de traducción individuales: está permitido declarar cosas en un archivo e implementarlas en otro. Es el motivo de la existencia de archivos de cabecera, y de que la generación de un ejecutable se realice en 2 pasos: compilación + enlazado.

¿ El problema de todo esto ? Pues que puedes declarar cosas en un sitio, y olvidar darles un valor ... que es lo que te está pasando.

Al declarar una función-miembro como virtual, el compilador añade una variable a tu clase, pero no le asigna ningún valor; es el enlazador el que se encarga de eso. Y ya sabemos que ese valor puede estar en otra unidad de traducción.

Ese es tu problema: el VPTR de tu clase no se corresponde con ninguna VTABLE.

Tienes 2 soluciones:

  1. Declara tu función-miembro como pura:

    class figura {
    ...
    public:
      virtual int area( ) = 0; // <- AQUÍ
    };
    

    Con eso, ya estás inicializando tu VPTR. Informas de que esa función-miembro no tiene cuerpo. Tu clase pasa a ser abstracta, con lo que no puedes declarar variables instancias de esa clase:

    figura mifigura1; // <-- ERROR.
    
  2. Implementa tu función-miembro; ponle un cuerpo. Esto se puede hacer en un archivo aparte y que luego el enlazador se encargue:

    // firgura.cpp
    int figura::area( ) {
    ...
    }
    

    O puedes hacerlo todo en un mismo archivo:

    class figura {
    ...
    public:
      virtual int area( ) {
        ...
      }
    };
    

Nota: esto declara tu función-miembro como inline, lo cual es una historia aparte ... pero ya nos estamos extendiendo demasiado.

En tu caso concreto, creo que la solución que buscas es la 1: convertir tu clase en una clase abstracta.

Nota: Tu problema es una variación de Símbolo externo sin resolver. ¿ Qué he hecho mal ?

Trauma
  • 25,297
  • 4
  • 37
  • 60
1

Todo tu problema se puede resumir en el siguiente código:

struct B
{
    virtual void f();
};

struct D : B
{};

int main()
{
    D{}.f();
    return 0;
}

El error completo sería:

undefined reference to `B::f()'
In function `B::B()':
undefined reference to `vtable for B'
undefined reference to `B::f()'

Es claro y autoexplicativo, pero lo traduzco para que lo sea aún más:

referencia a `B::f()' no definida
En la función `B::B()':
referencia a `vtable for B' no definida
referencia a `B::f()' no definida

El error nos ha dicho tres veces que la función f de B no está definida, y es cierto, está sólo declarada, así que para que el error desaparezca debes definir la función area de figura:

class figura {
protected: 
    string nombre;
    int lados;
public:
    virtual int area() { return {}; }
};

Pero en vista de que cada una de las figuras derivadas van a sobrescribir la función area, no tiene sentido que ésta tenga definición, podrías evitar definirla haciéndola virtual pura:

class figura {
protected: 
    string nombre;
    int lados;
public:
    virtual int area() = 0;
};

Esta solución, además de tener la ventaja de no escribir código que no tiene sentido que sea escrito, te obliga a definir la función en las clases derivadas y provoca error de compilación en caso de que se te olvide hacerlo.

PaperBirdMaster
  • 44,474
  • 6
  • 44
  • 82