C++ Einfache Frage zur Vererbung

Sponny

Lt. Commander
Registriert
März 2008
Beiträge
1.052
Hallo liebe Community,

ich habe bereits nachfolgenden code geschrieben:

using namespace std;

typedef enum color{ROT, GRUEN, GELD, BLAU}color;

class oberklasse {
public:
color farbe; /* color ist vom typ enum */
color get_color(){return farbe;}

virtual float foo() = 0;
virtual ~oberklasse(){};
};

class objekt : public oberklasse{

private:
float laenge;
float breite;

public:

objekt():laenge(0), breite(0), farbe(BLAU){};
objekt(float l, float b, color f):laenge(l), breite(b), farbe(f){};
~objekt(){};

float get_laenge(){return laenge;};
float get_breite(){return breite;};

char print_farbe();
float foo(){
return laenge*breite;
}

};

Ich möchte das Attribut "color farbe" an mein objekt vererben.
Ich habe dies bereits in meiner Oberklasse deklariert.
Sobald ich aber nun "color farbe" aus meinem objekt nehme, bekomme ich einen fehler:
"der Konstruktor des Objekts möchte farbe initialisieren. Dies geht aber anscheinend nicht, da ich dieses nicht in objekt deklariert habe"

Anschließend würde ich gerne über die Methode get_color() aus meiner oberklasse die farbe des objekts abrufen können.

Weiß jemand wo mein Fehler ist?

Vielen Dank im Voraus.
 
Zuletzt bearbeitet:
Dasselbe Thema hatten wir schon vor ein paar Tagen. Du musst "public" vererben, also sprich:
Code:
class objekt : public oberklasse{
Wenn du das nicht machst, wird standardmäßig private vererbt, d.h. alle Variablen der Basisklasse sind private Variablen in der abgeleiteten Klasse. Deshalb hast du keinen Zugriff drauf.

Außerdem hast du da "puplic" anstatt "public" stehen.
 
stwe schrieb:
Dasselbe Thema hatten wir schon vor ein paar Tagen. Du musst "public" vererben, also sprich:
Code:
class objekt : public oberklasse{
Wenn du das nicht machst, wird standardmäßig private vererbt, d.h. alle Variablen der Basisklasse sind private Variablen in der abgeleiteten Klasse. Deshalb hast du keinen Zugriff drauf.

Außerdem hast du da "puplic" anstatt "public" stehen.

das habe ich leider schon versucht, hat nichts geholfen. :(
da habe ich beim übertragen, einen fehler gemacht. im code ist das richtig drin. mit den public. :)

Immer noch der folgende Fehler:

.\main.cpp:38:60: error: class 'objekt' does not have any field named 'farbe'

Und zwar genau in diesen beiden Zeilen:

objekt():laenge(0), breite(0), farbe(BLAU){};
objekt(float l, float b, color f):laenge(l), breite(b), farbe(f){};
 
Zuletzt bearbeitet:
Was fürn Compiler nutzt du? Ich finde der Code sieht sehr seltsam aus. Der Compiler von VS 2012 findet den Code auch nicht gut. Versuch mal deinen Code etwas hübscher zu machen, so mit Einrücken und so. Ich zeig dir mal wie ich das meine. So kann man den Code zumindest kompilieren. Versuch doch mal ob das dein Problem löst.

Code:
#include "stdafx.h"

using namespace std;

 typedef enum color
 {
	 ROT, 
	 GRUEN, 
	 GELB, 
	 BLAU
 }color;

 class oberklasse 
 {
	 public: color farbe; /* color ist vom typ enum */
	 color get_color()
	 {
		 return farbe;
	 }
	 virtual float foo() = 0;
 };

 class objekt : oberklasse
 {
	 private:
	 float laenge;
	 float breite;

	 public:
	 objekt()
	 {
		 laenge = 0; 
		 breite = 0;
		 farbe = BLAU;
	 };

	 objekt(float l, float b, color f)
	 {
		 laenge = l;
		 breite = b;
		 farbe = f;
	 };

	 float get_laenge()
	 {
		 return laenge;
	 };

	 float get_breite()
	 {
		 return breite;
	 };

	 char print_farbe();

	 float foo()
	 {
		return laenge*breite;
	 }
 };
 
Also ... deine Klasse objekt erbt zwar den Datenmember farbe von ihrer Basisklasse oberklasse und kann den auch normal verwenden, aber für das Initialisieren ist immer noch der Konstruktor von oberklasse und nicht der von objekt zuständig! Wenn du dein Vorhaben so umsetzen möchtest, solltest du auch oberklasse einen parametrierbaren Konstruktor verpassen und den dann von objekt aus nutzen:

Code:
class oberklasse {

protected:
	oberklasse(color dieFarbe) : farbe(dieFarbe) {}

public:
	color farbe; /* color ist vom typ enum */

