Lassen Sie uns einige Verwendungen und Probleme mit einem Q & A-Format organisieren.
Q1: Ich möchte zum Speichern eines Zeiger zu einer Klasse Component
in meiner Klasse X
.
Ich möchte nicht die "Container" -Klasse X
kopierbar sein; X
ist der einzige Besitzer von Component
Instanzen.
Ich weiß, dass besitzenroh Zeiger eine schlechte Sache und mögliche Quellen von „leaktrocities“ sind (statt beobachtet rohe Zeiger sind in Ordnung). Was Smart-Zeiger könnte ich für diesen Zweck verwenden?
A1: C++ 11 des std::unique_ptr
ist sicherlich eine gute Wahl.
Es ist gut, in Fällen von einzigartigen (nicht-shared) Eigentum, und nicht über den Overhead von std::shared_ptr
.
Es ist ein ausgezeichneter Ersatz für vorherige C++ 98/03 boost::scoped_ptr
.
In der Tat bietet std::unique_ptr
Bewegung Semantik.
Also, wenn Klasse X
enthält unique_ptr<Component>
Datenelemente (und andere bewegliche Datenelemente), wird die gesamte Klasse X
automatisch beweglich sein.
Ein Beispiel für die Verwendung ist unten gezeigt:
#include <memory> // for std::unique_ptr
class X
{
std::unique_ptr<Component> m_pComponent;
....
public:
X()
: m_pComponent(new Component())
{
....
}
}
(. Natürlich ist ein Smart Zeiger, gibt es keine Notwendigkeit, explizit in der destructor enthaltenden Klasse löschen)
Q2: Das ist großartig! Keine Notwendigkeit für explizite Destruktor, keine std::shared_ptr
Overhead (typische C++ Philosophie: "Wir zahlen nicht für Dinge, die wir nicht verwenden"), bewegen Semantik-Maschinen bereits implementiert!
Allerdings habe ich ein Problem: meine Klasse Component
hat eine Konstruktorüberladung, die einige Parameter, die ich im Konstruktorcode vor dem Erstellen von Component
Instanzen berechnen muss, dauert.Ich habe versucht, in Konstruktor gewöhnlichen operator=
assigment mit den neu geschaffenen Component
zum unique_ptr
zuweisen, aber ich bekam eine Fehlermeldung:
X::X()
{
....
const int param = CalculateCoolParameter();
// This assignment fails:
m_pComponent = new Component(param); // <---- Error pointing to '=' here
^--- error
}
A2: OK, haben Sie wahrscheinlich eine operator=
Überlastung erwartet hatten die vorherige Freigabe im Besitz Zeiger (falls vorhanden) und Zuweisung zu dem neu erstellten.
Leider gibt es keine solche Überlastung.
Allerdings wird die std::unique_ptr::reset()
Methode tun!
m_pComponent.reset(new Component(param));
Q3: Hey! Diese unique_ptr
ist wirklich cool! Ich mag die Tatsache, dass es schlau ist, es ist automatisch beweglich und bringt keinen Overhead.
Also würde ich es gerne verwenden, um eine dynamisch zugewiesenen Array von einiger konstanter Größe (berechnet zur Laufzeit) statt std::vector
zu speichern (in diesem Teil des Codes bin ich stark eingeschränkt und ich nicht will für die std:vector
Overhead bezahlen, da ich nicht alle std::vector
dynamisch Größe ändern Funktionen, Deep-Copy, etc.) wollen.
Ich habe versucht, so etwas wie dieses:
const size_t count = GetComponentsCount();
unique_ptr<Component> components(new Component[count]);
Es kompiliert gut, aber ich stellte fest, dass ~Component
destructor nur einmal genannt wird, statt count
destructor Anrufe ich erwartet hatte! Was läuft hier falsch?
A3: Das Problem ist, dass mit der obigen Syntax, std::unique_ptr
verwendet delete
die zugeordneten Objekte freizugeben. Da diese jedoch unter Verwendung von new[]
zugeteilt wurden, ist der richtige Bereinigungsaufruf delete[]
(nicht der einfache delete
ohne Klammern).
unique_ptr<Component[]> components(new Components[count]);
// ^^
//
// Note brackets "[]" after the first occurrence of "Component"
// in unique_ptr template argument.
//
Q4: Das ist großartig
das zu beheben und unique_ptr
zu instruieren richtig delete[]
für die Freigabe Ressourcen zu verwenden, muss die folgende Syntax verwendet werden! Aber kann ich unique_ptr
auch in Fällen, in denen der Code Ressourcenfreigabe ist nicht mit normaler C durchgeführt ++ delete
(oder delete[]
), sondern mit einiger benutzerdefinierten Bereinigungsfunktion, wie fclose()
für C <stdio.h>
Dateien (geöffnet mit fopen()
) oder CloseHandle()
für Win32-Datei HANDLE
s (erstellt mit CreateFile()
)?
A4: Das ist auf jeden Fall möglich: Sie benutzerdefinierte deleter für std::unique_ptr
angeben.
z.B.:
//
// Custom deleter function for FILE*: fclose().
//
std::unique_ptr<FILE, // <-- the wrapped raw pointer type: FILE*
int(*)(FILE*)> // <-- the custom deleter type: fclose() prototype
myFile(fopen("myfile", "rb"), // <-- resource (FILE*) is returned by fopen()
fclose); // <-- the deleter function: fclose()
//
// Custom deleter functor for Win32 HANDLE: calls CloseHandle().
//
struct CloseHandleDeleter
{
// The following pointer typedef is required, since
// the raw resource is HANDLE (not HANDLE*).
typedef HANDLE pointer;
// Custom deleter: calls CloseHandle().
void operator()(HANDLE handle) const
{
CloseHandle(handle);
}
};
std::unique_ptr<HANDLE, CloseHandleDeleter> myFile(CreateFile(....));
FWIW, was bezahlen Sie für die Vektorfunktionen, die Sie nicht verwenden? (Hinweis: nichts) –
Zumindest aus einem Speicher-Footprint kann 'std :: vector' 3 Zeiger verwenden, statt 'unique_ptr' nur einen. –
A2: eine bessere Lösung, wenn möglich, ist eine Methode, die die Berechnung durchführt und die std :: unique_ptr zurückgibt, und diese dann in der Initialisierungsliste verwendet. – stijn