Hallo LUG,
der Prüfungsstress hat sich für mich nun erst einmal erledigt und ich kann mich endlich mal wieder ein paar anderen Dingen widmen. Dabei bin ich heute ein ein Problem gestoßen, für das mir bei einem LUG-Treffen schon einmal jemand eine Lösung gezeigt hat (Ulf oder Tobias??). Allerdings war kurz darauf die Platte von dem Notebook gecrashed und ich konnte mich nicht mehr daran erinnern. Das Problem ist in folgendem, stark vereinfachten Quelltext:
<--------------------- TEST.CPP --------------------->
#include <stdio.h> #include <stdlib.h>
class bus { public: int start( ); };
class bahn { public: int start( ); };
int bus::start( ) { printf("Bus wurde gestartet...\n"); }
int bahn::start( ) { printf("Bahn wurde gestartet...\n"); }
main() { char *fahrzeug[ 3 ]; // ^^^^ ????
fahrzeug[ 1 ] = new bus( ); fahrzeug[ 2 ] = new bahn( ); fahrzeug[ 1 ] -> start( ); fahrzeug[ 2 ] -> start( ); }
------------------------------------------------------
Ich möchte also einen einheitlichen Typ haben, in dem ich sowohl Instanzen des Objektes "Bus" wie auch jenen des Objektes "Bahn" ablegen kann. Ich hab hier nur einmal rezeptmäßig "char" hingeschrieben, was aber nicht funktioniert.
Das Ganze hat folgenden Hintergrund: ich möchte ein objekt- orientiertes Toolkit mit NCurses schreiben, das Widgets (Eingabefelder, Listen, Buttons) für die Textconsole zur Verfügung stellt. Dazu möchte ich eine Containerklasse bauen, die mit o.g. Vorgehensweise verschiedene Widgets (jeweils eigenständige Klassen) verwalten kann und dabei quasi als Keyserver fugiert.
Ich bin schon fleißig am Lesen vom GoTo C++ Buch, dass mir Heiko empfohlen hat, aber zu meinem wahrscheinlich nicht ganz sauberen Ansatz hab ich da nichts gefunden, zumindest nicht beim Stöbern im Stichwortverzeichnis.
Ich würde mich freuen, wenn jemand von euch die Zeit findet und mir dabei helfen kann.
Schöne Pfingsten,
Matthias
-- ------------------------------------ Matthias Petermann --( )- wget http://linux.unixcity.de/matthias/key -O - | gpg --import gpg: 1E3D E373 0CCA AD8A D203 B210 8260 ED77 95D7 030D ------
On Sunday, 3. June 2001 00:59, Matthias Petermann wrote:
Hallo, ich schmier gleich mal im gequoteten rum, bin schreibfaul :-)
class bus : public class oepnv { public: int start( ); };
class bahn : public class oepnv { public: int start( ); };
Damit haben beide denselben Basistyp, und auch alle Eigenschaften und Methoden davon, z.B.:
class oepnv { oepnv(); ~oepnv(); virtual unsigned int fahrgaeste(); // je nach Fahrzeug unterschiedlich int farbe; // immer gelb in Dresden };
Für die Konsole könnte man das dann so machen: Die Basisklasse ist ein aus ASCII-Zeichen aufgebautes Rechteck, davon abgeleitet eines mit Beschriftung, sowie ein Objekt man markieren kann (muß nicht graphisch sein), beides zusammen ergibt dann z.B. einen Button.
Eventuell ist auch SLang für dich interessant als Alternative für NCurses.
Josef Spillner
On Sunday 03 June 2001 00:59, Matthias Petermann wrote: Du must von einer gemeinsamen Klasse ableiten und die Methode virtual deklarieren (d.h. welche von beiden benutzt wird entscheidet sich erst zur Laufzeit).
<--------------------- TEST.CPP --------------------->
#include <stdio.h> #include <stdlib.h>
class c_fahrzeug { public: virtual int start()=0; };
class bus { public: int start( ); };
class bahn { public: int start( ); };
int bus::start( ) { printf("Bus wurde gestartet...\n"); }
int bahn::start( ) { printf("Bahn wurde gestartet...\n"); }
main() { char *fahrzeug[ 3 ]; // ^^^^ ????
c_fahrzeug *fahrzeug[3];
fahrzeug[ 1 ] = new bus( ); fahrzeug[ 2 ] = new bahn( );
fahrzeug[ 1 ] -> start( ); fahrzeug[ 2 ] -> start( );
}
Ich möchte also einen einheitlichen Typ haben, in dem ich sowohl Instanzen des Objektes "Bus" wie auch jenen des Objektes "Bahn" ablegen kann. Ich hab hier nur einmal rezeptmäßig "char" hingeschrieben, was aber nicht funktioniert.
Das wäre dann die gemeinsame Basisklasse c_fahrzeug.
Hinweis: das "=0" bedeutet es ist eine abstrakte Methode, was übersetzt soviel heißt wie "ich bin nur die Basisklasse, die Funktionen werden erst in den abgeleiteten Klassen definiert". Von solchen abstrakten Klassen darf man zwar Pointer und Referenzen aber keine Instanzen erzeugen.
[cut]
Ich bin schon fleißig am Lesen vom GoTo C++ Buch, dass mir Heiko empfohlen hat, aber zu meinem wahrscheinlich nicht ganz sauberen Ansatz hab ich da nichts gefunden, zumindest nicht beim Stöbern im Stichwortverzeichnis.
Die Stichworte lauten "Vererbung", "virtuelle Methoden/Klassen", "Ableitung", "abstrakte Methoden".
Konrad
Hallo,
erst einmal vielen Dank für Eure Hilfe. So funktioniert es prima, allerdings bin ich gleich auf ein weiteres Problem gestoßen:
<-------------- TEST.CPP -------------> class c_fahrzeug { int fahrgaeste; public: virtual int start( ) = 0; };
class bus : public c_fahrzeug { public: int start( ); };
class bahn : public c_fahrzeug { public: int start( ); int notbremsen( ); };
int bus::start( ) { printf("Bus wurde gestartet...\n"); }
int bahn::start( ) { printf("Bahn wurde gestartet...\n"); }
int bahn::notbremsen( ) { printf("Notbremse der Bahn wurde gezogen\n"); }
main() { c_fahrzeug *fahrzeug[ 3 ];
fahrzeug[ 1 ] = new bus( ); fahrzeug[ 2 ] = new bahn( ); fahrzeug[ 1 ] -> start( ); fahrzeug[ 2 ] -> start( ); fahrzeug[ 2 ] -> notbremsen( ); } ------------------------------------------------------
Wie man erkennen kann wollte ich der abgeleiteten Klasse "bahn" eine zusätzliche Methode "notbremsen" geben, die aber _nicht_ Element der Klasse "bus" sein soll.
aber egcs meint... t.C:46: no matching function for call to `c_fahrzeug::notbremsen ()' :(
Was hab ich jetzt noch falsch gemacht?
Matthias
On Sunday 03 June 2001 11:53, Matthias Petermann wrote:
Hallo,
erst einmal vielen Dank für Eure Hilfe. So funktioniert es prima, allerdings bin ich gleich auf ein weiteres Problem gestoßen:
[cut]
Wie man erkennen kann wollte ich der abgeleiteten Klasse "bahn" eine zusätzliche Methode "notbremsen" geben, die aber _nicht_ Element der Klasse "bus" sein soll.
aber egcs meint... t.C:46: no matching function for call to `c_fahrzeug::notbremsen ()'
:(
Logisch, in c_fahrzeug (und von dem Typ sind nun mal die Pointer) ist die Methode abgeleitet. Es wäre etwas viel verlangt von einem Compiler den Zustand des Pointers zur Laufzeit vorherzusagen (einen Interpreter juckt das nicht, weil er den Code sowieso erst zur Laufzeit auswertet).
Es gibt also offensichtlich 2 Möglichkeiten:
a) Du sorgst dafür, dass c_fahrzeug eine Methode notbremsen() hat.
a1) ------------- class c_fahrzeug{ //.... void notbremsen(); }; c_fahrzeug::notbremsen(){printf("dummy\n");} ------------ ->was Dir, wie Du sehen wirst nix bringt, der Compiler nimmt dann nämlich immer die Notbremse von c_fahrzeug (schließlich ist der Pointer von welchem Typ? Richtig: c_fahrzeug.)
a2) also sagen wir ihm "löse erst zur Laufzeit auf": ------------- class c_fahrzeug{ //.... virtual void notbremsen()=0; }; ------------- -> was Dir den Fehler "cannot instantiate abstract class bus" einbringen wird, denn theoretisch könnte man ja auf die Idee kommen die abstrakte Methode notbremsen() auch bei bus anzuwenden, wo sie nicht definiert wurde, und das will der Compiler verhindern. a3) kombinieren wir mal beides: ------------- class c_fahrzeug{ //.... virtual void notbremsen(); }; c_fahrzeug::notbremsen(){printf("dummy\n");} ------------ -> was weiterhelfen würde, aber bus hätte nun auch eine dummy-Notbremse. (Was ja nicht schaden muss.)
b) Du benutzt einen Cast ------------ ((bahn*)(fahrzeug[2]))->notbremsen(); ------------ -> Was aber sehr gefährlich sein kann, sobald nicht eine bahn sondern ein bus in diesem Element gespeichert ist - dann kann die Methode nämlich ihre Daten nicht finden und man bekommt sehr seltsame Abstürze (zugegeben bei diesem Beispiel wird es funktionieren, aber nur, weil es gar keine Speicherelemente einsetzt).
Was hab ich jetzt noch falsch gemacht?
Das Buch noch nicht komplett durchgearbeitet.
Ich schlage vor, nach diesem Ausflug in die Gefilde der Fortgeschrittenen solltest Du erst einmal systematisch die Tutorials in Deinem Buch durcharbeiten und verstehen - oder zumindest (wie ich das oft mache) das Buch komplett durchlesen, um einen Überblick zu erhalten, was man alles beachten muss.
Konrad
PS.: bitte schick Deinen Quelltext das nächste mal als Attachment, das läßt sich besser speichern und austesten.
PPS.: Funktionen und Methoden, die nix zurückliefern sind normalerweise vom Typ void.
Am Son, 03 Jun 2001 schrieb Konrad Rosenbaum:
On Sunday 03 June 2001 11:53, Matthias Petermann wrote:
... <snip>
Was hab ich jetzt noch falsch gemacht?
Das Buch noch nicht komplett durchgearbeitet.
Ich schlage vor, nach diesem Ausflug in die Gefilde der Fortgeschrittenen solltest Du erst einmal systematisch die Tutorials in Deinem Buch durcharbeiten und verstehen - oder zumindest (wie ich das oft mache) das Buch komplett durchlesen, um einen Überblick zu erhalten, was man alles beachten muss.
Konrad
PS.: bitte schick Deinen Quelltext das nächste mal als Attachment, das läßt sich besser speichern und austesten.
Und am besten gleich noch ein Makefile dazu.
PPS.: Funktionen und Methoden, die nix zurückliefern sind normalerweise vom Typ void.
-pedantic
Bye, Stephan
Hallo,
also noch einmal vielen Dank an alle, die auf meine Frage geantwortet haben. Ich glaube nun die beste Methode für mein Programm gefunden zu haben und werde selbstverständlich das Buch weiterlesen. Bin nur manchmal ein bisschen ungeduldig wenn etwas nicht gleich auf Anhieb so klappt, wie ich es gerne hätte ;) Aber ich gelobe Besserung...
Schönen Sonntag noch,
Matthias
On Sun, Jun 03, 2001 at 09:53:46AM +0000, Matthias Petermann wrote:
Hallo,
Hallo Matthias,
erst einmal vielen Dank für Eure Hilfe. So funktioniert es prima, allerdings bin ich gleich auf ein weiteres Problem gestoßen:
<schnipp>
main() { c_fahrzeug *fahrzeug[ 3 ];
fahrzeug[ 1 ] = new bus( ); fahrzeug[ 2 ] = new bahn( );
fahrzeug[ 1 ] -> start( ); fahrzeug[ 2 ] -> start( ); fahrzeug[ 2 ] -> notbremsen( ); }
Ich bin mir nicht ganz sicher, da ich nicht gerade der C++-Profi bin, aber meines Wissens nach kann der Compiler nicht von selbst aus `fahrzeug' wieder die richtige Klasse erzeugen. Daher müsstest du in der Klasse `fahrzeug' noch eine Variable definieren, in der der Typ der Klasse gespeichert wird. Dann sieht der Code so aus:
if (fahrzeug[1]->type() == "Bahn") { bahn* obj = (bahn*)fahrzeuge[1]; obj->start(); obj->notbremsen(); }
if (fahrzeug[1]->type() == "Bus") { bus* obj = (bus*)fahrzeuge[1]; obj->start(); }
Normalerweise brauchst du das aber gar nicht, da man über so eine Liste sowieso nur die Methoden aufruft, die die Basisklasse beinhaltet. Bei Qt kann man z.B. setGeometry(), setFocus() o.ä. über solche Listen behandeln, da alle Widgets von QWidget abgeleitet sind, welches diese Methoden definiert.
Ciao, Tobias
lug-dd@mailman.schlittermann.de