Wie Sie am besten mit C++ - Objekt-Initialisierung: leere Konstruktoren oder Hinweise?
Frage ich mich, was ist der beste Weg zu gehen über Objekt-Initialisierung und-Speicherung mit Bezug auf Objekte, die haben eine relativ große Reichweite /lange Lebensdauer. Sagen wir, wir haben ein GameEngine
Klasse muss initialisiert werden, und einen Verweis auf eine Window
für das rendering. Die Referenz ist notwendig, während des Programms, die Lebenszeit und das Fenster muss wissen, seine Dimensionen, mindestens.
In Java, ich würde es so machen:
//Declaration:
Window window;
//Initialization:
window = new Window(width, height);
Habe ich verstanden, dass in C++, die ersten würden bereits die Standard-Konstruktor der Fenster-Klasse, daher werden Erklärung und Initialisierung. Mit einem window = Window(width, height);
wäre daher Zuordnung, wegwerfen die bereits bestehenden Objekt.
Die erste Lösung, die ich finden konnte, war die Verwendung eines Zeigers:
//GameEngine.hpp
class GameEngine {
Window *window;
};
//Somewhere in GameEngine.cpp:
window = new Window(width, height);
Aber dann wieder, ich ständig Lesen sollte man zugunsten plain-Objekte über Zeiger, wenn immer möglich und in der Tat, ich habe mich in ein Chaos von Zeigern in kürzester Zeit, so ich bin auf der Suche nach einem anderen Weg.
Eine andere Lösung scheint für die Gestaltung Ihrer Objekte haben einen Konstruktor ohne Parameter ein, und richten Sie das Objekt, das später auf:
//GameEngine.hpp
class GameEngine {
Window window;
};
//Somewhere in GameEngine.cpp
window.setWidth(width);
window.setHeight(height);
Diese funktioniert, hat aber einen gravierenden Nachteil: das Objekt (zumindest in diesem Fall) könnte in einem inkonsistenten Zustand, als zu versuchen, um das Fenster aufzurufen, ohne die Einstellung der Breite/Höhe führen zu einem Fehler oder Absturz. Es funktioniert für einige Objekte, aber für die meisten gibt es nicht.
Ein Weg, um dies zu vermeiden, wäre die default-Werte. Zum Beispiel wird der Konstruktor der Fenster-Klasse könnte wie folgt Aussehen:
Window::Window(int width = 800, int height = 600) {}
Oder auch wie diese:
Window::Window() : width(DEFAULT_WIDTH), height(DEFAULT_HEIGHT) {}
Aber in vielen Fällen default-Werte schwer zu ermitteln. Außerdem, wo sollte Sie herkommen? Sollten die Fenster-Klasse definieren DEFAULT_WIDTH
und DEFAULT_HEIGHT
? Oder sollte ich noch tun?
//GameEngine.hpp
class GameEngine {
static const int DEFAULT_WIDTH = 800;
static const int DEFAULT_HEIGHT = 600;
Window window(800,600);
};
Aber das scheint schlecht, als ich gelesen habe, dass Sie nicht tun sollten jede Initialisierung in der Kopfzeile nur die Erklärung, also die Werte von DEFAULT_WIDTH
und DEFAULT_HEIGHT
sollten nicht eigentlich bekannt sein, an dieser Stelle (und nur initialisiert werden, in der .cpp, richtig?).
Vermisse ich eine option? Oder ist es üblich in C++ zu vermuten, dass der Programmierer sollte wissen, was er tut und kümmert sich seine Objekte in einem konsistenten Zustand, bevor Sie Sie verwenden? , Wann welcher Ansatz?
- abstimmen - dies sieht nicht wie eine Meinung in Frage für mich. Zwar ein bisschen schlecht formuliert (wahrscheinlich Mangels Verständnis) die Frage ist wirklich über die technischen Aspekte der Sprache C++.
- Ich denke, es ist zu Fragen, wie man ein zwei-Phasen-Initialisierung
- Ich fühle mich wie ich bin etwas fehlt; gibt es einen Grund, warum Sie nicht erstellen kann die Window-Objekt und initialisiert es in der gleichen Zeit? Sie müssen die Deklaration und die Initialisierung in verschiedenen Orten? Weil wenn nicht, dann einfach schließen Sie die Initialisierung in der Deklaration, Erklärung und es ist kein problem.
- Huh. Ich dachte, die Frage war etwa, wie Sie vermeiden, tun zwei-Phasen-Initialisierung.
- stellen Sie sich zum Beispiel, dass wir nicht wissen, die Erzeugung der Parameter für das Fenster, doch wenn wir ein Spiel erstellen
- lassen Sie mich anders formulieren... er hat zwei-Phasen-Initialisierung in Java und er kann gar nichts arbeiten, in C++ (damit er entweder muss C++ zwei-Phasen-Initialisierung, oder erfahren Sie mehr über jamming alles in den Konstruktor init-Liste (was IMHO nicht immer angemessen ist)).
- Von was ich gelesen habe, im Idealfall ist die .hpp sollten alle die Erklärung, während die .cpp hat das wirkliche Fleisch (Initialisierung, Umsetzung) in es. Vielleicht mehr Klarheit auf, dass ist in der Tat erforderlich. Sorry, bin ein blutiger C++ Anfänger.
- und Cubic: ich versuche zu vermeiden, zwei-Phasen, wie ich es verstanden würde, dann erstellen Sie das Objekt zweimal (von denen der erste werden weggeworfen). Zur gleichen Zeit, ich versuche, nicht zu über-Zeiger verwenden.
- zwei-Phasen-Initialisierung bedeutet, dass Sie initialisiert einige Dinge, die während der Bauphase und dann initialisieren Sie einige Dinge später. Sie beschreiben die Initialisierung und dann Zuordnung, die eine Möglichkeit der Implementierung von zwei-phase; aber nicht der einzige Weg. Meine Antwort zeigt eine andere Weise, die nicht die Zuweisung. Es gibt keine einzige richtige Antwort auf diese Frage , es ist eine design-Entscheidung, die basiert in der Regel auf Erfahrung! Also ich würde Ihnen raten, einfach etwas tun, und dann werden Sie lernen, die vor-und Nachteile der es, indem Sie sehen, wie Ihr Projekt stellt sich heraus.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wenn Sie möchten, erstellen Sie nur einmal und es kann getan werden in der Initialisierung der Klasse, dann brauchen Sie nicht ein Zeiger. Sie deklarieren es als Mitglied und initialisieren Sie im Konstruktor wie folgt:
HPP
CPP
Diese bauen das window-Objekt, wenn Sie bauen das Spiel-Objekt und es wird bestehen bleiben, bis das Spiel-Objekt zerstört wird. Wenn Sie wollen in der Lage sein, um es zu konstruieren oder später rekonstruieren zu jeder Zeit, verwenden Sie dann ein std::unique_ptr etwa so:
HPP
CPP
Diese wird automatisch löschen Sie die Fenster, wenn das Spiel-Objekt zerstört wird und automatisch löschen Sie die Fenster jedes mal, wenn Sie rufen Sie std::make_unique ein neues zu bauen. Hier finden Sie einige doc auf unique_ptr:
http://en.cppreference.com/w/cpp/memory/unique_ptr
class GameEngine { Window window; }
würde schon rufen Sie den Window ctor, weilWindow window;
tun würde, dass, wenn Sie auftreten würde, wie das in einigen-Funktion, richtig? Aber die Tatsache, dass Sie nicht, rufen Sie den ctor hier und ich kann Sie initialisieren das Objekt in der GameEngine ist member-Initialisierungs-Liste ist in der Tat eine befriedigende Lösung für die meisten Fälle. Danke für die Hilfe mit einem Anfänger-Verwirrung!window
im ctor vonGame
? Wäre es noch nicht initialisierte oder würde der compiler-Aufruf der default-ctor vonWindow
?Du bist anscheinend Missverständnis C++. Sie hätte nie
Window window;
einfach so in einem header. Definiert ein Window-Objekt jedes mal die header enthalten ist !Haben Sie möglicherweise
class GameEngine { Window window; .... }
aber nicht, dass tatsächlich Kiste ein Fenster überhaupt. Jeder GameEngine Konstruktor hat eine Initialisierungsliste, und da Sie initialisierenwindow
. Macht Sinn: die Spiel-engine erstellt, die Fenster, die es braucht.Window window;
wäre innerhalb der definition der Klasse, natürlich. Ich werde entsprechend bearbeitet. Das würde dann richtig sein, obwohl, Recht?Wenn Sie reden über die Schüler, dann die Erklärung ist nicht den gleichen Punkt der Konstruktor aufgerufen. Die Initialisierung solcher Mitglieder ist genau das, was Initialisierer-Listen (was Sie zu wissen scheinen) sind für!
und
Dann können Sie rufen Sie den Konstruktor der Fenster-Klasse aus dem Spiel Konstruktor in etwa so:
In Fall, dass Sie redeten über globals: Wenn Sie wirklich brauchen ein globales Objekt (obwohl Sie wahrscheinlich nicht wollen, dass) Sie können (und sollten!) erklären Sie das Objekt mit externer Bindung in den header (die nur die Namen zur Verfügung, aber nicht aufrufen, Konstruktoren), und führen Sie die definition in der Umsetzung:
Erklärung:
Umsetzung:
Im Idealfall würden Sie Ihre design-Klassen, so dass alle die Initialisierung, die Sie benötigen, kann in den Konstruktor. Beispiel hier.
Aber dies ist nicht immer möglich (z.B. wenn Sie möchten, ein Fenster, das nicht erstellt, bis einige bestimmte Ereignis passiert, während das Spiel); oder es kann schwer sein, wickeln Sie Ihren Kopf um, als ein neuer Programmierer.
Ein Ansatz ist die Verwendung von Zeigern - aber ein smart-pointer statt einer raw-pointer.
Wenn Ihre Klasse enthalten muss ein Objekt behandelt, jedoch sind Sie nicht bereit, das Objekt zu erstellen, noch, dann, Sie können eine Klasse Mitglied:
Dann, wenn Sie bereit sind, um das Fenster zu erstellen, führen Sie den code:
Den smart-pointer kümmert sich um den Aufruf
delete
wenn das zugehörige Objekt gelöscht wird, und es gibt einen compile-Fehler, wenn Sie versehentlich versuchen, eine "flache Kopie".Verwenden Sie den Mauszeiger, sobald es gerichtet ist, irgendwo würden Sie schreiben
p_window->bla...
, und um zu überprüfen, ob er zugeordnet wurde, aber Sie könnenif ( p_window )
.Die option, die Sie ' re fehlt ist die Initialisierung
Window
Objekte, wenn Sie erstellt. Nicht erklärenWindow
Objekte in Ihren Funktionen, bevor Sie wissen, wie Sie zu initialisieren. Wenn Sie ein Objekt mit einemWindow
Mitglieder, den Konstruktor für das Objekt initialisieren desWindow
Mitglied.Zeiger sind in Ordnung und das richtige zu tun, wenn der Zeitpunkt der Erzeugung eines Objekts wirklich unbestimmt ist, oder Sie wirklich brauchen, um eine variable deklarieren, bevor Sie bereit sind, erstellen Sie ein gültiges Objekt.
Dem Punkt, der die Ratschläge, die Sie erwähnen, ist nicht zu ändern, wie Sie design-Objekte, aber Sie brauchen, um zu überdenken, wie Sie verwenden der Objekte: Sie müssen sich abgewöhnen, Gewohnheiten, die Sie abgeholt haben von alles-ist-ein-Zeiger-Programmierumgebungen wie Java.
(obwohl Sie sollten die Verwendung von smart Pointern, wie
unique_ptr
odershared_ptr
als angemessen)(auch, wenn Sie glauben, eine Klasse wird ziemlich viel müssen immer verwendet werden, mit Zeigern, ist es sinnvoll, eine wrapper-Klasse um den Zeiger, der wirkt wie ein "leeres Objekt", obwohl seine umgesetzt mit einem Zeiger innen)