C++ Smart Pointer

Freezedevil

Lieutenant
Registriert
Mai 2011
Beiträge
649
Hi,

ich versuche gerade mir die Benutzung von Smart Pointern anzueignen und hab dafür mal folgenden Codeschnipsel produziert.

Code:
#include <iostream>
#include <memory>
#include <vector>

using namespace std;

shared_ptr<char> allocShared() {
    shared_ptr<char> ptr(new char[1024]);
    return ptr;
}

char* allocStandard() {
    return new char[1024];
}

int main(int argc, char* argv[]) {
    int size = 1024*500;
    if (argv[1] != NULL) {
        cout << "shared" << endl;
        vector<shared_ptr<char>> arr;
        for (int i=0; i<size; ++i) {
            arr.push_back(allocShared());
        }
        cout << "wait to free mem" << endl;
        string wait;
        cin >> wait;
    } else {
        cout << "standard" << endl;
        for (int i=0; i<size; ++i) {
            char* arr = allocStandard();
        }
    }
    cout << "wait to end" << endl;
    string wait;
    cin >> wait;
    return 0;
}

Im else-Zweig werden Standardpointer genutzt, weshalb der Speicherplatz nach dem Verlassen des Blocks verloren ist. Mein Ziel war es jetzt eigentlich zu zeigen, dass das bei der Benutzung von shared_ptr nicht mehr der Fall ist, sprich bei "wait to free mem" ca 500MB belegt sind und bei "wait to end" dieser Platz wieder frei ist. Am Ende des else-Zweig wird der vector schließlich zerstört, womit es keine Referenzen mehr auf die angelegten Arrays gibt und der Smart Pointer diese löschen sollte. Jetzt zeigt mir die Systemüberwachung jedoch trotzdem an, dass nach wie vor 500MB Speicherplatz belegt sind. Was habe ich übersehen oder falsch verstanden?
 
Welchen Compiler bzw. welche STL Version verwendest du
wie genau der Smartpoitner implementiert ist hängt davon ab.

bzw. mit was für einen Programm schaust du dir den Speicherverbrauch an?
 
Zuletzt bearbeitet:
Compiler: g++ (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2
Aufruf: g++ -std=c++0x main.cpp -o main
Ergänzung ()

the_nobs schrieb:
bzw. mit was für einen Programm schaust du dir den Speicherverbrauch an?
Hab ich sowohl über die GUI gemacht sprich "Systemüberwachung" als auch per top übers Terminal.
 
Code:
shared_ptr<char> allocShared() {
shared_ptr<char> ptr(new char[1024]);
return ptr;
}

Liegt hier nicht das Problem vor, das das ptr-Objekt auf dem Stack erzeugt wird und beim Verlassen der Funktion automatisch wieder zerstört wird und damit auch der Pointer gefreed wird?
 
So ich hab das ganze jetzt mal mit Valgrind untersucht, da freigegebener Platz auf dem Heap scheinbar nicht vorhersehbar an das OS zurückgegeben wird. Das Ergebnis dieser Untersuchung war das eigentlich von Anfang an vermutete. Mit Smart Pointer habe ich im Gegensatz zu Standard Pointern kein Memory Loss.

Dafür hat er mir folgenden Fehler angezeigt:
Code:
==8766== Mismatched free() / delete / delete []
==8766==    at 0x4C2A44B: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8766==    by 0x401EE7: std::_Sp_counted_ptr<char*, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr_base.h:293)
==8766==    by 0x40113F: std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (shared_ptr_base.h:147)
==8766==    by 0x400FDA: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (shared_ptr_base.h:558)
==8766==    by 0x400F71: std::__shared_ptr<char, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (shared_ptr_base.h:813)
==8766==    by 0x400F8B: std::shared_ptr<char>::~shared_ptr() (shared_ptr.h:93)
==8766==    by 0x401AAD: void std::_Destroy<std::shared_ptr<char> >(std::shared_ptr<char>*) (stl_construct.h:95)
==8766==    by 0x40184B: void std::_Destroy_aux<false>::__destroy<std::shared_ptr<char>*>(std::shared_ptr<char>*, std::shared_ptr<char>*) (stl_construct.h:105)
==8766==    by 0x40152C: void std::_Destroy<std::shared_ptr<char>*>(std::shared_ptr<char>*, std::shared_ptr<char>*) (stl_construct.h:128)
==8766==    by 0x4012C0: void std::_Destroy<std::shared_ptr<char>*, std::shared_ptr<char> >(std::shared_ptr<char>*, std::shared_ptr<char>*, std::allocator<std::shared_ptr<char> >&) (stl_construct.h:155)
==8766==    by 0x40106A: std::vector<std::shared_ptr<char>, std::allocator<std::shared_ptr<char> > >::~vector() (stl_vector.h:403)
==8766==    by 0x400E75: main (main.cpp:23)
==8766==  Address 0x5a06040 is 0 bytes inside a block of size 1,024 alloc'd
==8766==    at 0x4C2AAA4: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8766==    by 0x400DAC: allocShared() (main.cpp:8)
==8766==    by 0x400E39: main (main.cpp:22)

