Problem mit std::list und auto_ptr



  • Hallo

    hab mein Programm das Ürsprünglich die Daten in eine std::list<konto*> gespeichert hat umgebaut zu std::set<auto_ptr<konto> > .

    Mein Compiler (Borland Builder 2006) gibt aber jetzt den Fehler :
    "[C++ Fehler] functional(134): E2093 'operator<' ist im Typ 'auto_ptr<konto>' für Argumente desselben Typs nicht implementiert"

    Wenn ihr sonstige Anmerkungen zum Quelltext habt dann macht die 🙂 Kritik ist erwünscht will mich schließlich verbessern.

    So hier der Code
    Unit1.cpp

    //---------------------------------------------------------------------------
    #include <iostream>
    #include <set>
    #include <conio.h>
    #include <fstream>
    #include <vector>
    #include <string>
    #include <sstream>
    #include <memory>
    #pragma hdrstop
    using namespace std;
    #include "konto.h"
    //---------------------------------------------------------------------------
    int mainmenu()
    {
     int tmp;
     clrscr();
     gotoxy(20,8);
     cout<<"1 Neues Konto anlegen";
     gotoxy(20,9);
     cout<<"2 Buchungen";
     gotoxy(20,10);
     cout<<"3 Konto loeschen";
     gotoxy(20,11);
     cout<<"4 Kontoinformationen anzeigen";
     gotoxy(20,12);
     cout<<"5 Programm beenden";
     gotoxy(35,16);
     cin>>tmp;
     return tmp;
    }
    void writeData(const set<auto_ptr<konto> >& dat)
    {
     ofstream out("konto.dat");
     set<auto_ptr<konto> >::const_iterator it = dat.begin();
     kDat tmp;
     while(it != dat.end())
     	{
            tmp=(*it)->retDat();
            out<<tmp.kArt<<endl;
            out<<tmp.kStand<<endl;
            out<<tmp.kNr<<endl;
            out<<tmp.kR<<endl;
        }
    
    }
    
    void CreateKonto(set<auto_ptr<konto> >& dat)
    {
      clrscr();
      string tmp;
      int kd;
      stringstream u;
      int tm;
      gotoxy(12,6);
      cout<<"Geben Sie den gewünschten Kontotyp ein:";
      gotoxy(51,6);
      cin>>tmp;
      if(tmp == "Girokonto" || tmp == "girokonto")
      	{
        gotoxy(12,7);
        cout<<"Geben Sie den Uberziehungsrahmen ein";
        gotoxy(58,7);
        cin>>tm;
        tm*=-1;
        kd=dat.size()+1;
        u<<kd;
        u>>tmp;
        auto_ptr<konto> temp;
        temp.reset(new girokonto(tm,tmp));
        dat.insert(temp);
    
        }
      if(tmp == "Sparkonto" || tmp=="sparkonto")
      	{
        gotoxy(12,7);
        cout<<"Geben Sie die Mindesteinlage ein";
        gotoxy(58,7);
        cin>>tm;
        kd=dat.size()+1;
        u<<kd;
        u>>tmp;
        auto_ptr<konto> temp;
        temp.reset(new sparkonto(tm,tmp));
        dat.insert(temp);
        }
    }
    void Buchen(const set<auto_ptr<konto> >& dat)
    {    
      string tmp;
      clrscr();
      cout<<"Geben Sie die Kontonummer an";
      cin>>tmp;  
    }
    void Kontoloeschen(set<auto_ptr<konto> >& dat)
    {
      clrscr();
    }
    void LoadDat(set<auto_ptr<konto> >& dat)
    {
     vector<string> tmp;
     string t,art,nr;
     int stand,r;
     auto_ptr<konto> temp;
     stringstream u;
     ifstream in("konto.dat");
     while(!in.eof())
     	{
          in>>t;
          tmp.push_back(t);
        }
     vector<string>::iterator it = tmp.begin();
     for(unsigned int i=0;i<tmp.size()/4;i++)
     	{
        art = *it;
        it++;
        nr=*it;
        it++;
        u<<*it;
        u>>stand;
        it++;
        u<<*it;
        u>>r;
        if(art=="s")
        	{
            	temp.reset(new sparkonto(r,nr));
                temp->Buchung(stand);
                dat.insert(temp);
            }
        if(art=="g")
        	{
                temp.reset(new girokonto(r,nr));
                temp->Buchung(stand);
                dat.insert(temp);
            }
    
        }
    }
    void ShowKonto(const set<auto_ptr<konto> >& dat)
    {
    
    }
    #pragma argsused
    int main(int argc, char* argv[])
    {
    set<auto_ptr<konto> > tmp;
    
    while(1)
    switch(mainmenu())
    	{
        case 1:CreateKonto(tmp); break;
        case 2:Buchen(tmp);break;
        case 3:Kontoloeschen(tmp); break;
        case 4:ShowKonto(tmp); break;
        case 5:
        clrscr();
        writeData(tmp);
        cout<<"Aenderungen wurden gespeichert"<<endl;
        cout<<"Press any Key to quit";
        getchar();
        getchar();
        return 0;
        }
    
    }
    

    konto.h

    //---------------------------------------------------------------------------
    
    #ifndef kontoH
    #define kontoH
    struct kDat
    {
        string kArt;
        string kStand;
        string kNr;
        string kR;
    };
    class konto
    {
      private:
      string kontoart;
      int kontostand; //in Cent
      string kontonr;
      protected:
      konto(const string& kdnr,const string& art);
    
      public:
      virtual ~konto(){}
      int getKontostand();
      virtual void Buchung(int Betrag)=0;
      virtual kDat retDat();
      long long int getKdnr();
    };
    class girokonto : public konto
    {
      private:
      int ueberziehungsrahmen;
      public:
      void Buchung(int Betrag);
      girokonto(int Ueberziehungsrahmen,const string& Kontonr);
      kDat retDat();
    
    };
    class sparkonto : public konto
    {
      private:
      int mindestEinlage;
      public:
      void Buchung(int Betrag);
      sparkonto(int MindestEinlage,const string& Kontonr);
      kDat retDat();
    };
    //---------------------------------------------------------------------------
    #endif
    

    konto.cpp

    //---------------------------------------------------------------------------
    #include<string>
    #include<sstream>
    #pragma hdrstop
    using namespace std;
    
    #include "konto.h"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    int konto::getKontostand()
    {
      return kontostand;
    }
    void konto::Buchung(int Betrag)
    {
     kontostand += Betrag;
    }
    void girokonto::Buchung(int Betrag)
    {
      if(konto::getKontostand() + Betrag < ueberziehungsrahmen)
      	{
            throw 1;
        }
      else konto::Buchung(Betrag);
    }
    void sparkonto::Buchung(int Betrag)
    {
      if(konto::getKontostand() + Betrag < mindestEinlage)
      	{
            throw 2;
        }
      else konto::Buchung(Betrag);
    }
    konto::konto(const string& kdnr,const string& art) : kontonr(kdnr),kontostand(0),kontoart(art){} 
    girokonto::girokonto(int Ueberziehungsrahmen,const string& Kontonr): konto(Kontonr,"Girokonto"),ueberziehungsrahmen(Ueberziehungsrahmen){}
    sparkonto::sparkonto(int MindestEinlage,const string& Kontonr) : konto(Kontonr,"Sparkonto"),mindestEinlage(MindestEinlage){}
    kDat konto::retDat()
    {
        kDat erg;
        stringstream u;
        u<<kontostand;
        u>>erg.kStand;
        erg.kNr=kontonr;
        return erg;
    }
    
    kDat sparkonto::retDat()
    {
     stringstream u;
     kDat erg = konto::retDat();
     erg.kArt="s";
     u<<mindestEinlage;
     u>>erg.kR;
     return erg; 
    }
    kDat girokonto::retDat()
    {
     stringstream u;
     kDat erg = konto::retDat();
     erg.kArt="g";
     u<<ueberziehungsrahmen;
     u>>erg.kR;
     return erg;
    }
    long long int konto::getKdnr()
    {
        stringstream u;
        u<<kontonr;
        int erg;
        u>>erg;
        return erg;
    }
    


  • Naja, std::list ist nicht sortierender container. std::set hingegen ja.
    Du kannst dein problem dadurch lösen, dass du den <-Operator für std::auto_ptr überlädst, der z.b. die adressen vergleicht.
    also soetwas wie:

    bool operator<(std::auto_ptr<konto> a, std::auto_ptr<konto> b)
    { return a.get() < b.get(); }
    

    Dadurch (eigentlich schon vorher) baust du zwar eine konto-lösch-maschiene. aber das findest du spätestens wenn du das programm startest (hoffentlich) selbst heraus.



  • ok dieses Problem wäre gelöst. vielen dank


  • Mod

    auto_ptr hat in Standardkontainern nichts verloren.



  • warum ? was wäre die alternative?

    wenn ich einen normalen zeiger verwende wird glaub ich der dynamische Speicher nicht freigegeben wenn ein zeiger aus dem kontainer gelöscht wird. Oder täusche ich mich da?



  • Zur Frage warum, habe ich dir schon einen hinweis gegeben.
    Beispiel:

    struct A
    {
       int a;
       A() : a(10) {}
    };
    
    void foo(std::auto_ptr<A> a)
    {
    }
    
    int main()
    {
       std::auto_ptr<A> var(new A);
       std::cout << var->a << std::endl;
       foo(var);
       std::cout << var->a << std::endl; /// hier 
       // und jedes mal, wenn du auf das Objekt zugreifst bekommst du ein Problem
       return 0;
    }
    

    Passenderes gibt es dafür in der Standard-lib von c++ nicht.
    Was du suchst sind smart pointer oder aber kannst du eine Funktion schreiben, die den Speicher selbst freigibt.

    Außerdem vorher hast du std::list verwendet. warum bist du jetzt auf std::set umgestiegen?



  • hmm ok dann wäre z.B. boost::scoped_ptr<konto> besser nehme ich an.

    std::set verwende ich nur zur Übung weil ich damit noch nie etwas gemacht habe.


  • Mod

    walljumper schrieb:

    warum ?

    Weil auto_ptr nicht den Anforderungen, den Kontainer an ihre Elemente stellen, genügt. Für auto_ptr ist u.a.

    auto_ptr<foo> t(new foo);
    const auto_ptr<foo> u(new foo);
    auto_ptr<foo> T(t); // ok, aber T und t nicht äquivalent
    auto_ptr<foo> U(u); // error, konstanter auto_ptr nicht kopierbar
    

    walljumper schrieb:

    wenn ich einen normalen zeiger verwende wird glaub ich der dynamische Speicher nicht freigegeben wenn ein zeiger aus dem kontainer gelöscht wird. Oder täusche ich mich da?

    Ein nackter Zeiger verhält sich in einem Kontainer nicht anders als sonst auch.

    walljumper schrieb:

    was wäre die alternative?

    Reihenfolge ohne Belang:
    1. Benutz einen Kontainer, der explizit für Polymorphe Objekte gedacht ist [boost.ptr_container]
    2. Benutz einen Smartpointer, der shared ownership betreibt, z.B. boost::shared_ptr
    3. Benutz einen Smartpointer, der depp copy betreibt
    Für 1. und 3. muss deine Klasse clonebar sein.



  • walljumper schrieb:

    hmm ok dann wäre z.B. boost::scoped_ptr<konto> besser nehme ich an.

    ich kenne mich mit boost nicht aus, aber dem namen nach macht es dasselbe wie auto_ptr

    ich persönlich verwende Loki http://loki-lib.sourceforge.net/
    Dort heißt das Ding was du suchst. SmartPtr



  • cppuser schrieb:

    walljumper schrieb:

    hmm ok dann wäre z.B. boost::scoped_ptr<konto> besser nehme ich an.

    ich kenne mich mit boost nicht aus, aber dem namen nach macht es dasselbe wie auto_ptr

    ich persönlich verwende Loki http://loki-lib.sourceforge.net/
    Dort heißt das Ding was du suchst. SmartPtr

    der Loki::SmartPtr ist nicht so einfach zu benutzen, wie die Smartpointer von boost. In diesem Fall ist der boost::shared_ptr<> die erste Wahl. Denn dort heißt es

    Every shared_ptr meets the CopyConstructible and Assignable requirements of the C++ Standard Library, and so can be used in standard library containers.

    Gruß
    Werner



  • hmm das verwirrt mich jetzt etwas in der dokumentation von boost wird boost::scoped_ptr als smartpointer bezeichnet. naja

    2. Benutz einen Smartpointer, der shared ownership betreibt, z.B. boost::shared_ptr

    scheint das richtige zu sein.



  • kann es sein, dass du nicht verstanden hast warum auto_ptr bzw. scoped_ptr nicht geeignet sind?

    wie gesagt scoped_ptr kenn ich nicht. ICh bin heute mal nett und erkläre auto_ptr

    Im Groben und Ganzen funktioniert auto_ptr folgendermaßen:
    Diese Klasse sorgt dafür, dass der Zeiger, den sie kapselt, dann zerstört wird, wenn der Desturktor von auto_ptr aufgerufen wird.
    Der Desktuktor wird dann aufgerufen, wenn der Gültigkeitsbereich des auto_ptr-Objekts verlassen wird. Diese Technik wird RAII (Resource Acquisition Is Initialization).
    Wenn du beispielweise nun diese Funktion aufrufst:

    void foo(std::auto_ptr<int> a)
    {
    // viel kode
    // ..
    
    // Dann ist hier der Gültigkeitsbereich von a zu 
    // ende. a gibt selbst dann den Speicher den du vorher 
    // angefordert hast frei. (Siehe unten)
    }
    
    int main()
    {
       std::auto_ptr<int> abc(new int);
       *(abc.get()) = 10;
    
       foo(zeiger); // Die Speicherverwaltung wird von dem Parameter übernommen
       // Nach dem Aufruf, ist der Speicher freigegeben. abc zeigt nicht mehr 
       // auf einen gültigen Bereich.
    
       // Warum das so ist
       std::auto_ptr<int> bcd(new int);
       *(bcd.get()) = 10;
       std::auto_ptr<int> cde(bcd); /// Hier passiert nun folgendes:
       /*
          der Zeiger wird an cde übergeben.
          bcd verweist auf null.
       */
    

    Eine vereinfachte implementierung von dem auto_ptr würde ungefähr so aussehen:

    struct MyPtr
    {
       explicit MyPtr(int* p)
          : zeiger_(p) { }
    
       MyPtr(const MyPtr z)
       {
          zeiger_ = z.zeiger_;
          z.zeiger_ = NULL;
       }
    
       int* get()
       { return zeiger_; } 
    private:
       int* zeiger_; 
    };
    

    Hoffentlich war es verständlich.



  • Verdammt, habe bei MyPtr den Destruktor vergessen:

    ~MyPtr()
    {
       delete zeiger_;  // Wenn der Zeiger NULL ist, wird nichts freigegeben.
    }
    

    PS: Und nochwas, denke nicht daran auto_ptr (oder MyPtr) im Zusammenhang mit Arrays zu verwenden. Arrays werden mit delete[] freigegen!



  • Ja hab ich schon verstanden bin nur etwas durcheinander gekommen, weil du gemeint hast ich brauche einen smartpointer.
    In der Dokumentation von boost wird boost::scoped_ptr als smartpointer bezeichnet. Die anderen hier haben aber aber gesagt das auto_ptr nicht geht wegen Problemen beim kopieren diese hat der boost::scoped_ptr aber auch.

    Jetzt frag ich mich nur was gilt den jetzt als smartpointer 😕





  • Eine ausführliche Erläuterung findet man bei Herb Sutter (nach "Container" suchen).

    Herb Sutter nennt auto_ptr<> in Containern in einem anderen Artikel auch "nackt über das Minenfeld laufen" - d.h. es kann gut gehen 🕶

    Gruß
    Werner



  • vielen dank für eure ausführliche hilfe 🙂



  • scoped_ptr kannst du im Übrigen gar nicht in einen Container packen, weil dafür müsste er eine Kopiersemantik besitzen, was ein scoped_ptr nicht hat.

    Wenn du eh boost verwendest nimm die pointer container von boost.


Anmelden zum Antworten