Problema con g++,compilar múltiples ficheros.

m0rG

El caso es que lo más probable es que tenga que hacer el PFC usando C++ y como nunca lo había tocado me he puesto hoy a aprender el lenguaje.Estoy haciendo ejemplos chorras para ver la sintaxis del lenguaje pero me he atascado con un programa simple con herencia entre clases.

Estoy intentando crear 2 clases,una llamada Animal que represente cualquier tipo de Animal y otra llamada Perro que hereda de ella.El ejemplo es muy sencillo si meto las definiciones de ambas clases en un fichero junto con la función main que sirve de ejemplo.Sin embargo lo que quiero hacer es definir cada cosa por separado,ya que en aplicaciones grandes es lo más cómodo.Es decir,quedaría tal que así:

Animal.h - Fichero header que contiene la definición de la clase Animal y la declaración de sus métodos.
Animal.cpp - Fichero c++ que contiene la definición de los métodos de Animal.h (con un #include "./Animal.h")

Perro.h - Fichero header que contiene la definición de la clase Perro (como heredera de Animal) y la declaración de sus métodos.
Perro.cpp - Fichero c++ que contiene la definición de los métodos de Perro.

prueba_herencia.cpp - Programa de prueba que incluye a Animal.h y/o Perro.h para ver que funcionan bien.

El problema es que Animal.cpp y el programa de prueba compilan bien,pero Perro no por que creo que no hago bien el linkado de ambos ficheros ya que cuando en el constructor intento inicializar cualquier variable heredada de Animal me dice que no existe.

Los códigos fuentes son los siguientes:

Animal.h - http://pastebin.com/m11575fd9
Animal.cpp - http://pastebin.com/m7ec4eeff

Perro.h - http://pastebin.com/m620343ef
Perro.cpp - http://pastebin.com/m5609e8f8

Ejemplo - http://pastebin.com/m33f65a21

El error que me indica es el siguiente:

g++ Animal.cpp Perro.cpp prueba_herencia.cpp -o herencia
././Animal.h: In constructor ‘Perro::Perro()’:
././Animal.h:9: error: ‘std::string Animal::nombre’ is private
Perro.cpp:7: error: within this context
././Animal.h: In constructor ‘Perro::Perro(std::string)’:
././Animal.h:9: error: ‘std::string Animal::nombre’ is private
Perro.cpp:12: error: within this context
././Animal.h: In constructor ‘Perro::Perro(std::string, std::string)’:
././Animal.h:9: error: ‘std::string Animal::nombre’ is private
Perro.cpp:17: error: within this context
prueba_herencia.cpp:11:2: warning: no newline at end of file

¿Qué estoy haciendo mal?

m0rG

Se me olvidaba comentar que estoy compilando estos ficheros con el comando:

g++ Animal.cpp Perro.cpp prueba_herencia.cpp -o herencia

Incrusto el código fuente para que se vea mejor (aunque es un ejemplo muy sencillo) :

Animal.h :

#ifndef Animal_h
#define Animal_h

#include <iostream>

using namespace std;

class Animal {
	string nombre;
	public:
		Animal();
		Animal(string nom);
		string get_Name();
};

#endif

Animal.cpp:

#include <iostream>
#include "./Animal.h"

using namespace std;

Animal::Animal(){
	nombre="Bicho";
	cout << "No se ha especificado ningun nombre para el animal" << endl;
};

Animal::Animal(string nom){
	nombre=nom;
	cout << "El animal se llama: " << nombre << endl;
}

string Animal::get_Name(){
	return nombre;
}

Perro.h :

#ifndef Perro_h
#define Perro_h

#include <iostream>
#include "./Animal.h"

using namespace std;

class Perro : public Animal {
	string raza;
	public:
		Perro();
		Perro(string nom);
		Perro(string nom,string rz);
		string get_Raza();
};

#endif

Perro.cpp:

#include <iostream>
#include "./Perro.h"

using namespace std;

Perro:: Perro(){
	nombre="Chucho";
	raza="Indefinida";
};

Perro:: Perro(string nom){
	nombre=nom;
	raza="Indefinida";
};

Perro:: Perro(string nom,string rz){
	nombre=nom;
	raza=rz;
};

string Perro::get_Raza(){
	return raza;
};
dagavi

El problema de compilación está en que desde Perro, subclase de Animal, estás accediendo al campo PRIVADO "nombre" de la clase Animal e intentándolo editar. (nota: dentro de una clase, si no se dice nada, por defecto es private, sin embargo si creas un struct es al revés, por defecto es public)

Como puede que sepas, un campo privado solo es visible desde la propia clase, si quieres que ese campo sea visible también desde las subclases ponlo como protected o crea una función pública para poder modificar el nombre.

class Animal {
    protected:
        string nombre;
    public:
        Animal();
        Animal(string nom);
        string get_Name();
};

Por otro lado, al hacer el include no hace falta que pongas el "./" pones directamente #include "File.h", y te buscará primero en el directorio actual y después en el directorio de includes por defecto.

m0rG

#3

Tienes más razón que un santo,ese era el problema ;).Ha sido un fallo de comprensión lectora por mi parte (es lo que tiene leer la documentación en inglés).Había entendido que una clase hereda siempre todos los atributos de la clase base con los "permisos" que éstos tienen y que cuando hablaban de permisos de acceso se referían a acceder al atributo (por ejemplo haciendo un a.nombre,siendo a un objeto de Animal).Sin embargo,parece que no es así.

He cambiado el atributo de la clase Animal a protected y ha funcionado.Sin embargo he visto algo curioso que no sé si es problema de la implementación o es que c++ realmente funciona así.En la función de ejemplo cuando creo un objeto de la clase Perro (la clase derivada) se llama al constructor por defecto de la clase Animal (Animal() ) además de al constructor de Perro que corresponda.De hecho si elimino el constructor por defecto de Animal el programa no compila.¿Esto es así siempre o es un fallo de mi implementación?,¿Cuando una clase es base para otras tiene que tener definido obligatoriamente un constructor por defecto (no sé si es el nombre apropiado,me refiero a un constructor sin argumentos) ?

Sobre lo de los includes también tienes razón,lo puse por asegurarme que no era un problema de localización de los archivos :P .

dagavi

Las creadoras si, se llaman primero la base y después la derivada:
Animal
Perro

Y con la destructora pasa lo mismo, pero a la inversa, se destruye la derivada y después la base:
Perro
Animal

Si tuvieras herencia múltiple pues más de lo mismo: primero las bases y finalmente la derivada (y la destrucción igual, primero la derivada y finalmente las bases).

En lo de la creadora, por lo que he comprobado el "problema" está en que el compilador crea una creadora por defecto si no existe ninguna creadora, como tienes la de Animal(string) ya no se crea la creadora por defecto y debes de crearla tu expresamente (aunque la dejes vacía como Animal() {}). (edit: cuanto crea en un solo párrafo xD)

Mira esto por encima http://es.wikibooks.org/wiki/Programaci%C3%B3n_en_C%2B%2B/Herencia#CONSTRUCTORES.2C_DESTRUCTORES.2C_Y_HERENCIA y también te dice como llamar a la creadora con parámetros en vez de a la creadora por defecto a la hora de crear una clase derivada.

cabron

#4:

Si tienes una clase que hereda de otra, al instanciar un objeto, hay que crear la parte de la clase base, y la parte de la clase derivada, en resumen, sí, al instanciar perro, se llama al constructor de animal.

Usuarios habituales

  • cabron
  • dagavi
  • m0rG