std::vector
verwaltet den Speicher wie immer für Sie, aber dieser Speicher besteht aus Zeigern, nicht aus Objekten.
Dies bedeutet, dass Ihre Klassen im Speicher verloren gehen, sobald Ihr Vektor den Gültigkeitsbereich verlässt. Zum Beispiel:
#include <vector>
struct base
{
virtual ~base() {}
};
struct derived : base {};
typedef std::vector<base*> container;
void foo()
{
container c;
for (unsigned i = 0; i < 100; ++i)
c.push_back(new derived());
}
int main()
{
foo();
}
Sie müssen lediglich sicherstellen, dass Sie alle Objekte löschen, bevor der Vektor den Gültigkeitsbereich verlässt:
#include <algorithm>
#include <vector>
struct base
{
virtual ~base() {}
};
struct derived : base {};
typedef std::vector<base*> container;
template <typename T>
void delete_pointed_to(T* const ptr)
{
delete ptr;
}
void foo()
{
container c;
for (unsigned i = 0; i < 100; ++i)
c.push_back(new derived());
std::for_each(c.begin(), c.end(), delete_pointed_to<base>);
}
int main()
{
foo();
}
Dies ist jedoch schwierig aufrechtzuerhalten, da wir uns daran erinnern müssen, eine Aktion auszuführen. Noch wichtiger ist, wenn eine Ausnahme zwischen der Zuweisung von Elementen und der Freigabeschleife auftreten würde, würde die Freigabeschleife niemals ausgeführt und Sie stecken trotzdem mit dem Speicherverlust fest! Dies wird als Ausnahmesicherheit bezeichnet und ist ein kritischer Grund, warum die Freigabe automatisch erfolgen muss.
Besser wäre es, wenn sich die Zeiger selbst löschen würden. Diese werden als intelligente Zeiger bezeichnet, und die Standardbibliothek bietet std::unique_ptr
und std::shared_ptr
.
std::unique_ptr
stellt einen eindeutigen Zeiger (nicht freigegeben, Einzelbesitzer) auf eine Ressource dar. Dies sollte Ihr standardmäßiger intelligenter Zeiger sein und die vollständige Verwendung aller Rohzeiger vollständig ersetzen.
auto myresource = make_unique<derived>();
std::make_unique
fehlt im C ++ 11-Standard durch Versehen, aber Sie können selbst einen erstellen. Gehen Sie folgendermaßen vor, um ein direkt zu erstellen unique_ptr
(nicht empfohlen, make_unique
wenn Sie können):
std::unique_ptr<derived> myresource(new derived());
Eindeutige Zeiger haben nur eine Bewegungssemantik. Sie können nicht kopiert werden:
auto x = myresource;
auto y = std::move(myresource);
Und das ist alles, was wir brauchen, um es in einem Container zu verwenden:
#include <memory>
#include <vector>
struct base
{
virtual ~base() {}
};
struct derived : base {};
typedef std::vector<std::unique_ptr<base>> container;
void foo()
{
container c;
for (unsigned i = 0; i < 100; ++i)
c.push_back(make_unique<derived>());
}
int main()
{
foo();
}
shared_ptr
hat eine Referenzzählkopiesemantik; Es ermöglicht mehreren Eigentümern, das Objekt gemeinsam zu nutzen. Es verfolgt, wie vieleshared_ptr
s für ein Objekt existieren, und wenn das letzte nicht mehr existiert (diese Anzahl geht auf Null), gibt es den Zeiger frei. Durch das Kopieren wird lediglich die Referenzanzahl erhöht (und durch das Verschieben wird das Eigentum zu geringeren, fast kostenlosen Kosten übertragen). Sie erstellen sie mit std::make_shared
(oder direkt wie oben gezeigt, aber da shared_ptr
interne Zuweisungen vorgenommen werden müssen, ist die Verwendung im Allgemeinen effizienter und technisch ausnahmsicherer make_shared
).
#include <memory>
#include <vector>
struct base
{
virtual ~base() {}
};
struct derived : base {};
typedef std::vector<std::shared_ptr<base>> container;
void foo()
{
container c;
for (unsigned i = 0; i < 100; ++i)
c.push_back(std::make_shared<derived>());
}
int main()
{
foo();
}
Denken Sie daran, dass Sie im Allgemeinen std::unique_ptr
standardmäßig verwenden möchten, da es leichter ist. Zusätzlich std::shared_ptr
kann konstruiert aus einem werden std::unique_ptr
(aber nicht umgekehrt), so dass es in Ordnung ist , klein zu beginnen.
Alternativ können Sie einen Container verwenden, der zum Speichern von Zeigern auf Objekte erstellt wurde, z boost::ptr_container
:
#include <boost/ptr_container/ptr_vector.hpp>
struct base
{
virtual ~base() {}
};
struct derived : base {};
typedef boost::ptr_vector<base> container;
void foo()
{
container c;
for (int i = 0; i < 100; ++i)
c.push_back(new Derived());
}
int main()
{
foo();
}
Während boost::ptr_vector<T>
dies in C ++ 03 offensichtlich verwendet wurde, kann ich jetzt nicht über die Relevanz sprechen, da wir es std::vector<std::unique_ptr<T>>
mit wahrscheinlich geringem bis keinem vergleichbaren Overhead verwenden können, aber diese Behauptung sollte getestet werden.
Ungeachtet, niemals explizit Dinge in Ihrem Code frei . Packen Sie alles zusammen, um sicherzustellen, dass das Ressourcenmanagement automatisch erledigt wird. Sie sollten keine rohen Besitzzeiger in Ihrem Code haben.
Als Standard in einem Spiel würde ich wahrscheinlich mit gehen std::vector<std::shared_ptr<T>>
. Wir erwarten sowieso, dass das Teilen schnell genug ist, bis die Profilerstellung etwas anderes sagt, es sicher ist und einfach zu verwenden ist.