Das bedeutet in meinem Fall wohl, dass ein mit new[] angelegtes Object nicht mit delete[] (sondern wahrscheinlich mit delete) gelöscht wurde. So wie ich das sehe habe ich das Problem aber nicht zu verantworten oder doch?
 
stwe schrieb:
Code:
shared_ptr<char> allocShared() {
shared_ptr<char> ptr(new char[1024]);
return ptr;
}

Liegt hier nicht das Problem vor, das das ptr-Objekt auf dem Stack erzeugt wird und beim Verlassen der Funktion automatisch wieder zerstört wird und damit auch der Pointer gefreed wird?

Nein. Das ist ja gerade der Sinn am shared_ptr. Zwar wird beim Verlassen der Funktion das auf dem Stack liegende Objekt ptr zerstört, aber vorher ist schon (da ja über das return eine Kopie von ptr zurückgegeben wird) per Kopierkonstruktor von shared_ptr eine neue Instanz erzeugt worden. Eine Kopie von ptr also. Damit ist der Referenzzähler (den shared_ptr intern mitschleift) größer als 0, und das intern referenzierte Objekt wird nicht gelöscht.

@OP: shared_ptr ist nicht gedacht, auf Arrays zu zeigen, da der Destruktor ein delete, nicht ein delete [] vornehmen würde.
 
also ich hab es unter
Wn7 64 und VS 2010 ausprobiert
verhaltet sich genau so wie es sollte

Check nochmal ob du die richtigen Werte bei der Speicherüberprüfung liest
Die OS optimieren Speicherzugriffe indem sie nicht sofort Speicher komplett freigeben, sondern erst mal warten bis wirklich jemand anders das braucht.

Unter win heißt das Virtual Size, sowas ähnliches gibts unter LInux sicher auch.
 
stwe schrieb:
Liegt hier nicht das Problem vor, das das ptr-Objekt auf dem Stack erzeugt wird und beim Verlassen der Funktion automatisch wieder zerstört wird und damit auch der Pointer gefreed wird?

Der Shared Pointer zeigt ja auf Heap. Das sollte schon so passen.
Ergänzung ()

antred schrieb:
@OP: shared_ptr ist nicht gedacht, auf Arrays zu zeigen, da der Destruktor ein delete, nicht ein delete [] vornehmen würde.

Ok, damit hast du die Frage oben ja schon beantwortet. In diesem Fall hab ich das ja auch nur gemacht um Platz zu füllen, aber wie wäre es rcihtig wenn man mal Arrays auf diese Weise verwalten möchte?
 
Freezedevil schrieb:
Ok, damit hast du die Frage oben ja schon beantwortet. In diesem Fall hab ich das ja auch nur gemacht um Platz zu füllen, aber wie wäre es rcihtig wenn man mal Arrays auf diese Weise verwalten möchte?

