GCC & auto_ptr



  • Ich arbeite mich gerade durch auto_ptr durch. Versteh ich soweit auch, unter Visual C++ 6 kompiliert der folgendes Beispiel ohne Weiteres:

    #include "xxx.h"
    #include <iostream>
    #include <memory>
    #include <string>
    using namespace std;
    
    const auto_ptr<XXX> getX1()
    {
       return auto_ptr<XXX>(new XXX(1));
    }
    
    const auto_ptr<string> getString(const string& str)
    {
       string* pStr = new string(str);
       *pStr += " Welt";
       return auto_ptr<string>(pStr);
    }
    
    int main()
    {
       auto_ptr<XXX> x1;
    
       cout << "x1" << endl;
       x1 = getX1();
    
       cout << x1->getValue() << endl;
    
       cout << *getString("Hallo") << endl;
    
       return 0;
    }
    

    Naja, ist aus Marcus' Buch (Objekt. Programmierung für Dummies), die Headerdateien sind auch die gleichen, blah. Jetzt wirft der mir unter Linux folgende Meldung raus:

    retlocal4.cpp:24: passing `const std::auto_ptr<XXX>' as `this' argument of `
    std::auto_ptr<_Tp>::operator std::auto_ptr_ref<_Tp1>() [with _Tp1 = XXX, _Tp
    = XXX]' discards qualifiers

    Ich benutze den GCC 3.2.2 20030222 und die glibc 2.3.2-27.9. Ich hab auch schon in Newsgroups nachgeschaut und soweit ich das verstanden habe, ist diese Implementierung nicht das Wahre. Ein Lösungsvorschlag war dann, einen eigenen auto_ptr zu implementieren. Wie auch immer, ich erinnere mich, dass ich schonmal Probleme hatte, Beispiele aus dem Buch unter Linux zu kompilieren.

    Frage:
    Hat die Beispiele aus dem Buch schonmal jemand unter Linux kompiliert, und gab es da irgendwelche Schwierigkeit und wenn ja, wie habt ihr die behoben?



  • Vielleicht kann ich dir mit einer eigenen SmartPointer-Klasse helfen:
    ref_counter.h:

    //ref_counter.h: defines a class for reference counting
    
    #ifndef _SP_REF_COUNTER_H_
    #define _SP_REF_COUNTER_H_
    
    namespace sp
    {
       class ref_counter
         {
          private:
    	unsigned int *_count;
    
    	void decrement()
    	  {
    	     if(unique())
    	       delete _count;
    	     else
    	       --*_count;
    	  }
    
          public:
    	ref_counter() : _count(new unsigned int(1U))
    	  {
    	  }
    
    	ref_counter(const ref_counter &src) : _count(src._count)
    	  {
    	     ++*_count;
    	  }
    
    	ref_counter &operator =(const ref_counter &src)
    	  {
    	     ++*src._count;
    	     decrement();
    	     _count = src._count;
    	     return *this;
    	  }
    
    	~ref_counter()
    	  {
    	     decrement();
    	  }
    
    	bool unique() const
    	  {
    	     return *_count == 1;
    	  }
    
    	unsigned int get_count() const
    	  {
    	     return *_count;
    	  }
         };
    }
    
    #endif
    

    sp.h:

    //sp.h: smart pointer template
    
    #ifndef _SP_SP_H_
    #define _SP_SP_H_
    
    #include <typeinfo>
    #include "ref_counter.h"
    
    namespace sp
    {
       template <typename T>
         class sp
         {
    	template <typename F>
    	  friend class sp;
    
          private:
    	T *_ptr;
    	ref_counter _counter;
    
    	sp(T *fresh, const ref_counter &counter) : _ptr(fresh), _counter(counter)
    	  {
    	  }
    
          public:
    	sp() : _ptr(0)
    	  {
    	  }
    
    	sp(T *fresh) : _ptr(fresh)
    	  {
    	  }
    
    	~sp()
    	  {
    	     if(_counter.unique() && _ptr)
    	       delete _ptr;
    	  }
    
    	bool unique() const
    	  {
    	     return _counter.unique();
    	  }
    
    	unsigned int get_references() const
    	  {
    	     return _counter.get_count();
    	  }
    
    	bool null() const
    	  {
    	     return _ptr == 0;
    	  }
    
    	T *get_ptr() const
    	  {
    	     return _ptr;
    	  }
    
    	template <typename T2>
    	  sp<T2> cast() const
    	  {
    	     if(_ptr)
    	       {
    		  T2 *ptr = dynamic_cast<T2 *>(_ptr);
    		  if(!ptr)
    		    throw std::bad_cast();
    		  else
    		    return sp<T2>(ptr, _counter);    //same object, so copy _counter
    	       }
    	     else
    	       {
    		  return sp<T2>(0);
    	       }
    	  }
    
    	T &operator *() const
    	  {
    	     return *_ptr;
    	  }
    
    	T *operator ->() const
    	  {
    	     return _ptr;
    	  }
    
    	sp<T> &operator =(const sp<T> &src)
    	  {
    	     if(_ptr != src._ptr)
    	       {
    		  if(unique() && _ptr)
    		    delete _ptr;
    		  _ptr = src._ptr;
    		  _counter = src._counter;
    	       }
    	     return *this;
    	  }
         };
    
       template <typename T>
         inline bool operator ==(const sp<T> &l, const sp<T> &r)
           {
    	  return l.get_ptr() == r.get_ptr();
           }
    
       template <typename T>
         inline bool operator !=(const sp<T> &l, const sp<T> &r)
           {
    	  return l.get_ptr() != r.get_ptr();
           }
    
       template <typename T>
         inline bool operator <=(const sp<T> &l, const sp<T> &r)
           {
    	  return l.get_ptr() <= r.get_ptr();
           }
    
       template <typename T>
         inline bool operator < (const sp<T> &l, const sp<T> &r)
           {
    	  return l.get_ptr() < r.get_ptr();
           }
    
       template <typename T>
         inline bool operator >=(const sp<T> &l, const sp<T> &r)
           {
    	  return l.get_ptr() >= r.get_ptr();
           }
    
       template <typename T>
         inline bool operator > (const sp<T> &l, const sp<T> &r)
           {
    	  return l.get_ptr() > r.get_ptr();
           }
    }
    
    #endif
    

    Ist zwar nicht perfekt, funktioniert aber. Funktioniert ähnlich wie auto_ptr...



  • wenn dein Code nicht eine xxx.h benötigen würde, tät ich ihn ausprobieren ...



  • kann es sein, dass die Methode, die du aufrufst nicht const ist?

    ich bin mir gar nicht sicher, aber eigentlich sollte es nicht möglich sein, dass du ein const auto_ptr kopierst, da kopieren bei auto_ptr zerstörend ist. Lass vielleicht mal das const weg.



  • @Bashar: Ich poste mal die xxx.h und xxx.cpp, so dass du das ausprobieren kannst...

    // xxx.h
    #ifndef _XXX_H
    #define _XXX_H
    
    class XXX
    {
    public:
       XXX();
       XXX(int value);
       XXX(const XXX& source);
       const XXX& operator=(const XXX& source);
       int getValue() const {return m_Value;}
       void setValue(int value) {m_Value = value;}
    private:
       int m_Value;
    };
    
    #endif
    
    // xxx.cpp
    #include "xxx.h"
    #include <iostream>
    using namespace std;
    
    XXX::XXX()
       : m_Value(0)
    {
       cout << "Defaultkonstruktor" << endl;
    }
    
    XXX::XXX(int value)
       : m_Value(value)
    {
       cout << "int-Konstruktor" << endl;
    }
    
    XXX::XXX(const XXX& source)
       : m_Value(source.m_Value)
    {
       cout << "Copykonstruktor" << endl;
    }
    
    const XXX& XXX::operator=(const XXX& source)
    {
       m_Value = source.m_Value;
       cout << "Operator =" << endl;
       return *this;
    }
    

    @Steven: Ich werds mal ausprobieren, danke auf jeden Fall



  • ach, der Fehler ist klar (hättest du die Zeile angegeben, wo das passier hätte ich dir das auch vorher sagen können 🙄)

    std::auto_ptr ist ein Smart Pointer mit zerstörender kopierung. Heisst, dass es immer nur ein Objekt geben darf, was auf den Pointer zeigt. Wenn du einen auto_ptr in einen anderen kopierst, wird der erste zerstört. Deswegen erwartet der copy-operator und der copy-ctor ein nicht const Objekt. Du lieferst aber const auto_ptr<XXX> zurück. Lass mal das const weg und deine Probleme sollten der Vergangenheit angehören.

    Falls du trotzdem noch einen anderen Smart Pointer suchst, schau dir mal die Loki Library an oder das Buch "Modern C++ Design" (bzw. "Modernes C++ Design"). Das Kapitel über Smart Pointer solltest du glaub ich sogar Online als Probe Kapitel finden, auf der "Modern C++ Design" Seite



  • Mmmh...ne, gibt nur ne andere Fehlermeldung...

    Fakt ist:
    Unter Visual C++ wird das anstandslos kompiliert.
    Der Code ist 1:1 aus dem Buch übernommen.

    Naja, werde ich nochmal weitertüfteln müssen.....



  • Naja, ist aus Marcus' Buch

    Schau mal in den Errata-Text zum Buch.
    Dort habe ich Marcus zu dem Thema folgendes schreiben lassen:

    Dort wird aus den Funktionen getX1 und getString ein const auto_ptr zurück
    gegeben. Der Rückgabewert von getX1 wird später einem auto_ptr zugewiesen.
    Das ist in Standard-C++ nicht erlaubt, leider erlaubt der Microsoft Visual C++ 6
    diese Konstruktion. Der Visual C++ 7 meckert übrigens an dieser Stelle, genau
    wie der Borland C++ Builder und die ganzen GNU-Compiler.
    Ein const auto_ptr ist ein auto_ptr der niemals einen ownership Transfer
    durchführen kann. Er ist also immer das Ende einer Kette. Korrekterweise
    müssten beide Funktionen jeweils einen auto_ptr (ohne const) liefern. Leider
    gibt es bisher nur wenig Compiler die diesen Fehler bemerken.

    Wenn du genau wissen willst, warum sich ein const auto_ptr so verhält, such im Internet nach "colvin/gibbons trick" oder frag hier noch mal. Sei dir aber darüber im Klaren, dass die Technik dahinter nicht ohne ist (ohne gute Kenntnisse zum Thema lvale/rvalue-Bindung + Überladungsauflösung wird's schwer zu verstehen).

    Letztlich muss man aber auch nicht den Trick sondern nur dessen Auswirkung verstehen.

    PS:
    Ausführliches zum Thema auto_ptr findest du z.B. auch in meinen FAQs:
    http://fara.cs.uni-potsdam.de/~kaufmann/?page=GenCppFaqs&faq=auto_ptr#Answ



  • Ach Hume, was wäre ich ohne dich... :D. Dachte schon ich zerbrech an dem Problem....



  • CarstenJ schrieb:

    Ach Hume, was wäre ich ohne dich... :D. Dachte schon ich zerbrech an dem Problem....

    was ist jetzt der Unterschied von dem was ich gesagt hab und Hume? Ist doch das gleiche. Das const war das Problem.



  • Ja aber Hume schafft es das selbe durch wesentlich mehr Fachbegriffen intelligenter wirken zu lassen. (@Hume: Das ist nicht böse gemeint.)



  • Ich danke natürlich auch dir, Kingruedi und allen anderen, die mir geholfen haben. Vor allem hat mich aber verunsichert, dass der Code mit dem VC++ kompiliert wurde, und das konnte ich mir beim besten Willen nicht erklären.

    Laufen tuts bei mir übrigens immer noch nicht, weder mit const noch ohne, aber das liegt wohl wirklich an meiner Implementierung des auto_ptr. Werde jetzt mal die von Steven testen und ich tiefer in die Materie einarbeiten.



  • was sagt der denn jetzt? 🙄



  • Habs hinbekommen....ohne const läuft es jetzt, hatte noch nen Alias auf den GCC 2.96, und mit dem liefs nicht. 🙄
    Ich glaube ich spiele erstmal was anderes als C++. 😃


Log in to reply