Forum: PC-Programmierung C++ : Constructor aufruf aus Copyconstructor


von Papa (Gast)


Lesenswert?

Morgen.
ich habe das wohl ein prinzipielles Verständnisproblem. Ich habe einen 
Constructor in dem arrays allokiert werden. Im copyconstructor soll die 
selben arrays allokiert werden. Anstatt copy&paste zu verwenden möchte 
ich den Constructor aus dem Copyconstrutor aufrufen.

Warum kann ich den Constructor nicht wie unten (unter "geht nicht") 
aufrufen. Es compiliert und ich kann in den Constructor debuggen. Nach 
dem zurücksprungen in den Copyconstrcuctor beinhalten sind die attribute 
m_pPOI und m_PWaypoint keine gültigen Adressen, die aber im Constructor 
gesetzt werden.
Im ersten Fall funktioniert es, also was genau ist hier der Unterschied?
Oder ist es einfach ein Compilerproblem da nicht offensichtlich ist dass 
die pointer verändert werden?
1
CRoute::CRoute(int maxWp, int maxPOI){
2
    [...]
3
    m_pPOI = new CPOI*[m_maxPOI];
4
    m_pWaypoint = new CWaypoint[m_maxWp];
5
}
6
7
// geht, m_pPOI und m_pWaypoint sind gültige pointer
8
CRoute::CRoute(const CRoute & origin) : CRoute::CRoute(origin.m_maxWp,origin.m_maxPOI) {
9
   [...] 
10
}
11
12
//geht nicht, m_pPOI und m_pWaypoint sind ungültige pointer
13
CRoute::CRoute(const CRoute & origin) {
14
    CRoute(origin.m_maxWp,origin.m_maxPOI); 
15
   [...] 
16
}

von Ronny S. (duselbaer)


Lesenswert?

Dein erstes Beispiel verwendet sog. delegate-constructors und ist erst 
mit C++11 valide. Vorher ging das auch nicht.

Deine zweite Variante legt auf dem lokalen Stack im Constructor ein 
zweites Objekt vom Typ CRoute an. Da es keiner Variablen zugewiesen 
wird, wird es danach gleich wieder gelöscht (temporary).

Ist genauso wie:

   std::string("Hallo");

zu schreiben.

Du hast mal Java programmiert, richtig?

Grüße,

RSp

von Papa (Gast)


Lesenswert?

Ronny Spiegel schrieb:
> Dein erstes Beispiel verwendet sog. delegate-constructors und ist
> erst
> mit C++11 valide. Vorher ging das auch nicht.
Das ist mir nicht aufgefallen, wie hätte ich es vorher gemacht?

> Deine zweite Variante legt auf dem lokalen Stack im Constructor ein
> zweites Objekt vom Typ CRoute an. Da es keiner Variablen zugewiesen
> wird, wird es danach gleich wieder gelöscht (temporary).
>
> Ist genauso wie:
>
>    std::string("Hallo");
>
> zu schreiben.
Ok, d.h. es kann nicht als Funktionsaufruf des Constructors 
interpretiert werden? Kann man ihn dazu zwingen?

> Du hast mal Java programmiert, richtig?
Naja eigentlich nicht. Habs mal gelernt aber das is lange her.

von Ronny S. (duselbaer)


Lesenswert?

Papa schrieb:
> Ronny Spiegel schrieb:
>> Dein erstes Beispiel verwendet sog. delegate-constructors und ist
>> erst
>> mit C++11 valide. Vorher ging das auch nicht.
> Das ist mir nicht aufgefallen, wie hätte ich es vorher gemacht?

z.B. mit privaten init-Methoden, die vom jeweiligen Konstruktor gerufen 
werden. Finde ich persönlich unschön. Braucht man ja jetzt gottseidank 
nicht mehr - sofern der Arbeitgeber einen vernünftigen Compiler einsetzt 
:)

