wake-up-neo.net

Richtige Methode zum Erstellen von unique_ptr, das ein zugewiesenes Array enthält

Wie kann ein unique_ptr erstellt werden, das ein Array enthält, das dem freien Speicher zugeordnet ist? Visual Studio 2013 unterstützt dies standardmäßig, aber wenn ich die gcc-Version 4.8.1 unter Ubuntu verwende, bekomme ich Speicherlecks und undefiniertes Verhalten.

Das Problem kann mit diesem Code reproduziert werden: 

#include <memory>
#include <string.h>

using namespace std;

int main()
{
    unique_ptr<unsigned char> testData(new unsigned char[16000]());

    memset(testData.get(),0x12,0);

    return 0;
}

Valgrind gibt diese Ausgabe aus:

==3894== 1 errors in context 1 of 1:
==3894== Mismatched free() / delete / delete []
==3894==    at 0x4C2BADC: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-AMD64-linux.so)
==3894==    by 0x400AEF: std::default_delete<unsigned char>::operator()(unsigned char*) const (unique_ptr.h:67)
==3894==    by 0x4009D0: std::unique_ptr<unsigned char, std::default_delete<unsigned char> >::~unique_ptr() (unique_ptr.h:184)
==3894==    by 0x4007A9: main (test.cpp:19)
==3894==  Address 0x5a1a040 is 0 bytes inside a block of size 16,000 alloc'd
==3894==    at 0x4C2AFE7: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-AMD64-linux.so)
==3894==    by 0x40075F: main (test.cpp:15)
42
lauw

Verwenden der T[]-Spezialisierung:

std::unique_ptr<unsigned char[]> testData(new unsigned char[16000]());

Beachten Sie, dass Sie in einer idealen Welt new nicht explizit verwenden müssen, um einen unique_ptr zu instanziieren, um eine potenzielle Ausnahme-Sicherheitsfalle zu vermeiden. Zu diesem Zweck stellt Ihnen C++ 14 die Funktionsvorlage std::make_unique zur Verfügung. Siehe diese ausgezeichnete GOTW für weitere Details. Die Syntax lautet:

auto testData = std::make_unique<unsigned char[]>(16000);
61
juanchopanza

Verwenden Sie die Array-Version:

auto testData = std::unique_ptr<unsigned char[]>{ new unsigned char[16000] };

Oder mit c ++ 14 eine bessere Form (VS2013 hat es schon):

auto testData = std::make_unique<unsigned char[]>( 16000 );
25
galop1n

Die wahrscheinlich bessere Möglichkeit wäre, stattdessen std::vector<unsigned char> zu verwenden

#include <vector>
#include <string>

using namespace std;

int main()
{
    vector<unsigned char> testData(0x12, 0); // replaces your memset
    // bla    
}

Der Vorteil ist, dass dies weniger fehleranfällig ist und Sie auf alle Arten von Funktionen zugreifen können, z. B. einfache Iteration, Einfügung und automatische Neuzuweisung, wenn die Kapazität erreicht ist. 

Es gibt eine Einschränkung: Wenn Sie Ihre Daten viel herumbewegen, kostet ein std::vector etwas mehr, da er nicht nur den Anfang der Daten, sondern auch Größe und Kapazität nachverfolgt.

Hinweis: Ihre memset tut nichts, weil Sie sie mit einem Null-Zähler-Argument aufrufen.

14
TemplateRex

Scheint wie ein Trottel, ich werde erklären, was ich meine

class Object {
private :
    static int count;
public :
    Object() {
        cout << "Object Initialized " << endl;
        count++;
    }
    ~Object() {
        cout << "Object destroyed " << endl;
    }
    int print()
    {
        cout << "Printing" << endl;
        return count;
    }
};

int Object::count = 0;

int main(int argc,char** argv)
{
    // This will create a pointer of Object
    unique_ptr<Object> up2 = make_unique<Object>();  
    up2->print();
    // This will create a pointer to array of Objects, The below two are same. 
    unique_ptr<Object[]> up1 = std::make_unique<Object[]>(30);
    Object obj[30];
    cout << up1.get()[8].print();
    cout << obj[8].print();

    // this will create a array of pointers to obj. 
        unique_ptr<Object*[]> up= std::make_unique<Object*[]>(30);
        up.get()[5] = new Object();
        unique_ptr<Object> mk = make_unique<Object>(*up.get()[5]);
        cout << up.get()[5]->print();

        unique_ptr<unique_ptr<Object>[]> up3 =  std::make_unique<unique_ptr<Object>[]>(20);
        up3.get()[5] = make_unique<Object>();

    return 0;
}

Ziel des Beitrags ist, dass es versteckte kleine subtile Dinge gibt, die Sie verstehen müssen. Das Erstellen eines Arrays von Objekten entspricht dem Objektarray von unique_ptr. Es wird nur dann einen Unterschied machen, wenn Sie es im Argument übergeben. Das Erstellen eines Arrays von Objektzeigern von unique_ptr ist ebenfalls nicht sehr nützlich. In den meisten Szenarien müssen Sie also nur zwei Werte verwenden.

unique_ptr<Object> obj;
//and 
unique_ptr<unique_ptr<Object>[]>= make_unique<unique_ptr<Object>[]>(20);
0
unsigned int size=16000;
std::unique_ptr<unsigned char[], std::default_delete<unsigned char[]>> pData(new unsigned char[size]);
0
Competent Bit