	color get_color(){return farbe;}

	virtual float foo() = 0;
	virtual ~oberklasse(){};
};

class objekt : public oberklasse{

private:
	float laenge;
	float breite;

public:

	objekt() : oberklasse(BLAU), laenge(0), breite(0){}
	objekt(float l, float b, color f) : oberklasse(f), laenge(l), breite(b) {}
	~objekt(){};

	float get_laenge(){return laenge;};
	float get_breite(){return breite;};

	char print_farbe();
	float foo(){
		return laenge*breite;
	}
};

Im übrigen möchte ich davon abraten, irgendwelche Datenmember public zu machen. Die sollten mindestens protected, besser noch private sein, da es selbst in einer Klassenhierarchie nicht wirklich schön ist, wenn Klassen Datenmember ihrer Basisklassen direkt (also ohne eine Methode der Basisklasse aufzurufen) modifzieren.

Außerdem könntest du diverse Methoden (deine ganzen Getter) alle als const deklarieren, da sie die Instanz, an der sie aufgerufen werden, nicht verändern.
 
Zuletzt bearbeitet:
Edit: Über mir war einer schneller.

Man kann keinen Member der Oberklasse in einem Konstruktor einer Unterklasse initialisieren. Eine Lösung des Problems wäre die:

Code:
class oberklasse {
public:
  oberklasse(color _farbe, ...) // ggf. andere Attribute
  : farbe(_farbe), ... { }