>> Deine zweite Variante legt auf dem lokalen Stack im Constructor ein
>> zweites Objekt vom Typ CRoute an. Da es keiner Variablen zugewiesen
>> wird, wird es danach gleich wieder gelöscht (temporary).
>>
>> Ist genauso wie:
>>
>>    std::string("Hallo");
>>
>> zu schreiben.
> Ok, d.h. es kann nicht als Funktionsaufruf des Constructors
> interpretiert werden? Kann man ihn dazu zwingen?

Nein.

>> Du hast mal Java programmiert, richtig?
> Naja eigentlich nicht. Habs mal gelernt aber das is lange her.

Ist nämlich in Java völlig normal, Constructors so zu bauen.

Grüße,

RSp

von Karl H. (kbuchegg)


Lesenswert?

Die eigentlich wichtigere Frage ist aber, warum du der Ansicht bist, die 
Arrays selbst allokieren zu müssen, anstatt Standard-Container 
einzusetzen, wie zb. einen std::vector?

Die kümmern sich selbst um diese Details wie Copy Constructor oder auch 
Zuweisungsoperator und Destructor. Und was noch viel wichtiger ist: die 
vergrößern sich auch bei Bedarf, so dass du bei deiner Route im Vorfeld 
gar nicht wissen musst, wieviele Points of Interest bzw. Waypoints es 
geben wird bzw. kann.

: Bearbeitet durch User
von Papa (Gast)


Lesenswert?

Ronny Spiegel schrieb:
> Papa schrieb:
>> Ronny Spiegel schrieb:
>>> Dein erstes Beispiel verwendet sog. delegate-constructors und ist
>>> erst
>>> mit C++11 valide. Vorher ging das auch nicht.
>> Das ist mir nicht aufgefallen, wie hätte ich es vorher gemacht?
>
Ok danke, dann is mir das soweit klar.


> z.B. mit privaten init-Methoden, die vom jeweiligen Konstruktor gerufen
> werden. Finde ich persönlich unschön. Braucht man ja jetzt gottseidank
> nicht mehr - sofern der Arbeitgeber einen vernünftigen Compiler einsetzt
> :)
Ja gut daran hab ich auch gedacht. Es geht hier aber nicht um die 
Funktionalität sondern um das Verständnis.

Karl Heinz schrieb:
> Die eigentlich wichtigere Frage ist aber, warum du der Ansicht
> bist, die
> Arrays selbst allokieren zu müssen, anstatt Standard-Container
> einzusetzen.
>
> Die kümmern sich selbst um diese Details wie Copy Constructor oder auch
> Zuweisungsoperator. Und was noch viel wichtiger ist: die vergrößern sich
> auch bei Bedarf, so dass du bei deiner Route im Vorfeld gar nicht wissen
> musst, wieviele Points of Interest bzw. Waypoints es geben wird bzw.
> kann.

Es ist die Aufgabenstellung ;-) Is fürs Studium. Malen nach Zahlen (C++ 
Programmieren nach Vorgabe). Im Prinzip kann ich programmieren, nur mein 
Stil ist mehr funktional C als Objektorientier C++, deswegen kam ich nie 
in die Verlegenheit mit solchem Copyconstructor Bla bla gerade WEIL ich 
sonst stark für die STL-Container und Libraries bin.

von Karl H. (kbuchegg)


Lesenswert?

Papa schrieb:

>> Die kümmern sich selbst um diese Details wie Copy Constructor oder auch
>> Zuweisungsoperator. Und was noch viel wichtiger ist: die vergrößern sich
>> auch bei Bedarf, so dass du bei deiner Route im Vorfeld gar nicht wissen
>> musst, wieviele Points of Interest bzw. Waypoints es geben wird bzw.
>> kann.
>
> Es ist die Aufgabenstellung ;-) Is fürs Studium.

Dann ist das ok.
Es schadet nicht, wenn man selbst da mal (ein paar mal) durch musste und 
sich um die dynamischen Allokierungen in allen Details selbst kümmert. 
Zu Ausbildungszwecken.

> Malen nach Zahlen (C++
> Programmieren nach Vorgabe). Im Prinzip kann ich programmieren, nur mein
> Stil ist mehr funktional C als Objektorientier C++, deswegen kam ich nie
> in die Verlegenheit mit solchem Copyconstructor Bla bla gerade WEIL ich
> sonst stark für die STL-Container und Libraries bin.

