Was ist der richtige Weg, um ein unique_ptr zu erstellen, das ein Array enthält, das dem freien Speicher zugewiesen ist? Visual Studio 2013 unterstützt dies standardmäßig, aber wenn ich gcc Version 4.8.1 unter Ubuntu verwende, treten Speicherlecks und undefiniertes Verhalten auf.
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:
==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)
std::fill
.Antworten:
Verwendung der
T[]
Spezialisierung:std::unique_ptr<unsigned char[]> testData(new unsigned char[16000]());
Beachten Sie, dass Sie in einer idealen Welt nicht explizit verwenden müssen, um a
new
zu instanziierenunique_ptr
, um eine potenzielle Sicherheitslücke für Ausnahmen zu vermeiden. Zu diesem Zweck stellt Ihnen C ++ 14 diestd::make_unique
Funktionsvorlage zur Verfügung. Weitere Details finden Sie in diesem ausgezeichneten GOTW . Die Syntax lautet:auto testData = std::make_unique<unsigned char[]>(16000);
quelle
unique_ptr
vonT[]
geschaffen , wie oben gezeigt, rufen Sie den richtigen deleter (dhdelete []
) auf den Daten? Ich habe irgendwo ein Tutorial gesehen, in dem ein benutzerdefinierter Löscher angegeben wurde, daher frage ich mich, ob dies (noch) erforderlich ist oder ob alles ordnungsgemäß gelöscht wird.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 sie bereits):
auto testData = std::make_unique<unsigned char[]>( 16000 );
quelle
make_unique
ist nicht immer besser als Konstruktor Plusnew
. Dies bedeutet, dass für Anwendungsfälle, in denen keine Wertinitialisierung erforderlich ist und vermieden werden muss, da dies leistungskritisch ist, die Konstruktorversion besser ist. Beachten Sie, dassauto testData = unique_ptr<unsigned char[]> { new unsigned char[16000]() ];
dies dermake_unique
Version entspricht.Ein wahrscheinlich besserer Weg wäre,
std::vector<unsigned char>
stattdessen 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 viel weniger fehleranfällig ist und Sie Zugriff auf alle Arten von Funktionen wie einfache Iteration, Einfügung und automatische Neuzuweisung bei Erreichen der Kapazität erhalten.
Es gibt eine Einschränkung: Wenn Sie Ihre Daten viel verschieben,
std::vector
kostet dies etwas mehr, da nicht nur der Anfang der Daten, sondern auch die Größe und Kapazität erfasst werden.Hinweis: Sie
memset
tun nichts, weil Sie es mit einem Nullzählungsargument aufrufen.quelle
std::vector
ist zweifellos weniger fehleranfällig als die manuelle Speicherverwaltung. Aber wenn das OP genug weiß, um a zu verwendenstd::unique_ptr
, würde ich sagen, dass der Vektor kein klarer Gewinner mehr ist. Es hat wahrscheinlich mindestens etwas mehr als das Dreifache des Speicherbedarfs. Was zählen kann, wenn Sie es beispielsweise im leistungskritischen Code als Wert (Verschieben) übergeben.unique_ptr
und jetzt auf modernes C ++ "upgraden" möchte. Ihr Leistungsargument ist gut bekannt, sollte aber nur für den kritischsten Code von Bedeutung sein.unique_ptr
plusmemset
ist fehleranfällig, insbesondere wenn dies auf Nicht-POD-Typen verallgemeinert wird.std::unique_ptr
. Wenn Sie möchten , kopieren Sie die Daten dann kann man eine leichte (winzige) Overhead , aber Sie auch die Möglichkeit bekommen zu bewegen , die Daten gegebenenfalls.std::vector
ist dreimal so groß wie astd::unique_ptr
, aber es zahlt sich in Bezug auf Bequemlichkeit und Datenkapselung aus.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 es, dass es versteckte kleine subtile Dinge gibt, die Sie verstehen müssen. Das Erstellen eines Objektarrays 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. Daher müssen Sie in den meisten Szenarien nur unter zwei verwenden.
unique_ptr<Object> obj; //and unique_ptr<unique_ptr<Object>[]>= make_unique<unique_ptr<Object>[]>(20);
quelle
unsigned int size=16000; std::unique_ptr<unsigned char[], std::default_delete<unsigned char[]>> pData(new unsigned char[size]);
quelle
Wahrscheinlich so etwas wie das Folgende?
using namespace std; int size = get_size(); int const init_value = 123; unique_ptr<int[]> p = make_unique<int[]>(size) fill(p.get(), p.get() + size, init_value);
quelle