Überladen einer Methode



  • Hallo, ich hab diese Methode

    bool TFunktion::addElement(TElement Element)
    {
       // ...
    }
    

    TElement ist ein struct der so aussieht:

    struct TElement  // steht für 1 Element in der Gleichung
                     // Faktor * hatX ^ Exponent
    {
       float Faktor;
       float Exponent;
    
       bool hatX; // wenn die Stelle ein 'x' ist, wird die Var. auf true
                  // gesetzt
    };
    

    So, wenn ich jetzt meine Funktion addElement so überladen will, dass ich einmal ein Element mit
    Funktion->addElement( neuesElement ); und mit
    Funktion->addElement( Faktor, Exponent, hatX); hinzufügen kann, schreibt man das am besten?

    Ich habs jetzt so:

    bool TFunktion::addElement(float Faktor, float Exponent, bool hatX)
    {
    
       TElement Element = {Faktor, Exponent, hatX};
    
       return this->addElement( Element );
    }
    bool TFunktion::addElement(TElement Element)
    {
       // ...
    }
    

    In der obersten Funktion kann man doch bestimmt irgendwie die erste Zeile sparen oder?

    Wie schreibt man das am besten?



  • Zum einen solltest Du eine konstante Referenz auf das Element übergeben. Da Du vermutlich bis sicher eine Kopie des übergebenen Parameters in Deine Datenstruktur einfügen wirst (eine Referenz oder ein Zeiger auf ein Argument geht ja schlecht), spart das eine Kopie, nämlich die auf den Stack von addElement.

    bool TFunktion::addElement(const TElement& Element)
    

    Zum zweiten solltest Du der Struktur TElement einen Konstruktor spendieren, der drei Parameter entgegennimmt. Das ermöglicht Dir dann folgendes:

    bool TFunktion::addElement(float Faktor, float Exponent, bool hatX)
    {
       return addElement( TElement( Faktor, Exponent, hatX ) );
    }
    
    struct TElement
    {
       /* ... */
       TElement( float Faktor_, float Exponent_, bool hatX_ ):
          Faktor( Faktor_ ), Exponent( Exponent_ ), hatX( hatX_ ) {}
    };
    


  • Wunderbar 😉 Danke für die schnelle antwort.

    wie sieht denn dann der Konstruktor für die struktur aus:

    struct TGleichung
    {
       TElement Element[N_ELEMENTE];
       float x;
       float a;
    };
    

    Bei der ein Feld initialisiert werden soll (der compilier motzt nämlich gerade das diese Struktur keinen hat (was ja auch so ist).

    Vielleicht könntest du auch kurz auf die syntax eingehen. Ich versteh nämlich nicht ganz was der ":" dahinter soll. Oder warte, hier meine Erklärung:

    TElement( float Faktor_, float Exponent_, bool hatX_ )
    

    Prototyp für den Konstruktor. Die 3 Parameter, geben mir dann die möglichkeit
    für die Übergabe(oben).

    : Faktor( Faktor_ ), Exponent( Exponent_ ), hatX( hatX_ ){}
    

    Der ":" steht doch jetzt dafür, dass direkt hinter dem Prototyp die Methode stehen soll oder? und die {} sind halt da, weil kein code vorhanden ist.

    Ich verstehe allerdings jetzt nicht wie sich das

    Faktor( Faktor_ ), Exponent( Exponent_ ), hatX( hatX_ )
    

    in dem Teil des Codes verhält. Hab noch nie hinter eine Variable zwei klammern
    geschrieben.

    Was ist das, was machst, und wo braucht man das?

    Dann noch ne Frage zu derm "_" hinter den benennungen. Ist das ein Standard von C++ Programmieren, sowie das "T" beim anfang von strukturen, für was steht das und wie heißen die Variablen dann (wenns einen sinn hat)?

    Danke 😉

    Edit:
    Ich hab gerade in der FAQ das hier gelesen:

    // new statt malloc und Kohorten
    Typ* tName= new Typ[Anzahl]; // C++
    // und nicht:
    // Typ* tName = (Typ*)malloc(Anzahl * sizeof(Typ)); // C
    

    Hab ich da mit der Feld initialisierung was falsch gemacht (ich meine jetzt bei dem Array in meinem struct)?

    Also so neben bei steht im Konstruktor meine Klasse auch nur das drinn:

    TFunktion::TFunktion()
    {
    
       /* Speicherplatz reservieren */
       new TFunktion();
    
       /* Säubert die eigene Gleichung */
       this->clearGleichung(Gleichung);
    }
    


  • Aus Zeitmangel weil die Nacht gleich anbricht gehe ich erstmal nur auf den zweiten Teil ein:

    Der Doppelpunkt hinter der Konstruktordeklaration leitet die Initialisierungsliste ein. Dort kannst Du die Membervariablen einer Klasse (oder Struktur) mit Werten konstruieren. Das ist übrigens der bevorzugte Weg, und auch der einzige, wenn ein Member keinen trivialen Konstruktor hat. Bei trivialen Typen ist das dasselbe als würdest Du im Körper des Konstruktors die Werte zuweisen:

    struct TElement
    {
       /* ... */
       TElement( float Faktor_, float Exponent_, bool hatX_ )
       {
          Faktor = Faktor_;
          Exponent = Exponent_;
          hatX = hatX_;
       }
    };
    

    Allerdings ist die Initialisierungsliste immer vorzuziehen, da dort ein Wert direkt konstruiert wird. Im Körper wird ein bereits konstruierter Wert mit einem neuen überschrieben.

    Der Unterstrich hinter den Namen dient einzig und allein dazu, um die der Klasse (Struktur) zugehörigen Namen von den Parametern zu unterscheiden. Mit Initialisierungsliste wären die Unterstriche nicht einmal nötig, aber ich schreibe sie um Verwirrung zu vermeiden üblicherweise trotzdem hin. In C++ kannst Du übrigens jeden Typ mit Klammern initialisieren, statt mit Zuweisung.

    /* Irgendwo im Code, folgendes verhält sich identisch */
    int x( 3 );
    int x = 3;
    

    Klarer wird das, wenn Du Klassen verwendest, die keinen Konstruktor mit null Parametern bereitstellen.

    Und das T vor Klassennamen ist keinesfalls C++ Standard, sondern imho sogar eher verpönt. Ein durchdachtes Typsystem mit Namespaces braucht keine Ts und Cs um Typen und Klassen zu kennzeichnen. Dass der Unterstrich hinter den Namen gesetzt wird hat hier nur den Grund, dass man sich mit _ oder __ vor dem Namen Probleme mit dem Compiler einhandeln kann, denn solche Bezeichner sind u.U. für die Implementation (den Compiler-/Bibliothekshersteller) reserviert.

    Kurzes Beispiel zur Initialisierungsliste bei non-trivialen Konstruktoren:

    struct A
    {
       A( int x ); // was diese Klasse tut, ist erstmal egal
       // wichtig ist, das folgendes nicht funktioniert, weil der Konstruktor einen Parameter erwartet:
       // A a;
    };
    
    struct X
    {
       A member_a;
       X( void ) { member_a = A( 12 ); } // funktioniert nicht
       X( void ): member_a( 12 ) {} // ist i.O., denn member_a wird direkt konstruiert
    };
    


  • TFunktion::TFunktion()
    {
    
       /* Speicherplatz reservieren */
       new TFunktion();
    
       /* Säubert die eigene Gleichung */
       this->clearGleichung(Gleichung);
    }
    

    Das solltest Du gleich mal wieder löschen, denn das new TFunktion allokiert Speicher für ein TFunktion, weist diesen Speicher aber keiner Variablen zu ➡ das ist toter Speicher, den Du nie wieder erreichen kannst.



  • LordJaxom schrieb:

    TFunktion::TFunktion()
    {
    
       /* Speicherplatz reservieren */
       new TFunktion();
    
       /* Säubert die eigene Gleichung */
       this->clearGleichung(Gleichung);
    }
    

    Das solltest Du gleich mal wieder löschen, denn das new TFunktion allokiert Speicher für ein TFunktion, weist diesen Speicher aber keiner Variablen zu ➡ das ist toter Speicher, den Du nie wieder erreichen kannst.

    Vor allem im Standardkonstruktor (der dann rekursiv aufgerufen wird) eine prima Sache !! 🤡

    Gruß,

    Simon2.



  • Ok, ich hab mir das gestern abend alles mal durchgelesen und muss sagen, dass eure sprache ganze schön schwer zu verstehen ist. Vorallem verstehe ich das mit den trivialen und non-trivialen Membern nicht. Aber das ihr mir das jeztt erklärt, ist zuviel verlangt. Ich guck mal was sich über google finden läßt. Vielleich könntet ihr mir eure lieblings c++ seite nennen, wo solche infos zufinden sind.

    Aber guckt mal hier, ist das Sinnvoll:

    struct TElement
    {
       /* ... */
       TElement( float Faktor_, float Exponent_, bool hatX_ ):
          Faktor( Faktor_ ), Exponent( Exponent_ ), hatX( hatX_ ) {}
    };
    

    Da ich außerhalbt der Klasse öfter mal TElement als eigene Struktur benötige, kann ich doch auch den konstruktor überladen oder?

    struct TElement
    {
       /* ... */
       TElement() { TElement( 0.0, 0.0, false };
       TElement( float Faktor_, float Exponent_, bool hatX_ ):
          Faktor( Faktor_ ), Exponent( Exponent_ ), hatX( hatX_ ) {}
    };
    

    Das das ermöglicht mir doch jetzt dies zu benutzen:

    int main()
    {
     /* ... */
       TElement Element; // z.B. zum zwischenspeichern
    
       TElement ElementenReihe[MAX_ELEMENTS]; // z.B. als einfaches array
     /* ... */
    }
    

    Ist das jetzt irgendwie unsauber? Wofür brauche ich da noch ein new?



  • d0x schrieb:

    struct TElement
    {
       /* ... */
       TElement() { TElement( 0.0, 0.0, false };
       TElement( float Faktor_, float Exponent_, bool hatX_ ):
          Faktor( Faktor_ ), Exponent( Exponent_ ), hatX( hatX_ ) {}
    };
    

    Nicht ganz. Du kannst von einem Konstruktor aus nicht den anderen aufrufen. Du kannst es aber so machen:

    struct TElement
    {
       /* ... */
       TElement():
          Faktor( 0.0 ), Exponent( 0.0 ), hatX( false ) {}
    
       TElement( float Faktor_, float Exponent_, bool hatX_ ):
          Faktor( Faktor_ ), Exponent( Exponent_ ), hatX( hatX_ ) {}
    };
    

    Ist das jetzt irgendwie unsauber? Wofür brauche ich da noch ein new?

    Unsauber ist das nicht. Überladung macht es ja gerade möglich, genau sowas zu machen, wieso sollte das dann als unsauber gelten? 😉

    Das new brauchst Du nur, wenn Du TElemente auf dem Heap anlegen willst. Hier in dem Beispiel kommst Du gut ohne aus.


Anmelden zum Antworten