Programmablauf C++



  • ahh

    ja das ist'n guter ansatz, dann bräuchte ja theoretisch nur noch
    private vererbung einzusetzen und könnte mir dann aussuchen welche werte
    davon ich für die vererbten klassen nutzen möchte

    sehr schön; danke 😉



  • Leider hast du das nciht ganz richtig verstanden. Denn wenn ich deinen KJommentar im Code richtig verstanden habe, dann bist du der Meinung, dass bei privater Vererbung nur die provaten Elemente übernommen werden. Dem ist aber nicht so, denn es werde alle Elemente übernommen, diese aber zu privaten Elementen konvertiert.

    Aber du solltest jetz auch nicht bloß noch private Vererbung benutzem. denn dies kann ein großer Fehler sein und zu Problemen führen. Stattdessen sollte man, wenn man eine Klasse benutzen will, aber nicht mit öffentlicher Vererbung arbeiten kann, eine Instanz dieser Klasse in der Klasse erzeugen, in der man sie verwenden will und dann dieses Objekt verwenden.

    Bei KFZ würde ich wohl eher zu öffentlicher Vererbung denken, da ja ein Auto ein KFZ ist und nicht durch eines implementiert wird.



  • ok

    das wirft ein anderes licht auf das ganze
    also ich kann ruhig öffentlich vererben, hab den code nur geschrieben
    um eine basis zu haben zum üben und wenn alles vererbt und dann nur
    in den entsprechenden vererbungstyp konvertiert wird, liegt wieder eine
    andere situation vor als ich dachte

    ich werd den kompletten code neu schreiben
    da jetz andere rahmenbedingungen herrschen als ich dachte

    danke für den hinweis 😉
    mfg rum



  • also ich hab den code mal umgeschrieben, und hab festgestellt das es genauso ist wie du gesagt hast

    #include <iostream.h>
    
    class kfz
    {
    	private:
    		int typ;
    		int tuev;						//alle - monate bis tüv abgelaufen
    		int asu;						//alle - monate bis asu abgelaufen
    		int personen;					//pkw only
    		int tueren;						//pkw only
    		int achsen;						//lkw only
    		int traglast;					//lkw only
    		int hubraum;					//krad only
    		int gaenge;						//krad only
    	public:
    		void kfz::eingabe();
    		void kfz::ausgabe();
    		kfz();
    		~kfz();
    };
    
    class pkw:public kfz					//alle werte von kfz übernommen und in public umgewandelt
    {
    };
    
    class lkw:public kfz					//alle werte von kfz übernommen und in public umgewandelt
    {
    };
    
    class krad:private kfz					//alle werte von kfz übernommen und in private konvertiert
    {
    };
    
    kfz::kfz()								//standartkonstruktor
    {
    };
    
    kfz::~kfz()								//standartdestruktor
    {
    };
    
    void kfz::eingabe()						//funktion eingabe
    {
    	cout<<"Bitte geben Sie den Typ des Fahrzeugs ein(PKW=1,LKW=2,KRAD=3): "<<endl;
    	cin>>typ;	
    	if (typ==1)							//eingabemaske für PKW
    		{
    			cout<<"Fahrzeug ist ein PKW"<<endl;
    			cout<<"Bitte geben Sie die Monate bis zum Ablaufen des TUEV ein: "<<endl;
    			cin>>tuev;
    			cout<<"Bitte geben Sie die Monate bis zum Ablaufen des ASU ein: "<<endl;
    			cin>>asu;
    			cout<<"Bitte geben Sie die maximale Anzahl der befoerderbaren Personen ein: "<<endl;
    			cin>>personen;
    			cout<<"Bitte geben Sie die Anzahl der Tueren ein: "<<endl;
    			cin>>tueren;
    		}
    	else if (typ==2)					//eingabemaske für LKW
    		{
    			cout<<"Fahrzeug ist ein LKW"<<endl;
    			cout<<"Bitte geben Sie die Monate bis zum Ablaufen des TUEV ein: "<<endl;
    			cin>>tuev;
    			cout<<"Bitte geben Sie die Monate bis zum Ablaufen des ASU ein: "<<endl;
    			cin>>asu;
    			cout<<"Bitte geben Sie die maximale Traglast(Tonnen) ein: "<<endl;
    			cin>>traglast;
    			cout<<"Bitte geben Sie die Anzahl der Achsen ein: "<<endl;
    			cin>>achsen;
    		}
    	else if (typ!=1 && typ!=2 && typ!=3)//wenn fallsch gewählt = ausgabe error
    		{
    			cout<<"ERROR ERROR ERROR"<<endl;
    		}
    	else								//eingabemaske für KRAD
    		{
    			cout<<"Fahrzeug ist ein KRAD"<<endl;
    			cout<<"Bitte geben Sie die Monate bis zum Ablaufen des TUEV ein: "<<endl;
    			cin>>tuev;
    			cout<<"Bitte geben Sie die Monate bis zum Ablaufen des ASU ein: "<<endl;
    			cin>>asu;
    			cout<<"Bitte geben Sie den Hubraum ein: "<<endl;
    			cin>>hubraum;
    			cout<<"Bitte geben Sie die Anzahl der Gaenge ein: "<<endl;
    			cin>>gaenge;
    		}
    }
    
    void kfz::ausgabe()						//funktion ausgabe
    {
    	if (typ==1)
    		{
    			cout<<"..PKW.."<<endl;
    		}
    	else if (typ==2)
    		{
    			cout<<"..LKW.."<<endl;
    		}
    	else
    		{
    			cout<<"..KRAD.."<<endl;
    		}
    }
    
    void main()								//main
    {
    	pkw fahrzeug1;
    	fahrzeug1.eingabe();
    	fahrzeug1.ausgabe();
    
    	krad fahrzeug2;
    	fahrzeug2.eingabe();
    	fahrzeug2.ausgabe();
    }
    

    bei der instanz fahrzeug2 meckert er nämlich wie erwartet:
    error C2248: "eingabe" : Kein Zugriff auf public Element, dessen Deklaration in der Klasse "kfz" erfolgte
    Siehe Deklaration von 'eingabe'

    demnach ist die in kfz als public funktion deklarierte funktion eingabe in
    private konvertiert wurden, wie es in der vererbung von krad auch steht

    oder hab ich irgendwo nochn denkfehler?

    ein digges danke an viande
    mfg rum



  • Dystopic schrieb:

    ich versteh die Objekterzeugung in main nicht so ganz. Brauche ich für die Klasse A wirklich kein Konstruktor schreiben und ab wann kann ich mich nicht mehr auf den automatischen Konstruktor verlassen?

    Du kannst, musst aber nicht, denn der Konstruktor in A impliziert durch das Default-Argument einen Default-Konstruktor. Du kannst also A obj oder A obj(4) schreiben. Im ersten Fall wird der gleiche Ctor aufgerufen wie im zweiten mit dem Unterschied, dass du im ersten Fall der Variablen i das Default-Argument zugewiesen wird und im zweiten Fall das übergebene Argument.

    //Edit: Seh grad, dein Post ist schon älter. Aber vielleicht ist er ja trotzdem noch aktuell für dich.

    Caipi



  • Mist, voll verlesen ... hattest es ja schon selber rausgefunde ... :p



  • viande schrieb:

    Mist, voll verlesen ... hattest es ja schon selber rausgefunde ... :p

    Ich vereinfache einmal das Problem, vor dem ich stand:

    class FwzBaseClass{
     const USHORT fieldsize; // soll immer 10 sein
     ULONG lastrecord;
     // ... und noch einige weitere Elemente
    public:
     FwzBaseClass(HFILE* tablefile);
     USHORT fdsize();
    };
    

    Dem Konstruktor der Klasse sollte ein Dateihandle übergeben werden, anhand
    der Signatur der Datei sollte der Konstruktor herauslesen, an welcher Byte-Position der letzte Datensatz beginnt, und den Wert (etwa nach einem Aufruf von atol()) im ULONG Member 'lastrecord' abspeichern. Egal, welche Datei gerade geöffnet wurde, jedes Feld innerhalb eines FileWizard Datensatzes ist immer 10 Byte gross (es haengt nur von der Typmaske ab, ob diese 10 Bytes als Daten oder als Referenz interpretiert werden), das heisst, fieldsize ist IMMER 10 Byte gross, und es ist naheliegend, eine Konstante dafuer in der Klasse fwzBaseClass abzulegen. Mit dem Konstruktor

    FwzBaseClass(HFILE* tablefile);
    

    ist es aber natuerlich nicht moeglich, die Konstante fieldsize mit dem Wert 10 zu versehen, da Konstanten nur innerhalb einer Initialisierungsliste eines Konstruktors Werte zugewiesen werden können, und nirgendwo sonst. Es muesste also heißen:

    FwzBaseClass(HFILE* tablefile):fieldsize(10){}
    

    aber bekanntlich müssen in diesem Fall die Funktionsrumpfklammern gleich daneben stehen (es ist also nicht möglich, den Konstruktorrumpf irgend wo anders zu definieren:

    FwzBaseClass::FwzBaseClass(HFILE* tablefile){
     // FEHLER! KONSTRUKTOR-FUNKTIONSRÜMPFE MÜSSEN IN DER KLASSENDEFINITION STEHEN!
    }
    

    klappt also nicht. Das ist Teil 1 des Problems. Nun kann man versuchen, innerhalb der Klassendefinition den Funktionsrumpf des Konstruktors zu notieren - und schon tritt Teil 2 des Problems in kraft: Enthält eine Konstruktordefinition Initialisierunslisten, MUSS der Funktionsrumpf des Konstruktors leer bleiben (für Kenner der Feinheiten von Standard-C++ soweit nichts Neues).

    Das Problem lässt sich auch kurz formulieren:
    - Eine Klasse enthält non-static konstante Elemente (fieldsize)
    - Außerdem gibt es auch variable non-static Elemente (lastrecord)
    - beide Elemente soll der selbe Konstruktor initialisieren, wobei die Initiali-
    sierung des variablen Elements so komplex ist, dass sie nur im Konstruktor-
    rumpf stattfinden kann

    Lösung:
    OHNE Ableitungsmechanismus gibt es innerhalb der Standard-C++ Syntaxregeln
    GAR keine Lösung für das Problem. Die einzige Lösung besteht hierin:

    class FwzBaseClass{
     protected:
     const USHORT fieldsize;
     public:
     FwzBaseClass():fieldsize(10){} // Jetzt ist der Konstruktor nicht rechtswidrig
    };
    
    class TableDescriptor: public FwzBaseClass{
     protected:
     ULONG lastrecord;
     // Unmengen an weiteren Daten (ULONG* RecordTable z.B.)
    
     public:
     TableDescriptor(HFILE* tablefile); // und diesen Konstruktor kann ich
                                      // jetzt problemlos irgendwo anders
                                      // definieren, und kann dann für die
                                      // Berechnungen sogar schon auf den
                                      // Wert von fieldsize == 10 zurückgreifen
                                     // weil der Basisklassenkonstruktor zuerst
                                      // kommt
    
     // ... Unmengen an weiteren Funktionen und Funktiönchen (das Zergliedern
     // des Monsterprojekts hat mir hier wirklich geholfen, THX @Shade,
     // THX @simonphoenix THX @otze THX @mastah
    };
    

    Jedenfalls scheint es die einzige Möglichkeit in einer Klassenhierarchie zu sein, Konstante Members mit Werten zu versehen, dass man die Konstanten wie in einem Container in der Basisklasse zusammenfasst, wo sie dann über die Initialisierungsliste des Basisklassenkonstruktors mit Werten versehen werden können.

    // Meiner Meinung nach sollte es einem Konstruktor auch erlaubt sein, die
    // Werte von Member-Konstanten innerhalb seines Funktionsrumpfes zu mani-
    // pulieren, denn der oben skizzierte 'Ableitungszwang' ist unbefriedigend
    // und umständlich



  • Mecnels mal was anderes, warum benutzt du immer HFILE? Wenn du mit CreateFile arbeitest dann nimmst man HANDLE.



  • anti-HFILE schrieb:

    Mecnels mal was anderes, warum benutzt du immer HFILE? Wenn du mit CreateFile arbeitest dann nimmst man HANDLE.

    THX für den TIPP. Da hätte ich mir oft das explizite casten zu (HANDLE) ersparen können, wär' mir das gleich eingefallen. Jedenfalls ist die Library FileWizard fast fertig (hab seit Sonntag 38 (!!) Stunden wie ein Freak Code reingehackt). Das System operiert ähnlich wie ein Betriebssystem: Jedes Feld ist 10 Byte groß, jede Datei kriegt eine 'Maske' in der Signatur verpasst, die festlegt, ob die 10 Byte als Daten oder als Referenz zu interpretieren sind. Bei kleinen Datenmengen (10.000 Datensätze mit der Maske iiisssis - also int_int_int_string_string_string_string_int_string - wobei string immer eine Referenz ist:)) funzt das Teil super.
    Trotzdem Danke für den Extra-Tipp mit dem HANDLE. 😃



  • Mecnels schrieb:

    klappt also nicht. Das ist Teil 1 des Problems. Nun kann man versuchen, innerhalb der Klassendefinition den Funktionsrumpf des Konstruktors zu notieren - und schon tritt Teil 2 des Problems in kraft: Enthält eine Konstruktordefinition Initialisierunslisten, MUSS der Funktionsrumpf des Konstruktors leer bleiben (für Kenner der Feinheiten von Standard-C++ soweit nichts Neues).

    uh, das wär aber unpraktisch.

    struct Bar
    {
      int &i_;
      Bar (int &i)  : i_(i) { i = 23; }
    };
    

    warum sollte das verboten sein?



  • davie schrieb:

    Mecnels schrieb:

    klappt also nicht. Das ist Teil 1 des Problems. Nun kann man versuchen, innerhalb der Klassendefinition den Funktionsrumpf des Konstruktors zu notieren - und schon tritt Teil 2 des Problems in kraft: Enthält eine Konstruktordefinition Initialisierunslisten, MUSS der Funktionsrumpf des Konstruktors leer bleiben (für Kenner der Feinheiten von Standard-C++ soweit nichts Neues).

    uh, das wär aber unpraktisch.

    struct Bar
    {
      int &i_;
      Bar (int &i)  : i_(i) { i = 23; }
    };
    

    warum sollte das verboten sein?

    Sry mein Fehler, hab den Konstruktorrumpf außerhalb der Klassendefinition geschrieben. Sry. Innerhalb der Klassendefinition ist das ok.
    Nur - ab einer gewissen Länge des Funktionsrumpfs einer Memberfunktion bild ich mir ein, die Funktionsdefinition außerhalb der Klassendefinition unterbringen zu müssen (keine Ahnung warum, schlechte Angewohnheit wahrscheinlich.)
    Trotzdem hast Du recht, rein syntaktisch klappt es, wenn der Funktionsrumpf des Konstruktors innerhalb der Klassendefinition steht.


  • Mod

    was für ein problem du mit den initialisierungslisten + leere funktionsrümpfe du hast ist, ist mir schleierhaft.
    initialisierungslisten sind teil der Definition des Konstructors, daher der zwang, gleich anschliessend die { } Klammern zu bringen, aber das muss keineswegs bei der klassendefinition passieren. es funktioniert z.b. das hier
    test.h

    #ifndef TEST_H
    #define TEST_H
    struct foo
    {
    	const int a;
    	int b;
    	int c;
    	foo(int x);
    };
    #endif
    

    test.cpp

    #include "test.h"
    foo::foo(int x) : b( a ), a( x ) { c = a; }
    

    main.cpp

    #include <iostream>
    #include "test.h"
    int main()
    {
    	foo bar( 1 );
    	std::cout << bar.a << '\t' << bar.b << '\t' << bar.c << std::endl;
    	return EXIT_SUCCESS;
    }
    

    das funktioniert sogar mit einem compiler eines softwareherstellers aus redmond.


Anmelden zum Antworten