Argumentloser Konstruktor
-
Hi,
sorry ich dachte da es um keinen konkreten Fehler als viel mehr ein Kompiler-Verhalten geht wäre Code nicht notwendig. Hier mal kurz ein minimal Code:in header:
class A { public: int a; A(int a_); }; class B { public: A a; B(A a_); };
in source file:
A::A(int a_) { a = a_; } B::B(A a_) { a = a_; }
Fehler (by vs):
Fehler (aktiv) E0291 Für die Klasse ""A"" ist kein Standardkonstruktor vorhanden.
Der Fehler tritt in B::B(A a_) auf bzw. wird dort gezeigt.
Lösung des Fehlers durch Einführung eines attributlosen Konstruktors (in source and header):A::A() { a = 1; // dummy value }
Frage:
Warum genau ist das auf Seite des Kompilers notwendig einen attributlosen Konstruktor zu haben? (Sofern das überhaupt notwendig ist und ich hier nicht etwas falsch verstanden habe)
-
Im Header hast du geschrieben:
@NPC sagte in Argumentloser Konstruktor:
B(A a_);
das heißt, jedes Mal, wenn eine Instanz von B erstellt wird, wird versucht, die Instanz von A zu kopieren, was aber scheitert, da A keinen parameterlosen Konstruktor hat.
Workaround: Nimm ne Referenz.
-
@NoIDE
Ah dankeschön. Ja das macht natürlich absolut sinn :).@wob jetzt verstehe ich deinen Kommentar auch. Auch an dich ein Dankeschön :).
Viele Grüße
NPC
-
Ich bin mir aber nicht zu 100 % sicher, dass die Fehlermeldung/Warnmeldung auch tatsächlich daran liegt. Habe das lediglich logisch hergeleitet. Für mich ergibt das Sinn.
-
@NoIDE Die Fehlermeldung ist nach der Verwendung einer Referenz verschwunden und der Code läuft anstandslos. Daher sieht es gut aus
-
Anstatt eines Workarounds mit Referenz kann man das besser so machen:
B::B(A a_): a(a_) { }
Dann wird
a
direkt mit dem richtigen Ctor erstellt und man braucht keinen leeren Ctor um in ein leeres Objekt rein zu kopieren.P.S. Wie sieht denn dein Code mit Referenz aus?
-
Dieser Beitrag wurde gelöscht!
-
@NoIDE sagte in Argumentloser Konstruktor:
Im Header hast du geschrieben:
@NPC sagte in Argumentloser Konstruktor:
B(A a_);
das heißt, jedes Mal, wenn eine Instanz von B erstellt wird, wird versucht, die Instanz von A zu kopieren, was aber scheitert, da A keinen parameterlosen Konstruktor hat.
Workaround: Nimm ne Referenz.
Sorry, aber das ist falsch.
Bei der Kopie von A wird der (implizite) KopierkonstruktorA(const A&)
aufgerufen und an den Konstruktor übergeben.
Bei jedem Konstruktor, also auch beiB::B(A a_) { a = a_; }
werden zuerst die Member default-initialisiert (bevor der Methodenblock ausgeführt wird), sofern man keine Initialisierungsliste verwendet, d.h. hierfür würde dann der Standardkonstruktor benötigt.
Und selbst bei einer Parameterübergabe als Referenz käme es, ohne die Initialisierungsliste, zu der Fehlermeldung!@NoIDE: Du solltest zuerst C++ richtig erlernen, bevor du irgendwelche Mutmaßungen anstellst.
@NPC: Hast du den Member
a
der KlasseB
als Referenz erstellt (alsoA& a
)?
-
Hi @Th69,
vielleicht habe ich das mit der Referenz falsch verstanden aber ich habe nun einfach einen Pointer zu dem breits erstellten Object von A übergeben (in der header-Datei natürlich entsprechend geändert).
B::B(A* a_){ }
und das läuft durch.
Edit: Nvm von A& a habe ich verstanden was du mit Referenz meinst... Keine Ahnung ob das klappt
-
@NPC sagte in Argumentloser Konstruktor:
vielleicht habe ich das mit der Referenz falsch verstanden aber ich habe nun einfach einen Pointer zu dem breits erstellten Object von A übergeben
Ist ja schön, dass es funktioniert, aber die Parameterübergabe sollte so sein, wie es semantisch sinnvoll ist. Pointer erlaubt es auch,
nullptr
zu übergeben. Willst du das? Und die zweite Frage ist: wenn du den Pointer als Member vonB
speichern solltest (und ihn nicht nur im Konstruktur verwendest), dann ist das ein Rezept für Unglück, weil du dich dann um Ownership kümmern müsstest. Also: willst du wirklich einen Pointer übergeben?
-
Hi @wob,
ich wollte nur korrigieren was ich mit der Aussage zu der Referenz meinte. (Offensichtlichtlich fehlt mir Erfahrung im Sprachgebrauch der Cpp community und ich habe Referenz falsch verwendet).
Wie genau sähe dann eine sinnvolle Übergabe aus? Ist es dann dieser Ansatz:
B::B(A a_): a(a_) { }
von @Schlangenmensch?
Wenn ja wäre es schön, wenn du mir erklären könntest was genau hier passiert bzw a(a_) bedeutet. (Oder heist das ich muss ein Konstruktor für A(A: a) schreiben?)Eine erzeugung von A in B kommt leider eigentlich nicht in Frage. A ist ein relativ großes Objekt mit dem ich ein bisschen Arbeite bevor ich es an B weitergebe (weshalb ich eine Kopie von A vermeiden möchte). B braucht dann einige Methoden von A für seine Arbeit.
Viele Grüße
NPC
-
Die logische Frage ist: soll das Objekt
a
in der KlasseB
unabhängig von dem übergebenen Objekta_
arbeiten? Wenn nicht, d.h. du hast ein anderes Objekt, das mindestens eine so lange Lebenszeit wie das erzeugteB
-Objekt hat, dann kannst du diese als Referenz oder Zeiger übergeben (und auch als Member so speichern). Evtl. nochconst
setzen, falls du nur lesend auf die (ebenfallsconst
) Member zugreifen möchtest (Stichwort: const correctness).Und zu
: a(a_)
habe ich dir oben schon das Stichwort (sowie den Link) "Initialisierungsliste" gegeben - dies ist ein wichtiges Feature bei Erstellen von Klassenkonstruktoren!
-
@NPC Da tauchen ja noch mehr Fragen auf...
Was heißt denn "großes Objekt"?
In meinem Ansatz wird der Member
a
mit dem impliziet erstellten Copy Ctor erstellt. Tatsächlich wird der sogar 2xmal aufgerufen, das erste mal beim Aufruf des Ctors und das zweite mal bei der Erstellung des Members.Das kann man auf eine Kopie reduzieren, wenn man z.B. eine const Referenz nutzt
B::B(const A& a_):a(a_){}
(das ist, was ich eigentlich machen würde)Bei deinem Ansatz machst du aber auch zwei Kopien, einmal bei der Übergabe im Ctor und einmal bei der Zuweisung.
Wenn das Objekt wirklich groß ist, dann kommt eventuell tatsächlich ein Pointer oder eine Referenz in Frage, oder eine universal Referenz und ein Move. Das kommt dann auf die genaue Ausgestaltung an und hat auch seine Fallstricke.
Vielleicht ist auch dein Design mit den beiden Klassen eher schlecht gewählt, aber auch das kommt auf das konkrete Problem an.
Tatsächlich glaube ich, solange du dir Gedanken um solche Probleme machen musst, sollte Performance erstmal nicht dein Hauptaugenmerk sein.
-
Hi @Th69,
a soll nicht unabhängig von a_ arbeiten. D.h., wenn ich dich richtig verstehe, eine übergabe als Referenz oder Zeiger ist ok.
Mit Initialisierungslisten werde ich mich dann jetzt wohl mal beschäftigen xD.
Viele Grüße
NPC
-
Hi @Schlangenmensch,
d.h., dass das Objekt exponentiell mit einem Parameter wächst. Die konkrete Basis ist abhängig von einem anderen Parameter und spielt eigentlich auch keine Rolle. Das Objekt wird am Ende gut und gerne 20-30 Gb groß (Es liegt sehr nah an einem auf einem Blatt errechneten Mindestgröße die aus dem Problem selbst folgt). Dabei arbeitet A im wesentlichen als, schon komprimierter [wieder auf dem Blatt], Container.
Leider muss ich mich, der Aufgabe wegen, hier auf die Performance fokusieren.
Die Frage die sich sofort stellt ist natürlich die wie sehr ich auf Performance schauen kann, wenn mir dergleichen Eigenschaften unbekannt sind. Das ist (hoffentlich) weniger kritisch als es scheint. Mein Ursprüngliches Problem lag an einer misinterpretation der Übergabe selbst. Mir war nicht bewusst, dass ich nicht automatisch einen Pointer übergebe, wenn ich ein Objekt übergeben will. Da bin ich wohl aus anderen Programmiersprachen zu sehr verwöhnt.Viele Grüße
NPC
-
@Th69 sagte in Argumentloser Konstruktor:
Bei der Kopie von A wird der (implizite) Kopierkonstruktor A(const A&) aufgerufen und an den Konstruktor übergeben.
Bei jedem Konstruktor, also auch bei
B::B(A a_) {
a = a_;
}
werden zuerst die Member default-initialisiert (bevor der Methodenblock ausgeführt wird),Bin mir nicht sicher, ob das nicht das ist, was ich sagte, wenn nicht gar widersprüchlich...
@NPC Nimm
B(A & a_)
und fertig. In den meisten Fällen möchte man das haben. Der Aufrufer muss sich dann aber darum kümmern.
-
@NoIDE sagte in Argumentloser Konstruktor:
@NPC Nimm B(A & a_) und fertig. In den meisten Fällen möchte man das haben
Naja, "meistens" will man ...
const A&
... mitconst
haben. Man muss aber in C++ schon immer wissen, was man tut, gerade was Ownership und Parameterübergabe angeht. Und man kann schlecht ohne genaue Kenntnis des Problems sagen "nimm A" oder "nimm B". Vielleicht will man eine Kopie machen, vielleicht sogar einenshared_ptr
nehmen, wer weiß.
-
@NoIDE Und fertig? Zeig mal, wie die kurze Klasse dann bei dir aussehen würde?
-
@NoIDE sagte in Argumentloser Konstruktor:
Bin mir nicht sicher, ob das nicht das ist, was ich sagte, wenn nicht gar widersprüchlich...
Es wird aber keine Instanz von A kopiert, sondern der Member wird initialisiert.
-
Wenn ich da jetzt etwas hin klatsche, von dem ich glaube, dass es richtig ist, da ich die genauen Anforderungen ja nicht kenne, dann ist ihm ja nicht weitergeholfen.
Ich denke, einige vergessen hier gerne, dass auch sie mal klein angefangen haben.