Sehr gut. Dann will ich nichts gesagt haben. :-)

Wichtig: wenn man es ganz richtig machen will, dann wird das recht 
heftig. Denn dann muss man sich im Konstruktor auch um fehlgeschlagene 
Allokierungen kümmern und dann kann das recht schnell ausarten. Daher 
wird auch oft die Devise ausgegeben: Pro Klasse nur EINE dynamisch 
verwaltete Resource. Das würde dann in deinem Fall dazu führen, dass du 
für den Teil 'Verwaltung einer Menge von PoI in Form von Pointern auf 
die richtigen PoI' eine eigene Klasse einführst UND dasselbe für die 
Waypoints.
d.h. deine Klassen sehen so aus
1
class CPOIPtrArray
2
{
3
  ...
4
};
5
6
class CWaypointArray
7
{
8
  ...
9
};
10
11
class CRoute
12
{
13
   ....
14
15
  private:
16
    CPOIPtrArray    m_POIs;
17
    CWayPointArray  m_Waypoints;
18
};

Nebeneffekt: die CRoute Klasse braucht jetzt wieder keinen CCtor, keinen 
ZuweisungsOp und auch keinen Destructor. Dafür handeln die 
Verwaltungsklassen diese Details wieder für sich selbst.
Unterschätze sowas nicht, denn diese Veraltungsklassen können sich im 
weiteren Verlauf nämlich wieder als nützlich erweisen. Zb ein einer 
Zeichenfunktion die so ein WaypointArray bekommt. Dann kannst du in 
diese Zeichenfunktion zb das WaypointArray aus einer Route reingeben, du 
kannst aber auch 'on-the-fly' ein WaypointArray zb aus gelesenen 
GPS-Koordinaten zusammenstellen und diese dann mit derselben 
Zeichenfunktion malen lassen OHNE zuvor eine Route erzeugt haben zu 
müssen.

So ein paar Guide-Lines für die OOP:
Durchdenk deine Aufgabenstellung genau, durchaus schon ein bischen bis 
in die Details: Alles was dir beim 'Niederschreiben' als Hauptwort 
unterkommt, ist meistens ein heißer Kandidat für eine Klasse.
Klassen sind faul. Sie tun nichts lieber als Teilprobleme an andere 
Klassen zu delegieren.
Bei OOP dreht sich sehr viel um Zuständigkeiten. Du musst dir immer die 
Frage stellen: Ist diese Klasse für die Funktionalität überhaupt 
zuständig. Ist es ihr Bier dieses oder jenes zu berechnen, oder ist da 
nicht eigentlich eine andere Klasse (zb der Member eines Objekts) dafür 
zuständig? In dem Fall delegiert dann ein Objekt sofort diese 
Funktionalität an seinen Member. Gibt es keinen derartigen Member, dann 
könnte sowas ein Hinweis darauf sein, dass einer ins System einzuführen 
ist.

: Bearbeitet durch User
von Papa (Gast)


Lesenswert?

Ich hätte das sowieso anders gemacht. Das is eine alberne pointer, 
reference, array und bla geschubserei. Zweck der Sache ist es mit 
Pointern und Pointer auf Pointern usw zu arbeiten.
Sinnvoll finde ich die ganze Architektur nur bedingt, aber mich fragt ja 
eh keiner ;-)


Aber es bringt mich (wieder einmal) zu dem Punkt dass ich zugeben muss, 
wirklich objektorientiert programmieren kann ich bis heute nicht. Ich 
drifte immer wieder in prozeduale Muster ab und vewende Klassen meist 
als intelligente Funktionen.

von Karl H. (kbuchegg)


Lesenswert?

Papa schrieb:

> Aber es bringt mich (wieder einmal) zu dem Punkt dass ich zugeben muss,
> wirklich objektorientiert programmieren kann ich bis heute nicht. Ich
> drifte immer wieder in prozeduale Muster ab und vewende Klassen meist
> als intelligente Funktionen.

