#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");
}
- 6,565
- 8
- 19
- 41
- 21
- 2
2 Answers
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:
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.
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 ?
- 25,297
- 4
- 37
- 60
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.
- 44,474
- 6
- 44
- 82