In der boost-Library (aus der shared_ptr & co ja ursprünglich in den C++ Standard übernommen worden sind) gibt es auch ein shared_array-Template ( http://www.boost.org/doc/libs/1_54_0/libs/smart_ptr/shared_array.htm ). In bin mir jetzt leider nicht sicher, ob das ebenfalls mit in den Standard eingeflossen ist.
Falls nicht und du bestehst trotzdem auf einem geshared'ten array, könntest du ja einfach ein std::shared_ptr< std::vector< char > > oder einen std::shared_ptr< std::string > nehmen.
 
Das shared_array hat es wohl scheinbar nicht geschafft. Auf Stackoverflow wird auf jeden Fall auch die vector Variante empfohlen.
Meine Fragen zu diesem Thema sind damit vorerst alle beantwortet und ich danke allen die mir geholfen haben, allen voran antred.
Ergänzung ()

Ich hab grad gesehen, dass es sogar std::array gibt. Man lernt echt nicht aus.
 
Übrigens kannst du

Code:
shared_ptr<char> allocShared() {
    shared_ptr<char> ptr(new char[1024]);
    return ptr;
}

auch etwas abkürzen, indem du dir die ptr-Variable sparst.

Code:
shared_ptr<char> allocShared() {
    return shared_ptr<char>(new char[1024]);
}

EDIT: Bleibt natürlich immer noch das Problem, dass shared_ptr prinzipiell nicht für arrays gedacht ist. Ich wollte nur zeigen, daß man da eine Variable einsparen kann.
 
Hier werden meine Fragen sogar beantwortet ohne dass ich sie gestellt hab. Das nenne ich Service^^ Hab mich schon gefragt ob das so geht und wollte erstmal auf Nummer Sicher gehen.
Nochmals Danke.
Ergänzung ()

Code sieht jetzt im Endeffekt so aus
Code:
#include <iostream>
#include <memory>
#include <vector>
#include <array>

using namespace std;

shared_ptr<array<char, 1024>> allocShared() {
    return shared_ptr<array<char, 1024>>(new array<char, 1024>);
}

char* allocStandard() {
    return new char[1024];
}

int main(int argc, char* argv[]) {
    int size = 1024*500;
    if (argv[1] != NULL) {
        cout << "shared" << endl;
        vector<shared_ptr<array<char, 1024>>> vec;
        for (int i=0; i<size; ++i) {
            vec.push_back(allocShared());
        }
        // cout << "wait to free mem" << endl;
        // string wait;
        // cin >> wait;
    } else {
        std::cout << "standard" << std::endl;
        for (int i=0; i<size; ++i) {
            char* arr = allocStandard();
        }
    }
    // cout << "wait to end" << endl;
    // string wait;
    // cin >> wait;
    return 0;
}

Wollte ein Array behalten um Speicherplatz zu verbrennen ohne noch extra nen Vector zu füllen. Auf diese Weise hat Valgrind auch nichts mehr zu meckern und alle sind glücklich (hoffentlich^^), wobei "vector<shared_ptr<array<char,1024>>>" schon ne ziemlich absurde Konstruktion ist.
 
Zuletzt bearbeitet: (jetzt hatte ich doch glatt die zuletzt angesprochene Optimierung vergessen)
Nein. Das ist ja gerade der Sinn am shared_ptr. Zwar wird beim Verlassen der Funktion das auf dem Stack liegende Objekt ptr zerstört, aber vorher ist schon (da ja über das return eine Kopie von ptr zurückgegeben wird) per Kopierkonstruktor von shared_ptr eine neue Instanz erzeugt worden. Eine Kopie von ptr also.
Ah, danke für die Aufklärung. Das mit dem Kopierkonstruktor erklärt natürlich die Sache.

Meine C++-Tage sind wohl doch schon ein wenig länger her *g
 
Zurück
Oben