array vererben?



  • Hi, ich habe die ganze Nacht an folgender Problematik gesessen:

    Hier eine vereinfachte Darstellung:

    # define NUM_OBJ 10
    
    class A {
        int i
    }
    
    class B : public A {
        int j;
    }
    
    function main() {
    
        B objects[NUM_OBJ];
    
        //objects mit Werten gefüllt vorstellen
    
        test(objects);
    
    }
    
    function test(A* objects) {
    
        for(int i=0; i<NUM_OBJ; i++)
    
            objects[i].i;
    
    }
    

    Das Problem ist, dass objects[i]->i; meistens Falsche Werte hat.

    (In Wirklichkeit arbeite ich mit komplexeren Datentypen als int, daher könnte das Beispiel möglicherweise sogar korrekt sein)

    Was mache ich falsch?

    Ps: verzeiht bitte die Threadbezeichnung, ich weiß nicht, wie ich das Problem benennen soll.



  • Du darfst ein Array von B -Objekten nicht als Array von A -Objekten interpretieren. Das tust du aber, indem du einen Zeiger auf B bei der Übergabe an test() implizit in einen Zeiger auf A umwandelst.



  • Ja, das entspricht meinem Vorhaben 🙂

    Ich nehme also an, dass das unmöglich ist?

    Der Compiler erlaubt es zumindest, aber die Ergbnisse sind nicht korrekt.

    Gibt es eine Möglichkeit, das zu lösen?

    Ich möchte eine Funktion erstellen, die Arrays von A und B Objekten durchlaufen kann. Sie muss auch nur die A-spezifischen Eigenschaften kennen.



  • Hi!

    Ich glaube, die Lösung gefunden zu haben!

    # define NUM_OBJ 10
    
    class A {
        int i
    }
    
    class B : public A {
        int j;
    }
    
    function main() {
    
        B objects[NUM_OBJ];
    
        //objects mit Werten gefüllt vorstellen
    
        test(objects, [b]sizeof(B)[/b]);
    
    }
    
    function test(A* objects, int size) {
    
        int offset = size / [b]sizeof(A*)[/b];
        int j=0;
    
        for(int i=0; i<NUM_OBJ; i++) {
    
            objects[j].i;
            [b]j += offset;[/b]
    
        }
    
    }
    

    So funktioniert es auf jeden Fall sehr gut, sogar wenn ich die Klassen verändere.

    Wenn ihr mir sagen könntet, ob mein obiger Versuch zulässig und vor allem sinvoll ist, wäre ich euch sehr verbunden.



  • Antwort an deinen letzten Post angepasst.

    So, wie du es am Anfang gelöst hast, funktioniert es nicht, weil die Grösse von A und B unterschiedlich sind und du sozusagen "zwischen die Objekte" zugreifst, wenn du per Index zugreifst. Der Offset berechnet sich aus der Grösse von A , in der Tat sind die Elemente aber vom Typ B .

    Das mit dem Offset ist eine Möglichkeit, oder du verwendest Zeiger zur Speicherung. Ich hoffe nur, das bei dem Offset ist eine Subtraktion und keine Division... 😉


  • Administrator

    1. Ich hoffe, dass du keine defines als Konstanten verwendest.
    2. function vor der Funktion? Javascript? 🙂
    3. Zum eigentlichen Problem:

    #include <algorithm>
    
    // ...
    
    void test(A* object);
    
    // ...
    
    B objects[NUM_OBJ];
    
    std::for_each(objects, objects + NUM_OBJ, &test);
    
    // ...
    
    void test(A* object)
    {
      // mach was mit diesem einzelnen Objekt.
    }
    

    Gäbe auch noch andere Möglichkeiten, aber diese erschien mir am nächsten zu dem, was du machen möchtest.

    Zu deinem Versuch (in der Vorschau gesehen):
    Das ist simpel und einfach undefiniertes Verhalten. Würde ich auf jedenfalls vermeiden, da gibt es viele bessere Möglichkeiten. 😉

    Grüssli



  • 1. Ich hoffe, dass du keine defines als Konstanten verwendest.

    Nein, das war einfach (extremer) pseudo code.

    (edit: obwohl, manchmal schon... habe ich so aus dem buch Spieleprogrammierung mit directx und c++... nicht gut?)

    Wie ich auf das "function" kam, weiß ich auch nicht. Wahrscheinlich zu viel PHP oder Javascript programmiert 🙂

    Dein code sieht auf jeden Fall gut aus, werde es gleich mal so probieren.

    @Nexus

    Meine Berechnung des Offsets war mehr oder weniger trail und error.

    Ich habe gesehen, dass Objekt B die Größe 28 hat und bei jedem 7. Index den richtigen Wert angab. Daher habe ich geschlussfolgert size = 28 / 4;

    Wie wäre der Offset denn richtig zu berechnen? Was wäre zu subtrahieren?



  • einfach noch einen zeiger verwenden

    class A 
    {
        int i;
    };
    
    class B : public A
    {
        int j;
    };
    
    int main()
    {
        const unsigned size = 100;
    
        B objects[size];
    
        A *pobjects[size];
    
        for (unsigned i = 0; i < size; ++i)
            pobjects[i] = &objects[i];
    
        test(pobjects, size);
    
    }
    
    void test(A* objects, unsigned num)
    {
        for (unsigned i=0; i < num; ++i)
            objects[j]->i = i;
    }
    


  • pewpy schrieb:

    # define NUM_OBJ 10
    
    class A {
        int i
    }
    
    class B : public A {
        int j;
    }
    
    function main() {
        B objects[NUM_OBJ];
        //objects mit Werten gefüllt vorstellen
        test(objects);
    }
    
    function test(A* objects) {
        for(int i=0; i<NUM_OBJ; i++)
            objects[i].i;
    }
    

    Das kompiliert ja gar nicht (Funktionen definiert man anders, Semikolons fehlen).

    Du solltest statt mit #define die Konstante so definieren:

    const int num_of_objects = 10;
    

    Denn Makros sind "böse".

    Das Problem bei Dir ist, dass Du Vererbung mit Zeigerarithmetik vermischt. Das ist, wie Du festgestellt hast, fatal. Anderes Beispiel:

    void foo() {
        B barr[2];
        A* p0 = &barr[0];
        A* p1 = &barr[1];
        assert( p0+1==p1 ); // ...ist aber falsch
      }
    

    In "barr" stehen die 2 Objekte direkt hintereinander. Ein B-Objekt ist aber größer als ein A-Objekt. p0+1 zeigt deswegen nicht auf das A-Subobjekt von barr[1] .

    Versuch einfach, es irgendwie anders zu machen, also ein anderes Design zu wählen.

    Wenn Du bei dem Design unbedingt bleiben willst, kannst Du Dir eine kleine Headerdatei anlegen, sie mit etwas "schwarzer Magie" füllen und dann so benutzen:

    //------------------------------------------
      // Schwarze Bibliotheks-Magie (Header file)
      //------------------------------------------
    
      #include <cstddef>
      #include <stdexcept>
    
      template<typename T>
      class range_ref {
      protected:
        ~range_ref() {}
      public:
        virtual std::size_t size() const = 0;
        virtual T& operator[](std::ptrdiff_t index) const = 0;
      };
    
      template<typename As, typename From>
      class range_ref_array_impl : public range_ref<As> {
        From* base_;
        std::size_t size_;
      public:
        range_ref_array_impl(From* ptr, std::size_t size)
          : base_(ptr), size_(size) {}
    
        template<std::size_t Size>
        range_ref_array_impl(From (&arr)[Size])
          : base_(arr), size_(Size) {}
    
        std::size_t size() const {return size_;}
    
        As& operator[](std::ptrdiff_t index) const {
          if (index<0 || index>=size_)
            throw std::out_of_range("range_ref_array_impl<>: "
                                    "out of range error");
          return base_[index];
        }
      };
    
      template<typename As, typename From>
      inline range_ref_array_impl<As,From> make_array_ref(
          From* ptr, int size)
      {
        return range_ref_array_impl<As,From>(ptr,size);
      }
    
      template<typename As, typename From, std::size_t Size>
      inline range_ref_array_impl<As,From> make_array_ref(
          From (&arr)[Size])
      {
        return range_ref_array_impl<As,From>(arr);
      }
    
      //----------------------
      // Anwendung (cpp file)
      //----------------------
    
      #include <iostream>
    
      struct A {int i;};
      struct B : A {int j;};
    
      void double_Ai(range_ref<A> const& array)
      {
        for (int k=0; k<array.size(); ++k) {
          array[k].i *= 2;
        }
      }
    
      int main() {
        const int count = 3;
        B array[count];
        for (int k=0; k<count; ++k)
          array[k].i = k;
    
        double_Ai(make_array_ref<A>(array));
    
        for (int k=0; k<count; ++k)
          std::cout << array[k].i << '\n';
      }
    

    Die Schnittstelle range_ref<T> ist so allgemein, dass Du im Prinzip auch der "double_Ai" Funktion irgendwelche STL Kontainer unterjubeln kannst -- Hauptsache "random access".

    Gruß,
    SP



  • Bietet es sich hier nicht an, die Funktion test einfach als template zu schreiben:

    template<class T>
    void test(T* objects) {

    for(int i=0; i<NUM_OBJ; i++)
    objects[i].i;
    }

    ?



  • Belli schrieb:

    Bietet es sich hier nicht an, die Funktion test einfach als template zu schreiben:

    template<class T>
    void test(T* objects) {

    for(int i=0; i<NUM_OBJ; i++)
    objects[i].i;
    }

    ?

    Gibt bei mir den Fehler 'i': Ist kein Element von 'B' aus.

    Die Umsetzung von tippgeber klappt sehr gut, ich versuche nur gerade, die erstellung des zweiten zeigers irgendwie zu vereinfachen. Leider geht es nicht, wenn ich den Zeiger erst in der "test" funktion erstelle.


Anmelden zum Antworten