smartPointer
-
Ich habe mal versucht eine eigene kleine smartPointer Klasse zu bauen um besser vertehen zu können was da genau passiert.
Erst mal meine Klasse:
Header:#ifndef __SMARTPOINTER_H__
#define __SMARTPOINTER_H__template< typename T > class smartPointer { private: T* ptr; public: smartPointer( ); smartPointer( T* value ); ~smartPointer( ); T* operator->( ) const; T operator*( ) const; }; #endif __SMARTPOINTER_H__
include "SmartPointer.h" template< typename T > smartPointer< T >::smartPointer( ) { ptr = NULL; cout << "Called !" << endl; } template< typename T > smartPointer< T >::smartPointer( T *value ) { ptr = value; cout << "Called !" << endl; } template< typename T > smartPointer< T >::~smartPointer( ) { cout << "DESTROYED !" << endl; delete ptr; } template< typename T > T smartPointer< T >::operator *( ) const { return *ptr; } template< typename T > T* smartPointer< T >::operator ->( ) const { return ptr; }
Meine Main:
#include <windows.h> #include <iostream> #include "SmartPointer.h" #include "SmartPointer.cpp" using namespace std; class MYTESTCLASS { public: MYTESTCLASS( ) { } ~MYTESTCLASS( ) { } void Aus( ) { cout << "Ich bin aus MYTESTCLASS !" << endl; } }; int main( int arg, char* arvg[ ] ) { smartPointer< MYTESTCLASS > xc( new MYTESTCLASS ); xc->Aus( ); { smartPointer< MYTESTCLASS > xx( xc ); xx->Aus( ); } xc->Aus( ); return NULL; }
Nun meine Frage: Warum wird der letzt Call vor dem return NULL; noch ausgeführt? In der Console steht davor auch DESTROYED. Kann mir das jemand mal erklären?
MfG
-
Zu diesem Zeitpunkt ist nur der innere Smart-Pointer zerstört worden, der äussere lebt bis Ende
main()
. Da du aber keinen Kopierkonstruktor definiert hast, führen Kopien zu undefiniertem Verhalten (da der Speicher zweifach freigegeben wird).In deinem Code könnte man einiges verbessern, hier ein paar Vorschläge:
- Bezeichner mit Unterstrichen am Anfang, wie
__SMARTPOINTER_H__
, sind für Compiler und Implementierung reserviert. Verwende dochSMARTPOINTER_H
. - Benutze im Konstruktor die Initialisierungsliste statt Zuweisungen.
- Verbiete Kopierkonstruktor und Zuweisungsoperator, indem du sie
private
machst. Oder implementiere sie sinnvoll. - Mache den Konstruktor, der ein
T*
nimmt,explicit
, um implizite Konvertierungen von Zeigern zu verhindern. NULL
steht für den Nullpointer. Missbrauche das Literal nicht fürint
s, schreibereturn 0;
oder gar nichts.- Du benötigst kein
<windows.h>
. - Defaultkonstruktor und Destruktor mit leerer Definition werden automatisch vom Compiler generiert, du brauchst sie nicht hinzuschreiben.
#endif __SMARTPOINTER_H__
ist kein gültiges C++. Du kannst aberSMARTPOINTER_H
als Kommentar einsetzen.- .cpp-Dateien solltest du nie inkludieren.
- Templatedefinitionen müssen im Header stehen. Du kannst allerdings eine separate .inl-Datei für die Implementierung verwenden und am Schluss des Schnittstellen-Headers einbinden.
- Wenn du
std::cout
schon im Template benötigst, solltest du<iostream>
auch bereits dort einbinden. Sonst muss der Benutzer des Templates daran denken, damit der Code kompiliert.
- Bezeichner mit Unterstrichen am Anfang, wie
-
Wow. Vielen dank. Hab einiges ausgebessert!
Wie müsste ich das mit dem Kopierkonstruktor anstellen?MfG
-
BlauBär schrieb:
Wow. Vielen dank. Hab einiges ausgebessert!
Wie müsste ich das mit dem Kopierkonstruktor anstellen?MfG
Im Idealfall verbietest du Kopien, da das meist nicht gewünscht ist. Move-Konstruktor hingegen ist ein heißer Tipp.
template< typename T > class smartPointer { ... // Kopien verbieten. smartPointer(smartPointer<T> const&) = delete; smartPointer<T>& operator=(smartPointer<T> const&) = delete; smartPointer(smartPointer<T>&& other) : ptr(other.ptr) { other.ptr = 0; } smartPointer<T>& operator=(smartPointer<T>&& other) { if(ptr == other.m_ptr) { other.ptr = 0; } else { delete ptr; ptr = other.ptr; other.ptr = 0; } return *this; } ... };
So ungefähr. Keine Garantie, bin gerade etwas breit. ,)