Objekte in deque packen - ich steh auf dem Schlauch....



  • Hallo,

    was mir in C# locker von der Hand geht, funktioniert in C++ nicht. Da renne ich immer gegen die Wand. Ich habe deque nun schon erfolgreich in einer dll eingesetzt - als container für double.

    Nun habe ich eine Klasse geschrieben, die bislang auch immer anstandslos kompiliert wurde. Die Instanzen dieser Klasse sollen nun in Deque-Container gespeichert werden. Als erstes mus deque instanziert werden.
    Für double-Variablen sieht das so aus:

    deque<double> Puffer(maxDeq);
    

    Also dachte eich, das sieht für Objekte analog aus:

    deque<meineKlasse> ContainerName(maxDeq);
    

    Ab hier hagelt es dann Fehlermeldungen:

    [Error] no matching function for call to 'meineKlasse::meineKlasse()'
    [Error] candidates are:
    meineKlasse::meineKlasse(double, double, double,double, unsigned int)
      candidate expects 5 arguments, 0 provided
    meineKlasse::meineKlasse(const meineKlasse&)
      candidate expects 1 argument, 0 provided
    ......
    

    Richtig ist: Mein Konstruktor benötigt 5 Argumente. Aber ich wollte lediglich Deque einichten und nicht gleich eine Instanz von meineKlasse erzeugen. Diese werden später dierekt mit Daten erzeugt und via pop in den Container gepackt (oder kopiert). Was mache ich falsch?

    Am Rande habe ich auch einen Fehler, der mit MessagBoxen zusammenhängt: Damit MessageBoxen angezeigt werde, wurde hier in einem anderen Thread empfohlen, einfach vor dem ersten Aufruf von MessageBox ein LoadLibrary("Shell32.dll"); einzufügen. Das funktionierte auch wunderbar. Nur in meinem neuen Projekt nicht mehr. Da heißt es dann:

    [Error] expected constructor, destructor or type conversion before '(' token...
    

    Hilfe
    Jens 😕


  • Mod

    deque<meineKlasse> ContainerName(maxDeq);
    

    Das richtet aber keine leere deque ein, sondern eine deque mit maxDeg Elementen. Irgendwie müssen diese Elemente erzeugt werden. Da das nicht mit dem Standardkonstruktor geht (du hast schließlich keinen) kommt der Fehler. Guck dir nochmal die verschiedenen Konstruktoren der Container an und was sie genau machen.

    deque ist übrigens eine recht ungewöhnliche Containerwahl. Bist du sicher?



  • Hallo SeppJ,

    es soll eine deque (wegen push_front, pop_back und der Nutzung von Iteratoren) mit maxDeq Elementen angelegt werden. Soweit ist das richtig. Und da die Klasse ja vollständig geschrieben ist, sollte eigentlich auch die notwendige Größe bekannt sein.
    Soweit ich verstehe, wird eine Kopie des Objektes in deque gespeichert und kein Zeiger oder eine Referenz.

    class meineKlasse
    {
        public:
            meineKlasse(double a, doeuble b, double c, double d, unsigned int e):
                m_a(a),
                m_b(b),
                m_c(c),
                m_d(d),
                m_e(e)
              {}
    
            double A() const {return m_a;}
            double B() const {return m_b;}
            double C() const {return m_c;}
            double D() const {return m_d;}
            unsigned int E() const {return m_e;}
    
        private:
            double m_a;
            double m_b;
            double m_c;
            double m_d;
            unsigned int m_e;
    }
    
    ......
    int maxDeq = 100;
    deque<meineKlasse> ContainerName(maxDeq);
    .....
    

    So sieht meine Klasse aus. Wass muss ich machen, um deque einzurichten (Speicher reservieren), um später Objekte rein zu packen?

    Jens



  • (wegen push_front, pop_back und der Nutzung von Iteratoren)

    Die 3 Operationen bietet afaik auch jeder andere STL-Container.

    So sieht meine Klasse aus. Wass muss ich machen, um deque einzurichten (Speicher reservieren), um später Objekte rein zu packen?

    Eine std::deque<meineKlasse> der Länge 0 anlegen und später mit z.B. push_back Elemente anfügen.



  • @xenayoo
    STL Container regeln das mit ihrer Grösse dynamisch.
    Bei std::vector kann man mittels reserve() sicherstellen dass er genug Speicher für N Elemente hat, und daher bei bis zu N Elementen nicht umkopiert werden muss.
    Bei std::deque ist das nicht möglich.



  • da fehlt trotzdem dein StdKonstruktor

    schreibe in deine class noch dies rein

    MeineKlasse():
                m_a(0.0f),
                m_b(0.0f),
                m_c(0.0f),
                m_d(0.0f),
                m_e(0.0f)
    {
    
    }
    

  • Mod

    derStudi schrieb:

    da fehlt trotzdem dein StdKonstruktor

    Man muss keinen Standardkonstruktor haben und oft gibt es gute Gründe dagegen.



  • Hallo,

    vielen Dank für die Antworten.
    @Ich bin Matrix:
    Hier mal ein Vergleich (http://de.wikibooks.org/wiki/C++-Programmierung/_Die_STL/_Container)

    Demnach kommt vector nicht in frage, weil er kein push_front() kann, list beherrscht operator[] nicht. Zudem begegnete mir auf verschiedenen Seiten die Angabe, dass deque schneller sei als list.
    Für meine Anwendung kommt es auf die Reihenfolge der Daten an. Deshalb setze ich deque mit push_front(), pop_back() und Operator[] ein.

    Eine std::deque<meineKlasse> der Länge 0 anlegen und später mit z.B. push_back Elemente anfügen.

    Ich werde es mal ausprobieren. Jedoch ist auch die Größe wichtig - sonst macht push_front und pop_back keinen Sinn. Eventuell kann ich das später mit resize nachholen.

    @derStudi:
    Da ich die Klasse testhalber in einer eigenen .exe entwickelt habe und es keinerlei Beschwerden seitens des Kompilers gab, nehme ich mal an, dass das so in Ordnung ist. Die Verwendung eines solchen Standardkonstruktors erzeugt ein Objekt mit Fehlldaten. Die Klasse ist so beschaffen, dass sie Daten aufnimmt und diese später nicht geändert werden - lediglich wieder ausgelesen. Verwende ich einen solchen Leerkonstruktor, muss ich zusätzlichen Aufwand betreiben, um sicher zu stellen, dass mir diese Daten nicht meine Berechnungen verhageln....
    BTW: Das letze Element ist ein unsigned int und kein double.

    @all: ich probier's jetzt aus und melde mich.... Danke

    Jens 🙂


  • Mod

    xenayoo schrieb:

    Eine std::deque<meineKlasse> der Länge 0 anlegen und später mit z.B. push_back Elemente anfügen.

    Ich werde es mal ausprobieren. Jedoch ist auch die Größe wichtig - sonst macht push_front und pop_back keinen Sinn. Eventuell kann ich das später mit resize nachholen.

    Nein, kannst du nicht. Das ist genau das gleiche Problem. Wieso spielt hier die Größe eine Rolle? Was willst du überhaupt erreichen? Das klingt alles sehr wirr, du scheinst dir nicht so richtig im klaren zu sein, was ein Container speichert.



  • Hallo,

    mein Statusbericht:

    -gute Nachricht: Die ganze Fehlerliste, die auch Fehler in den icludierten dateien wie windows.h meldete, sind jetzt weg.

    -schlechte Nachricht: Es ist ein neuer Fehler da:

    [Error] request for member 'resize' in 'ContainerName', which is of non-class type 'std::deque<meineKlasse>()'
    

    Jens


  • Mod

    Da hast du wohl irgendwas wirres gemacht mit einem typedef. Wie denkst du, soll man dir ohne Code bei solch einer Frage helfen können? Bitte lies dir mal den ersten und den dritten Link in meiner Signatur durch. In diesem Thread wurde sehr viel aneinander vorbei geredet und die Helfer mussten sehr oft im Kaffeesatz lesen, was die eigentliche (nicht die unmittelbare) Problemstellung ist.



  • xenayoo schrieb:

    -schlechte Nachricht: Es ist ein neuer Fehler da:

    [Error] request for member 'resize' in 'ContainerName', which is of non-class type 'std::deque<meineKlasse>()'
    

    Noch schlechtere Nachricht: ohne deinen jetztigen Code kann man nur raten, was da passiert. Ich rate mal: du hattest ein Objekt "ContainerName" vom Typ deque, das du mit einem Konstruktorargument initialisiert hast:

    std::deque<meineKlasse> ContainerName(bla);
    

    Jetzt willst du den Default-Ctor aufrufen und hast deshalb einfach das Argument gelöscht, die Klammern aber gelassen:

    std::deque<meineKlasse> ContainerName();
    

    Was du nicht bemerkt hast ist, dass das jetzt keine Objektdefinition mehr ist, sondern die Deklaration einer Funktion namens ContainerName, die keine Argumente nimmt und eine deque zurückgibt. Wenn du jetzt ContainerName.resize aufrufst, denkt der Compiler, dass du resize auf der deklarierten Funktion aufrufst, was natürlich Blödsinn ist. Lösung: nimm die beiden Klammern weg, damit aus der Funktion wieder ein Objekt wird.



  • Sepp schrieb:

    Nein, kannst du nicht. Das ist genau das gleiche Problem. Wieso spielt hier die Größe eine Rolle? Was willst du überhaupt erreichen? Das klingt alles sehr wirr, du scheinst dir nicht so richtig im klaren zu sein, was ein Container speichert.

    Ich benötige einen sequentiellen Puffer, bei dem neue Daten oben rein kommen und alte daten unten rausfliegen. Dieser Puffer soll in seiner größe variabel sein - allerdings nur beim Aufruf. Wenn er eingerichtet ist, ändert sich seine Größe nicht mehr. Das Ganze soll aus einem C#-Programm genutzt werden.
    Hätte ich das Ganze in eine Klasse packen und diese einfach aus C# heraus direkt nutzen können, wäre dies kein Problem gewesen. Jedoch ist dies - aus welchem Grund auch immer (Absicht seitens MS, Kurzsichtigkeit der Programmierer oder tatsächlich technisch bedingt) nicht möglich. So kann ich entweder eine sogenannte Wrapper-dll erstellen oder den Wrapper gleich von vorne herein einbauen. Letzteres tue ich, indem aus den Memberfunktionen einfach extern"C"-Funktionen werden. Dann habe ich allerdings keinen Konstruktor, was eine der Ursachen für meine Probleme ist.

    Jens



  • xenayoo schrieb:

    Dieser Puffer soll in seiner größe variabel sein - allerdings nur beim Aufruf. Wenn er eingerichtet ist, ändert sich seine Größe nicht mehr.

    Die Größe muss sich ändern, sobald du etwas reinsteckst oder entnimmst. Meinst du vielleicht die maximale Größe?



  • xenayoo schrieb:

    Wenn er eingerichtet ist, ändert sich seine Größe nicht mehr.

    Du willst doch pop_... und push_... benutzen, wie Du schriebst? Selbstverständlich ändert sich dabei die Größe des Containers!


  • Mod

    Um die Antworten von MFK und Belli etwas zu erläutern:

    Wenn du ein Element aus deinem Puffer holst, die Größe aber konstant bleiben soll, was ist dann das neue Element im Puffer? Kannst du diese Frage beantworten? Das ist es, was dein Compiler von dir wissen möchte, ebenso ich, Belli und MFK. Ebenso: Wenn du ein Element in den Puffer packen willst, die Größe aber konstant bleiben soll, welches Element fliegt dann raus und wohin?



  • Konstante Größe ließe sich nur erreichen, wenn Du den Container mit einer Anzahl Elementen initialisierst (Default-Konstruktor erforderlich) und in der Folge nur noch mit dem [] - Operator darauf zugreifst, um auf Elemente zuzugreifen oder ihnen neue Werte zuzuweisen. Aber dann könntest Du viel besser einen Vector nehmen!



  • Hallo,

    der Puffer soll eine Maximalgröße haben. diesen hätte ich - wenn ich das Ganze direkt als Klasse hätte nach C# exportieren können (ohne Verwendung von Visual Studio und Berücksichtigung von Versionen) direkt den Konstruktor aufgerufen und die notwendigen Werte mit übergeben. Da dies aber nicht so geht,verwende ich extern"C"-Funktionen. Damit bin ich unabhängig von Kompiler und Version. Gleichzeitig habe ich auch keinen Konstruktor mehr.

    Deshalb wird der deque-Puffer global initialisiert - mit festgesetzter Maximalgröße. Wenn ich deque in der initialisierungs-Funktion einrichte, habe ich auf der einen Seite zwar alle Parameter zur Verfügung, aber deque hört auf zu existieren, sobald die Funktion verlassen wird.

    Die Maximalgröße wird beim ersten Aufruf festgelegt. Neue Elemente werden per push_front() eingefügt und alte Elemente (wenn die maximalgröße erreicht ist) per pop_back entfernt. Diese werden dann nicht mehr gebraucht. Für die Berechnung wird dann per []-Operator auf die Elemente zugegriffen. Genau so soll es sein.

    In C# kann ich den Klassenname direkt als Typ verwenden, um ein Feld oder was auch immer zu deklarieren. In C++ scheint dies nicht zu funktionieren - obwohl meine Klasse nur aus 4 doubble und 1 unsigned int besteht. Scheinbar benötigt deque eine fertige Instanz von meineKlasse, damit der Speicher entsprechend reserviert werden kann.

    Ich habe das rezise jetzt aus der Funktion herausgenommen und werde es durchführen, wenn die ersten daten in ein Objekt von meineKlasse gepackt wurden.
    Warum einfach, wenn es auch kompliziert geht.

    Jens



  • Bis auf die Tatsache, dass in C++ keine Referenzen/Pointer, sondern Objekte gespeichert werden, verhält sich der Container nicht anders als in C#. Dort werden halt bei der Reservierung von 10 Plätzen in der Queue 10 nulls abgelegt. Und die musst Du auch abfangen. Um das in C++ nachzubilden, könntest Du Deinem Objekt einen "null-Zustand" geben, oder aber wiederum Zeiger abspeichern (was ich aber beides nicht empfehlen würde).

    Aber so kompliziert ist das jetzt auch nicht 😉

    std::deque<MeinObjekt> queue; // Keine Reservierung, kein Resize
    
    if (queue.size() >= MAXIMUM_CAPACITY)
        queue.pop_back();
    queue.push_front(neues_element);
    
    for (int i = 0; i < queue.size(); ++i)
        ... queue[i] ...
    

    In C# kann ich den Klassenname direkt als Typ verwenden, um ein Feld oder was auch immer zu deklarieren. In C++ scheint dies nicht zu funktionieren - obwohl meine Klasse nur aus 4 doubble und 1 unsigned int besteht. Scheinbar benötigt deque eine fertige Instanz von meineKlasse, damit der Speicher entsprechend reserviert werden kann.

    Doch, in C++ funktioniert dies genauso. Nur, dass Du in C++ ein Feld von Objekten anlegst (welche natürlich einen gültigen Zustand haben müssen - Defaultkonstruktor) und in C# ein Feld von Referenzen (welche natürlich einen gültigen Zustand haben müssen - null).



  • xenayoo schrieb:

    der Puffer soll eine Maximalgröße haben.

    Maximalgröße != Größe

    xenayoo schrieb:

    diesen hätte ich - wenn ich das Ganze direkt als Klasse hätte nach C# exportieren können (ohne Verwendung von Visual Studio und Berücksichtigung von Versionen) direkt den Konstruktor aufgerufen und die notwendigen Werte mit übergeben. Da dies aber nicht so geht,verwende ich extern"C"-Funktionen. Damit bin ich unabhängig von Kompiler und Version. Gleichzeitig habe ich auch keinen Konstruktor mehr.

    Deshalb wird der deque-Puffer global initialisiert - mit festgesetzter Maximalgröße. Wenn ich deque in der initialisierungs-Funktion einrichte, habe ich auf der einen Seite zwar alle Parameter zur Verfügung, aber deque hört auf zu existieren, sobald die Funktion verlassen wird.

    Was denn nun? Ist die die deque global oder funktionslokal?!

    xenayoo schrieb:

    Die Maximalgröße wird beim ersten Aufruf festgelegt.

    Aufruf von was?

    xenayoo schrieb:

    Neue Elemente werden per push_front() eingefügt und alte Elemente (wenn die maximalgröße erreicht ist) per pop_back entfernt. Diese werden dann nicht mehr gebraucht. Für die Berechnung wird dann per []-Operator auf die Elemente zugegriffen. Genau so soll es sein.

    Schön und gut. Warum meinst du jetzt, irgendwie irgendwo eine Größe festlegen zu müssen?

    xenayoo schrieb:

    In C# kann ich den Klassenname direkt als Typ verwenden, um ein Feld oder was auch immer zu deklarieren. In C++ scheint dies nicht zu funktionieren - obwohl meine Klasse nur aus 4 doubble und 1 unsigned int besteht.

    Doch, das funktioniert auch. Du bist aber wahrscheinlich in die most-vexing-parse Falle reingelaufen. Zumindest verstehe ich die Fehlermeldung so. Wissen können wir das jetzt nicht, weil du deinen Code nicht zeigst.

    xenayoo schrieb:

    Scheinbar benötigt deque eine fertige Instanz von meineKlasse, damit der Speicher entsprechend reserviert werden kann.

    Nein. deque benötigt eine fertige Instanz, um Objekte zu erzeugen. Deine deque soll doch aber am Anfang sicherlich leer sein. Also spar die das mit der Größenangabe!

    xenayoo schrieb:

    Ich habe das rezise jetzt aus der Funktion herausgenommen und werde es durchführen, wenn die ersten daten in ein Objekt von meineKlasse gepackt wurden.
    Warum einfach, wenn es auch kompliziert geht.

    🙄



  • Du verstehst offensichtlich die Funktionsweise der Container nicht. Wenn Du

    deque<meineKlasse> deq(12);
    

    machst, dann enthält die deque bereits 12 Objekte (wenn Deine Klasse denn einen Def.-Konstr. hat). Wenn Du nun push_front aufrufst, dann hat sie bereits 13 Objekte.
    Um das, was ich nun verstanden zu haben glaube, umzusetzen, solltest Du Dir eine Klasse schreiben, die eine (zunächst) leere deque<meineKlasse> sowie ein Attribut maxGroesse beinhaltet.
    Dann kannst Du mit push_... Elemente in Deine deque bringen und jederzeit prüfen, ob maxGroesse erreicht ist, und dann nach Wunsch mit der deque verfahren.


Anmelden zum Antworten