Ja, das dauert.
Ich würde sagen, ich hab so 3 bis 4 Jahre gebraucht, bis ich OOP 
komplett verinnerlicht habe.
Also jetzt nicht einfach nur 'class' hinschreiben und die Funktionen 
rutschen in die class hinein. Sondern die ganze Thematik: wie baut man 
ein Klassensystem auf, wie benutzt man es, wann unterteil man in 
Klassen, Zuständigkeiten, wie arbeitet man mit Klassen etc. etc.
Am Anfang gings mir wie dir: ich sah OOP einfach nur als eine 
Zusammenfassung von struct+Funktionen und im Grunde habe ich C++ so 
programmiert, wie auch in C. Mit der Zeit (und mit einem Kollegen) hab 
ich mich mehr in Richtung OOP entwickelt, was dann dazu führte, dass wir 
uns verzettelt haben und viel zu viele Klassen auf das Problem geworfen 
haben. Im Laufe der Jahre entwickelt man dann ein Gefühl dafür, was 
sinnvoll ist und wo der Overkill anfängt und das System nicht mehr 
einfacher wird, sondern unübersichtlicher und komplexer.

Geholfen hat mir zb viel das Studium von Scott Meyers "Effective C++". 
Dann natürlich die GoF "Design Patterns" und vom Herb Sutter mit seiner 
regelmässigen "Guru of the week" Kolumne hab ich auch viel gelernt, 
obwohl es da oft mehr um irgendwelche Details ging.

: Bearbeitet durch User
von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Ronny Spiegel schrieb:
> Ist nämlich in Java völlig normal, Constructors so zu bauen.

Also erst mal gibt es in Java keinen "Copy-Konstruktor" und zweitens 
würde man da entweder ein 'super' oder ein 'this' benötigen und ganz 
sicher kein neues Objekt erstellen. Das wiederum würde in JS 
funktionieren.

von Ronny S. (duselbaer)


Lesenswert?

Läubi .. schrieb:
> Ronny Spiegel schrieb:
>> Ist nämlich in Java völlig normal, Constructors so zu bauen.
>
> Also erst mal gibt es in Java keinen "Copy-Konstruktor" und zweitens

1. Hab ich das nicht behauptet, oder?
2. Könnte man einen bauen, der aber explizit aufgerufen werden muss

> würde man da entweder ein 'super' oder ein 'this' benötigen und ganz

Syntaktische Feinheiten hab ich mal aussen vor gelassen, zumal ich in 
Java auch nicht mehr so fit bin.

Es ging einfach nur darum, dass Konstruktoren an andere Konstruktoren 
delegieren können. Das ist ein Feature, was es in Java schon (immer?) 
gibt und dort auch entsprechend benutzt wird. Daher wird das 
erfahrungsgemäß oft von Java-Entwicklern beim Umstieg auf C++ 
eingesetzt, mit dem Erfolg, dass der erzeugte Code nicht wie gewünscht 
funktioniert.

Grüße,

Rooney

von cybmorg (Gast)


Lesenswert?

Was in C++ leider auch nicht sehr offensichtlich ist: Die Konstruktoren 
machen auch die Initialisierung der vtable. Das ist fuer den Entwickler 
aus seinem Code heraus nicht ersichtlich - bis er auf Ideen wie den 
manuellen Aufruf von Konstruktoren kommt. Damit kann man sich schoen die 
schon gemacht Initialisierung wieder zerschiessen.

von Rolf Magnus (Gast)


Lesenswert?

cybmorg schrieb:
> Was in C++ leider auch nicht sehr offensichtlich ist: Die Konstruktoren
> machen auch die Initialisierung der vtable. Das ist fuer den Entwickler
> aus seinem Code heraus nicht ersichtlich - bis er auf Ideen wie den
> manuellen Aufruf von Konstruktoren kommt.

Es ist eher umgekehrt: Man kann Konstruktoren ganz einfach nicht manuell 
aufrufen. Man kann nur Objekte erzeugen, und als Teil der Erzeugung wird 
der Konstruktor automatisch aufgerufen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.