Verständnissprobleme bei "super".



  • Liebe Community

    Ich habe ein kleines Verständnisproblem.

    Ich verstehe nicht, wofür der Begriff super im folgenden Code gut sein soll.

    // cppbuch/k9/mathvek/mvektor.t
    #ifndef MVEKTOR_T
    #define MVEKTOR_T
    #include<iostream>
    #include"../vektor/vektor. t"
    
    template<typename T>
    class MathVektor : public Vektor<T> {
    public:
    using super = Vektor<T>; // Abkürzung für Oberklassentyp
    using Vektor<T>::Vektor; // Konstruktoren erben
    void init(T); // alle Elemente setzen
    // Operatoren
    MathVektor& operator=(T); // Operator *=
    // weitere Operatoren und Funktionen ...
    }; // #endif folgt am Dateiende (nach der Implementierung)
    
    template<typename T>
    void MathVektor<T>::init(T wert) { // alle Elemente setzen
    for(size_t i = 0; i < super::size(); ++i) {
    super::operator[](i) = wert;
    }
    }
    

    Hier ist die Vectorklasse, von der geerbt wird.

    template <typename T> 
    class Vektor {
    public:
      Vektor(size_t anzahl = 1); // Allg. Konstruktor
      Vektor(size_t n, T t); // 2. Allg. Konstruktor: n Elemente mit Wert t
      Vektor(std::initializer_list<T>); // Sequenzkonstruktor
      Vektor(const Vektor<T> &v);       // Kopierkonstruktor
    
      virtual ~Vektor() { // Destruktor
        delete[] start;
      }
    
      size_t size() const { return anzahl; }
    
      void groesseAendern(size_t); // dynamisch ändern
    
      T &operator[](int index) { // Indexoperator inline
        if (index < 0 || index >= static_cast<int>(anzahl)) {
          throw std::out_of_range("Indexüberschreitung!");
        }
        return start[index];
      }
    
      // Indexoperator für nicht-veränderliche Vektoren
      const T &operator[](int index) const {
        if (index < 0 || index >= static_cast<int>(anzahl)) {
          throw std::out_of_range("Indexüberschreitung!");
        }
        return start[index];
      }
    
      // Zuweisungsoperator
      // Vektor<T>& operator=(const Vektor<T>&);
      Vektor<T> &operator=(Vektor<T>); // Übergabe per Wert f. swap
      // Vektoren vertauschen
      void swap(Vektor<T> &v);
    
      // Zeiger auf Anfang und Position nach dem Ende für Vektoren
      // mit nichtkonstanten und konstanten Elementen
      T *begin() { return start; }
      T *end() { return start + anzahl; }
      const T *begin() const { return start; }
      const T *end() const { return start + anzahl; }
    
    private:
      size_t anzahl; // Anzahl der Datenobjekte
      T *start;      // Zeiger auf Datenobjekte
    };
    

    Das Beispiel ist aus meinem C++ Buch. Dort wird folgendes beschrieben.

    "Die Konstruktoren brauchen nicht implementiert zu werden, weil sie von der Oberklasse
    geerbt werden. In der folgenden Funktion wird mit super:: angegeben, von welchem
    Typ size() ist. Die Qualifizierung mit super:: kann nicht wegfallen, weil size() keinen
    Parameter hat, aus dem der Typ abgeleitet werden könnte. Weil die Klasse sinnvoll nur
    mit float- oder double-Zahlen verwendbar ist, werden Parameter dieses Typs jeweils per
    Wert übergeben."(Der C++ Programmierer, Ulrich Breymann)

    Nur zur Information.
    Die Vetorklasse wurde aus Übungszwecken erstellt. Normalerweise benutzt man ja
    die std::vector Klasse.

    Für eine kurze Erklärung wäre ich wirklich sehr dankbar.

    Liebe Grüße
    Sebastian Müller



  • super wird als Alias für Vektor<T> definiert. Wo super steht, steht also eigentlich Vektor<T>. Der Compiler kann hier nicht bestimmen, zu welchem T das size gehören soll, da T beim Aufruf der Funktion selber nicht verwendet wird. Mit Vektor<T> ist das klar. Stattdessen hätte man aber auch einfach this-> nehmen können.



  • super ist nur eine Abkürzung, die auf die Oberklasse (super class) hinweist.
    Statt super::size() kann man genau so gut Vektor<T>::size() schreiben.
    using führt nur einen Alias-Namen ein.



  • Achso.

    Das muss man aber nur machen, weil die Funktion außerhalb der Klasse definiert ist, und damit eine eigene Template hat.


  • Mod

    Zuerst einmal ist das super nur eine Abkürzung für Vektor<T>, die in Zeile 10 bekannt gemacht wird. Wenn du nicht super:: schreiben würdest, müsstest du eben Vektor<T>:: schreiben. Aber wieso muss das size überhaupt qualifiziert werden, wenn doch MathVektor von Vektor<T> erbt? Das liegt da dran, dass Vektor<T> von dem Templateparameter T abhängt. Da bei Templates so ziemlich alles mögliche passieren könnte (Templatespezialisierung!) ist in Zeile 20, wo das size benutzt wird, überhaupt nicht klar, wo das size her kommen soll. Daher muss man explizit sagen, dass dieses in Vektor<T> gesucht werden soll.

    PS: Hier wäre meiner Meinung nach mal ein gutes Beispiel, wo private-Vererbung angebracht wäre.



  • Aber wenn ich ein Objekt vom Typ MathVector<int> erstelle wir doch vom Compiler automatisch eine passende Klasse erzeugt. Das Subobjekt Vector wird dann ebenfals mit int erstellt. Warum weiß der Compiler nicht, zu welchem T das Size gehört.
    Tut mir wirklich leid, aber ich blicke leider immer noch nicht so gut durch.

    Das super nur ein Alias ist, ist mir klar.


  • Mod

    Sebastian Müller schrieb:

    Aber wenn ich ein Objekt vom Typ MathVector<int> erstelle wir doch vom Compiler automatisch eine passende Klasse erzeugt. Das Subobjekt Vector wird dann ebenfals mit int erstellt. Warum weiß der Compiler nicht, zu welchem T das Size gehört.

    Zwei Gründe:
    1. Wird der Code bereits einmal verarbeitet, bevor das Template instanziert wird. Damit der Compiler an der Stelle beurteilen kann, ob der Code überhaupt Sinn macht, muss er wissen, was was sein soll. Er weiß zwar noch nicht, ob die Oberklasse eine Funktion namens size hat, aber er weiß zumindest, dass diese Stelle ein Funktionsaufruf an eine Memberfunktion sein soll und kann entsprechend die Syntax prüfen.
    2. Bei der Instanzierung muss auch sicher gestellt werden, dass wirklich der Code erzeugt wird, den der Templateprogrammierer wollte. Hier wollte er, dass eine size-Methode der Oberklasse genommen wird. Aber was, wenn diese gar keine size-Methode hätte? Wenn da nicht explizit stünde, dass das size der Oberklasse genommen werden soll, würde der Compiler danach als nächstes nach einer size-Funktion im globalen Namensraum suchen. Falls er eine findet: Wer weiß, was die macht? Sicherlich nicht das, was der Programmierer sich hier gedacht hat.



  • OK. Danke.
    Jetzt habe ich es verstanden.

    Vielen Dank für eure Hilfe.


Anmelden zum Antworten