  color farbe; // ugly
};

class objekt : public oberklasse {
public:
  objekt()
  : oberklasse(BLAU) { }

private:
  float laenge = 0.0f;
  float breite = 0.0f;
};

oberklasse verliert dadurch aber den Default-Constructor - bei Bedarf nachliefern und v.a. Standard-Initializer vergeben.


Edit 2:
Im übrigen möchte ich davon abraten, irgendwelche Datenmember public zu machen.
Dem dürfte niemand widersprechen...

da es selbst in einer Klassenhierarchie nicht wirklich schön ist, wenn Klassen Datenmember ihrer Basisklassen direkt (also ohne eine Methode der Basisklasse aufzurufen) modifzieren.
...aber das halte ich dann doch für etwas übertrieben. Das Argument würde ich ja noch in Java verstehen, wo es offenbar zum guten Ton gehört, wenn Getter und Setter 90% des Codes ausmachen und zum Kopieren von allem, was komplexer ist als ein int, auch schonmal das Interface clonable implementiert wird, aber in C++ bringen einen die Dinger auch nicht unbedingt weiter.

Wenn man vollen Zugriff auf einen Member der Basisklasse braucht, müsste man entweder Referenzen returnen (was nun auch nicht wirklich gerne gesehen wird) oder das komplette Interface der Klasse des Members wrappen, und das schießt nun völlig übers Ziel hinaus.

Wobei man generell natürlich nur Zeug als protected deklarieren sollte, was auch in einer Unterklasse genutzt werden soll, aber das sollte klar sein.
 
Zuletzt bearbeitet:
Der Stil ist noch ausbaufähig da hast du recht.
Jetzt geht es, lag wohl daran; dass du den Konstruktor etwas verändert hast.
Danke dir.
 
Noch was. Das:

Code:
 typedef enum color
{
ROT,
GRUEN,
GELB,
BLAU
}color;

ist C-Stil. In C++ definieren wir ein enum ohne typedef:

Code:
enum color
{
ROT,
GRUEN,
GELB,
BLAU
};
 
Eine Aufzählung (sowie Struktur und Union) kann man auch in C problemlos ohne typedef definieren. Verstehe diese Anmerkung deshalb nicht ganz :(
 
asdfman schrieb:
Eine Aufzählung (sowie Struktur und Union) kann man auch in C problemlos ohne typedef definieren. Verstehe diese Anmerkung deshalb nicht ganz :(

Sorry, wollte dich nicht traurig stimmen. :p C ist nicht meine Stärke. Ich war der Meinung, daß C dieses typedef-Getanze erfordert. Vielleicht ist das im aktuellen C-Standard nicht mehr der Fall? Oder ist es nie erforderlich gewesen?
 
typedef ist in C++ obsolet, und auch wenn es funktioniert, es ist redundant. Das enum selbst definiert ja quasi schon einen Typen, den man über den Namen direkt ansprechen kann (in diesem Fall also z.B. color farbe;), in C müsste man enum color farbe; schreiben - deswegen benutzt man da typedefs.

Was man nebenbei auch machen könnte:
Code:
enum class color {
  BLAU,
  ...
};

Dann muss man zwar explizit color::BLAU schreiben, wenn man es verwenden will, aber dafür hält man den Namespace sauber.
 
VikingGe schrieb:
...aber das halte ich dann doch für etwas übertrieben. Das Argument würde ich ja noch in Java verstehen, wo es offenbar zum guten Ton gehört, wenn Getter und Setter 90% des Codes ausmachen und zum Kopieren von allem, was komplexer ist als ein int, auch schonmal das Interface clonable implementiert wird, aber in C++ bringen einen die Dinger auch nicht unbedingt weiter.

Wenn man vollen Zugriff auf einen Member der Basisklasse braucht, müsste man entweder Referenzen returnen (was nun auch nicht wirklich gerne gesehen wird) oder das komplette Interface der Klasse des Members wrappen, und das schießt nun völlig übers Ziel hinaus.

Wobei man generell natürlich nur Zeug als protected deklarieren sollte, was auch in einer Unterklasse genutzt werden soll, aber das sollte klar sein.

Im Interesse der Datenkapselung sollten abgeleitete Klassen außer in maximalst simplen Fällen niemals direkt Datenmember einer Basisklasse verändern. Eine abgeleitete Klasse sollte genau so wenig Annahmen über die Innereien einer Basisklasse machen wie irgend welcher anderer Code Annahmen über die Innereien irgend einer anderen Klasse machen sollte.

P.S. Diese Getteritis-Krankheit, von der du schreibst, ist in C++ meistens (na ja, sagen wir mal oft) ein Zeichen von suboptimalem Design. Wenn man Dutzende Getter hat, die wirklich nix weiter tun, als völlig trivial ein return auf ein Datenmember der Klasse zu liefern, dann ist das für mich ein Zeichen, daß zu viel der Logik, die eigentlich in der Klasse selbst sein sollte, nach außen verlagert worden ist.
Im Idealfall würde man nicht ständig per einfachem Getter Daten von der Klasse erfragen und dann mit diesen komplexe Dinge betreiben, sondern per Methodenaufruf gleiche diese komplexen Dinge von der Klasse selbst erledigen lassen und sich gar nicht weiter darum kümmern, ob die Klasse diese ganzen Member überhaupt hat.
 
Zuletzt bearbeitet:
antred schrieb:
Sorry, wollte dich nicht traurig stimmen. :p C ist nicht meine Stärke. Ich war der Meinung, daß C dieses typedef-Getanze erfordert. Vielleicht ist das im aktuellen C-Standard nicht mehr der Fall? Oder ist es nie erforderlich gewesen?
Ich wüsste nicht, dass es je nötig gewesen wäre. Die älteste Verwendung einer Struktur in C, die ich in 10 Minuten finden konnte, war aus Fifth Edition Unix von 1974: http://minnie.tuhs.org/cgi-bin/utree.pl?file=V5/usr/c/c00.c
Code:
struct kwtab {
	char	*kwname;
	int	kwval;
} kwtab[]
{
	"int",		INT,
	"char",		CHAR,
	"float",	FLOAT,
	"double",	DOUBLE,
	"struct",	STRUCT,
	"auto",		AUTO,
	"extern",	EXTERN,
	"static",	STATIC,
	"register",	REG,
	"goto",		GOTO,
	"return",	RETURN,
	"if",		IF,
	"while",	WHILE,
	"else",		ELSE,
	"switch",	SWITCH,
	"case",		CASE,
	"break",	BREAK,
	"continue",	CONTIN,
	"do",		DO,
	"default",	DEFAULT,
	"for",		FOR,
	"sizeof",	SIZEOF,
	0,		0,
};

€: Zur Erklärung, falls nötig. Hier wird ein Array namens kwtab[] vom Typ struct kwtab definiert und initialisiert. Ohne Typedef (aber auch ohne Zuweisungsoperator *staum*) :3
 
Zuletzt bearbeitet:
Das typedef bei C enums/structs macht man oftmals aus Bequemlichkeit. Wenn man ein "struct foo" definiert, dann muss man Variablen diesen Typs auch mit "struct foo fooObject" deklarieren. Nur ein "foo fooObject" geht nicht. Mit dem typedef geht das aber. Man spart sich also das "struct" bzw. "enum" bei der Deklaration.

Hier nich ein kompilierbares Beispiel:
Code:
struct foo {
    int bar;
    int baz;
};

typedef struct {
    int bar;
    int baz;
} foo2;

int main() {
    // Variablen vom Typ 'foo' muss man mit 'struct foo' deklarieren:
    struct foo fooObject;
    fooObject.bar = 1;

    // Deshalb compiled folgende Zeile nicht:
    //foo fooObject;
    
    // Mit typedef aber schon:
    foo2 foo2Object;
    foo2Object.baz = 1;
}
 
Zurück
Oben