C++ in Konstruktor einen Konstruktor aufrufen und Adresse übergeben

EueRolando

Cadet 2nd Year
Registriert
Aug. 2014
Beiträge
29
Hallo,

ich möchte ein bestehendes Programm etwas verändern. Und zwar möchte ich ein Objekt erzeugten, wenn ich ein anderes Objekt gerade erzeuge. Also im Konstruktor eines Objekts den Konstruktor eines anderen Objekts aufrufen. Dieser Konstruktor des anderen Objekts soll dann auch gleich noch die Adresse des Aufrufers übergeben bekommen. Es soll aber nicht mit Vererbung gemacht werden.

Code:
class CKlasseA{
public:
     CKlasseA(void);
};

Code:
class CKlasseB{
public:
     CKlasseA* origin;
     CKlasseB(CKlasseA*);
};

Code:
CKlasseA::CKlasseA(void){
     //Variableninitialisierung
     CKlasseB(this)
}

CKlasseB::CKlasseB(CKlasseA* pCKA){
     //Variableninitialisierung
     origin=pCKA;
}
 
Und wo ist jetzt das Problem? Genau das tut dein Code doch auch, auch wenn du lediglich ein "temporary" CKlasseB-Objekt anlegst, das sofort wieder zerstört wird.
 
Konstruktoraufruf sofern kein Aufruf eines Basisklassenkonstrutktors oder eines Constructorchainings innerhalb der eigenen Klasse immer nur mit
PHP:
new
.

Greetz
hroessler
 
hroessler schrieb:
Konstruktoraufruf sofern kein Aufruf eines Basisklassenkonstrutktors oder eines Constructorchainings innerhalb der eigenen Klasse immer nur mit
PHP:
new
.

Nö. Das geht auch genau so ohne new.

Code:
CKlasseA anInstanceOfClassA; // <- Default-Konstruktor von CKlasseA wird ausgeführt
 
Wie antred schon sagte, macht der Code eigentlich schon das, wonach du fragst, aber ich gehe mal davon aus, dass du so ein Objekt auch irgendwo speichern willst:

Code:
class CKlasseA {
public:
  CKlasseA()
  : _b(this) { }
private:
  CKlasseB _b;
};

Member _b wird also noch während der Initialisierung eines CKlasseA-Objekts mit dessen Pointer als Konstruktor-Parameter initialisiert.

Du musst allerdings dringend darauf achten, dass der Pointer nicht ungültig wird.'Wenn man jetzt sowas Tolles macht wie...
Code:
CKlasseA a;
a = CKlasseA();
fliegst du ganz schnell auf die Schnauze, weil der Pointer a._b.origin jetzt auf ein bereits zerstörtes Objekt zeigt. Also in solchen Fällen den Copy-Assignment-Operator und Copy-Constructor überladen oder löschen.

Edit:
hroessler schrieb:
Konstruktoraufruf [...] immer nur mit
PHP:
new
.
Wir sind hier nicht bei Java. :p
 
Zuletzt bearbeitet:
Also das stimmt natürlich, die Objekte sollen nicht sofort wieder gelöscht werden. Das war wirklich etwas fahrlässig von mir. Allerdings klappt es nicht ganz. Ich arbeite für jede Klasse mit je einer .h und einer .cpp Datei. Diese wiederrum binde ich "gegeseitig" mit include ein, da liegt wohl iwo der Fehler begraben.

KlasseA.h
Code:
#pragma once
#include "KlasseB.h"

class CKlasseA
{
public:
	CKlasseA();
	~CKlasseA();
private:
	CKlasseB _b;
};

KlasseA.cpp
Code:
#include "KlasseA.h"


CKlasseA::CKlasseA() :_b(this)
{
}


CKlasseA::~CKlasseA()
{
}

KlasseB.h
Code:
#pragma once
#include "KlasseA.h"

class CKlasseB
{
public:
	CKlasseB(CKlasseA* pCKA);
	~CKlasseB();
private:
	CKlasseA* origin;
};

KlasseB.cpp
Code:
#include "KlasseB.h"


CKlasseB::CKlasseB(CKlasseA* pCKA)
{
	origin = pCKA;
}


CKlasseB::~CKlasseB()
{
}

....wenn E-Techniker programmieren wollen...
 
Zwei Header können sich, wie du schon erkannt hast, nicht gegenseitig inkludieren. Das ist aber auch gar nicht nötig. Statt in KlasseB.h den Header KlasseA.h zu inkludieren, kannst du hier einfach mit einer Vorwärtsdeklaration arbeiten. Also:

KlasseB.h
Code:
#pragma once

class CKlasseA; // <- forward declaration

class CKlasseB
{
public:
	CKlasseB(CKlasseA* pCKA);
	~CKlasseB();
private:
	CKlasseA* origin;
};

Die Vorwärtsdeklaration sagt dem Compiler quasi "Ich werde weiter unten einen Typ erwähnen, den du noch nicht kennst. Du mußt an dieser Stelle nicht wissen, wie er aussieht. Du mußt nur wissen, dass es ihn gibt."

NACHTRAG: Die volle Definition von CKLasseA muß nur an Stellen bekannt sein, die auch wirklich was mit CKLasse tun möchten (z.B. eine Methode an einer Instanz von CKlasseA aufrufen). Da müßtest du dann also KlasseA.h inkludieren.
 
Zuletzt bearbeitet:
GEILO!! Vielen Dank! Auch wenns so'ne Halbwissen-Wie-Macht-Man-Das Frage war!

Der Abend ist gerettet, vielen Dank euch!
Ergänzung ()

Okee. Ist auf jeden Fall grundsätzlich lauffähig aber für meinen Fall funktioniert es nicht. Im Tabellenkopf sind jeweils die Dateinamen der Header meiner verwendeten Klassen und in den Zeilen darunter die Dateien, die darin inkludiert werden.

Also hier geht noch alles, da aus allen Headern saubere definitionen machen kann.
ChildView.hSzenario.hRaumschiff.hIntegrator.h
#Szenario.h#Raumschiff.h#Szenario.h
#Integrator.h


Jetzt kommt meine Veränderung, die Klasse Raumschiffe soll ein Objekt der Klasse Integrator als Member enthalten. Dafür muss Raumschiffe die Definition vom Integrator kennen. Das schafft er nicht mehr, da sich die Definition "im Kreis dreht", soweit habe ich das verstanden.

ChildView.hSzenario.hRaumschiff.hIntegrator.h
#Szenario.h#Raumschiff.h#Integrator.h#Szenario.h
#Integrator.h

Gibt es da eine Lösung? Ich habe schon geschaut, die Dateien benötigen wirklich alle die definitionen voneinander.
 
Zuletzt bearbeitet:
Bist du 100%-ig sicher, dass die Header wirklich jeweils die volle Definition der in den anderen Headern definierten Typen benötigen? Meines Erachtens brauchst du die volle Definition eigentlich nur, wenn du auf Member (Daten oder Methoden) der Klassen zugreifen möchtest oder eine Membervariable des Typs anlegen willst. Wenn ein Typ lediglich in einer Funktionssignatur erwähnt wird, reicht immer eine Vorwärtsdeklaration, selbst wenn es sich nicht um eine Referenz oder einen Pointer handelt.

Beispiel:

Code:
ClassA myFunc( ClassB instanceOfClassB ); // forward-declarations of ClassA and ClassB will suffice here
 
Zuletzt bearbeitet:
Das funktioniert tatsächlich, siehe http://stackoverflow.com/questions/553682/when-can-i-use-a-forward-declaration

Den Header musst du dann nur in der cpp-Datei inkludieren.

Abgesehen davon sollte sich der TE mal die Initialisierungsliste von Konstruktoren angucken, statt alles im Funktionsrumpf zu initialisieren.

Poste mal die Header, sonst können wir dir nicht sagen, was der Fehler ist oder wie man ein include einsparen kann.
 
Zuletzt bearbeitet:
Also, jetzt übersetzt es auch in meinem speziellen Fall. Linker stürzt aber ab (VS2008). Hier die Originalversion

ChildView.h
Code:
// ChildView.h : Schnittstelle der Klasse CChildView
//
/////////////////////////////////////////////////////////////////////////////

#if !defined(AFX_CHILDVIEW_H__1B25472D_1AF4_433A_BCBD_FA383CDBEB7F__INCLUDED_)
#define AFX_CHILDVIEW_H__1B25472D_1AF4_433A_BCBD_FA383CDBEB7F__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000


#ifndef SZENARIO_H
#include "Szenario.h"
#endif

#ifndef INTEGRATOR_H
#include "Integrator.h"
#endif


/////////////////////////////////////////////////////////////////////////////
// CChildView-Fenster

class CChildView : public CWnd
{
// Konstruktion
public:
	CChildView();

// Attribute
public:
	int count,count2;//simulieren den Integrator
	int status;
	CSzenario szenario;
	CIntegrator integ;
	void SpielerAbfrage(int spielernr);
	void SpielEnde(int result);

// Operationen
public:

// Überladungen
	// Vom Klassen-Assistenten erstellte virtuelle Funktionsüberladungen
	//{{AFX_VIRTUAL(CChildView)
	protected:
	virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
	//}}AFX_VIRTUAL
	afx_msg void OnTimer(UINT nIDEvent); //Timer von Hand einbauen

// Implementierung
public:
	virtual ~CChildView();

	// Generierte Funktionen für die Nachrichtentabellen
protected:
	//{{AFX_MSG(CChildView)
	afx_msg void OnPaint();
	afx_msg void OnSpielKonfiguration();
	afx_msg void OnSpielStart();
	afx_msg void OnSpielSzenarioerzeugen();
	afx_msg void OnSpielTest();
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ fügt unmittelbar vor der vorherigen Zeile zusätzliche Deklarationen ein.

#endif // !defined(AFX_CHILDVIEW_H__1B25472D_1AF4_433A_BCBD_FA383CDBEB7F__INCLUDED_)

Szenario.h
Code:
//file Szenario.h

#ifndef SZENARIO_H
#define SZENARIO_H

#ifndef PLANET_H
#include "Planet.h"
#endif

#ifndef RAUMSCHIFF_H
#include "Raumschiff.h"
#endif


class CSzenario {
public:
	//DATEN
	int valide;		//Szenario Zulässig
	int np;//Anzahl, Planeten
	CPlanet *PlanetenArray;
	CRaumschiff Raumschiffe[2];
	double faktor;           //einstellungen
	int mindestabs;
	int xpix,ypix;
	double rmin,rmax;
	//Funktionen
	CSzenario(void);
	~CSzenario(void);
	int Erzeugen(void);
	void Anzeigen(CDC *pDC);
	void SetzeFenstergroesse(int nxpix, int nypix);
};



#endif

Raumschiff.h
Code:
//file Raumschiff.h

#ifndef RAUMSCHIFF_H
#define RAUMSCHIFF_H

class CRaumschiff {
public:
	double kantenlaenge;
	double x,y;
	COLORREF farbe;
	CString Name;
	CRaumschiff(void);
	~CRaumschiff(void);
	void SetData(double nx, double ny, COLORREF nfarbe);
	void GetData(double *nx, double *ny, double *nkantenlaenge);
	void Anzeigen(CDC *pDC);
};

#endif

Integrator.h
Code:
// file Integrator.h


#ifndef INTEGRATOR_H
#define INTEGRATOR_H

#ifndef SZENARIO_H
#include "Szenario.h"
#endif


class CIntegrator {
public:
	//daten
	CSzenario *pSz;
	double x,y,xalt,yalt;
	double dt,steplen,vmax;
	int nistep;
	int count,maxcount;
	int ImRohr;
	//functionen
	CIntegrator(void);
	~CIntegrator(void);
    void ComputeScalingData(void);
    int InnerStep(int &nx, int &ny);
	void Initialize(CSzenario *pnSz, double v0, double alpha,int RSindex);
	int Step(int& nxalt, int& nyalt, int &nx, int &ny);
	void GetForceVector(double& fx, double& fy);
	int RaumschiffGetroffen(void);
	int OutOfLimit(void);
	int PlanetGetroffen(void);
};



#endif

Es ist mir doch möglich gewesen, das #include Szenario.h aus dem Header von CIntegrator in eine Vorwärtsdeklaration zu ändern. Also sehen die geänderten Dateien jetzt so aus:

Raumschiff.h
Code:
//file Raumschiff.h

#ifndef RAUMSCHIFF_H
#define RAUMSCHIFF_H

#include "Integrator.h" //#include zwingend notwendig, da ein CIntegrator Member dieses Klasse wird

class CRaumschiff {
public:
	CIntegrator _integ; //Integrator als Memberobjekt der Raumschiffklasse
	double kantenlaenge;
	double x,y;
	COLORREF farbe;
	CString Name;
	CRaumschiff(void);
	~CRaumschiff(void);
	void SetData(double nx, double ny, COLORREF nfarbe);
	void GetData(double *nx, double *ny, double *nkantenlaenge);
	void Anzeigen(CDC *pDC);
};

#endif

Raumschiff.cpp
Code:
//file Raumschiff.cpp


#include "stdafx.h"
#include "Raumschiff.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif




CRaumschiff::CRaumschiff(void):_integ(this)//_integ wird Memberobjekt, wenn Konstruktor aufgerufen wird
{
	Name="Unbenannt";
	farbe=RGB(0,0,0);
	kantenlaenge=14;
	x=0.0;
	y=0.0;
}


CRaumschiff::~CRaumschiff(void){}



void CRaumschiff::Anzeigen(CDC *pDC)
{
}

Integrator.h
Code:
// file Integrator.h


#ifndef INTEGRATOR_H
#define INTEGRATOR_H

/*#ifndef SZENARIO_H //notwendig zu entfernen, da sich Header sonst (über drei Ecken) gegenseitig inkludieren
#include "Szenario.h"
#endif*/ 

class CSzenario; //eine Vorwärtsdeklaraion ist hier ausreichend (siehe weiter unten Pointer auf ein CSzenario)
class CRaumschiff; //eine Vorwärtsdeklaraion ist hier ausreichend (siehe weiter unten Pointer auf ein CRaumschiff)

class CIntegrator {
public:
	//daten
	CSzenario *pSz;
	CRaumschiff *origin; //das zum Integrator zugehörige Raumschiff als Zeiger
	double x,y,xalt,yalt;
	double dt,steplen,vmax;
	int nistep;
	int count,maxcount;
	int ImRohr;
	//functionen
	CIntegrator(void); //der Konstruktor, wenn der Integrator vom Szenario erzeugt wird
	CIntegrator (CRaumschiff* pCR); //der Konstruktor, wenn ein Raumschiff ein Integratormember erzeugt
	~CIntegrator(void);
    void ComputeScalingData(void);
    int InnerStep(int &nx, int &ny);
	void Initialize(CSzenario *pnSz, double v0, double alpha,int RSindex);
	int Step(int& nxalt, int& nyalt, int &nx, int &ny);
	void GetForceVector(double& fx, double& fy);
	int RaumschiffGetroffen(void);
	int OutOfLimit(void);
	int PlanetGetroffen(void);
};



#endif

Integrator.cpp
Code:
// file Integrator.cpp



#include "stdafx.h"
#include "Integrator.h"
#include <math.h>
#include "Szenario.h" //Teil der Vorwärtsdeklaration


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define PI 3.141592654



void errbreak(char* message)
{
	AfxMessageBox(message);
	AfxAbort();
};


CIntegrator::CIntegrator(void)
{
	origin=NULL; //damit origin(public) nicht irgendein Schaden macht, falls der void-Konstruktor aufgerufen wird
	pSz=NULL;
	nistep=10;
	steplen=5.0;
	count=0;
	maxcount=1500;
}

CIntegrator::CIntegrator(CRaumschiff* pCR)
{
	origin=pCR;
	pSz=NULL;
	nistep=10;
	steplen=5.0;
	count=0;
	maxcount=1500;
}

CIntegrator::~CIntegrator(void)
{
}
 
Zuletzt bearbeitet:
Was sagt denn der Linker? Der sollte ja irgendeine Meldung ausspucken.

Aber....
Code:
#if !defined(AFX_CHILDVIEW_H__1B25472D_1AF4_433A_BCBD_FA383CDBEB7F__INCLUDED_)
#define AFX_CHILDVIEW_H__1B25472D_1AF4_433A_BCBD_FA383CDBEB7F__INCLUDED_
 
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
Argh. Was ist das bitte für ein Schwachsinn? Wahrscheinlich hat das deine IDE so erzeugt, aber bei dem Konstantennamen rollen sich mir die Fußnägel und #pragma once ist weder Teil des C++-Standards, noch nötig, wenn man ohnehin schon Guards hat (#ifndef...#define...#endif).

Noch was:
Code:
#ifndef SZENARIO_H
#include "Szenario.h"
#endif
 
#ifndef INTEGRATOR_H
#include "Integrator.h"
#endif
Du musst die #includes nich noch einmal mit #ifndefs absichern. Das machen die Header-Dateien schon selbst den jeweils ersten Defines. Effektiv steht da ja jetzt folgendes, wenn man das #include auflöst:
Code:
#ifndef SZENARIO_H
#ifndef SZENARIO_H
// Code
#endif
#endif
...etwas redundant.

Einfach
Code:
#include "Szenario.h"
#include "Integrator.h"
und fertig.
 
Ja also das macht Sinn, was du schreibst. Also kommt das raus. Also das Buildprotokoll sieht so aus:

1>------ Neues Erstellen gestartet: Projekt: Space, Konfiguration: Debug Win32 ------
1>Die Zwischen- und Ausgabedateien für das Projekt "Space" mit der Konfiguration "Debug|Win32" werden gelöscht.
1>Kompilieren...
1>StdAfx.cpp
1> WINVER not defined. Defaulting to 0x0600 (Windows Vista)
1>Kompilieren...
1>ChildView.cpp
1>DlgKonfiguration.cpp
1>c:\users\flo\documents\studium\fh\6. fachsemester\objektor. progr. i. team\space\dlgkonfiguration.cpp(143) : warning C4996: 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
1> c:\program files (x86)\microsoft visual studio 9.0\vc\include\stdio.h(237): Siehe Deklaration von 'fopen'
1>DlgSchuss.cpp
1>Integrator.cpp
1>MainFrm.cpp
1>planet.cpp
1>Raumschiff.cpp
1>c:\users\flo\documents\studium\fh\6. fachsemester\objektor. progr. i. team\space\raumschiff.cpp(16) : warning C4355: this': wird in Initialisierungslisten für Basisklasse verwendet
1>Space.cpp
1>c:\users\flo\documents\studium\fh\6. fachsemester\objektor. progr. i. team\space\space.cpp(59) : warning C4996: 'CWinApp::Enable3dControls': CWinApp::Enable3dControls is no longer needed. You should remove this call.
1> c:\program files (x86)\microsoft visual studio 9.0\vc\atlmfc\include\afxwin.h(4818): Siehe Deklaration von 'CWinApp::Enable3dControls'
1>Szenario.cpp
1>c:\users\flo\documents\studium\fh\6. fachsemester\objektor. progr. i. team\space\szenario.cpp(23) : warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
1> c:\program files (x86)\microsoft visual studio 9.0\vc\include\stdio.h(366): Siehe Deklaration von 'sprintf'
1>Code wird generiert...
1>Ressourcen werden kompiliert...
1>Microsoft (R) Windows (R) Resource Compiler Version 6.0.5724.0
1>Copyright (C) Microsoft Corporation. All rights reserved.
1>Manifest in Ressourcen wird kompiliert...
1>Microsoft (R) Windows (R) Resource Compiler Version 6.0.5724.0
1>Copyright (C) Microsoft Corporation. All rights reserved.
1>Verknüpfen...
1>Das Manifest wird eingebettet...
1>Microsoft (R) Windows (R) Resource Compiler Version 6.0.5724.0
1>Copyright (C) Microsoft Corporation. All rights reserved.
1>LINK : fatal error LNK1000: Internal error during IncrBuildImage
1> Version 9.00.21022.08
1> ExceptionCode = C0000005
1> ExceptionFlags = 00000000
1> ExceptionAddress = 010AFCF7 (01030000) "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\link.exe"
1> NumberParameters = 00000002
1> ExceptionInformation[ 0] = 00000000
1> ExceptionInformation[ 1] = 00D6D670
1>CONTEXT:
1> Eax = 40131234 Esp = 0018ECA4
1> Ebx = 40008154 Ebp = 0018ECCC
1> Ecx = 00D6D670 Esi = 401310C8
1> Edx = 0018ECBC Edi = 0103D6C0
1> Eip = 010AFCF7 EFlags = 00010246
1> SegCs = 00000023 SegDs = 0000002B
1> SegSs = 0000002B SegEs = 0000002B
1> SegFs = 00000053 SegGs = 0000002B
1> Dr0 = 00000000 Dr3 = 00000000
1> Dr1 = 00000000 Dr6 = 00000000
1> Dr2 = 00000000 Dr7 = 00000000
1>Browseinformationsdatei wird erstellt...
1>Microsoft Browse Information Maintenance-Programm Version 9.00.21022
1>Copyright (C) Microsoft Corporation. All rights reserved.
1>Das Buildprotokoll wurde unter "file://c:\Users\Flo\Documents\Studium\FH\6. Fachsemester\Objektor. Progr. i. Team\Space\Debug\BuildLog.htm" gespeichert.
1>Space - 1 Fehler, 4 Warnung(en)
========== Alles neu erstellen: 0 erfolgreich, Fehler bei 1, 0 übersprungen ==========
 

Anhänge

  • Unbenannt.jpg
    Unbenannt.jpg
    60,9 KB · Aufrufe: 125
Gibts nen bestimmten Grund, warum du mit ner 7 Jahre alten IDE/Compiler arbeitest? Die 2015er (und ich glaube die 2013er) Version gibts umsonst - und natürlich alle möglichen anderen freien IDEs,die mit mingw arbeiten.

@VikingGe: #pragma once ist zwar nicht standardisiert, wird aber von allen großen Compilern unterstützt und ist mir persönlich sogar lieber als normale Include Guards, weil es genau das aussagt, was ich erreichen will: Die headerdatei file nur einmal zu includen und danach zu ignorieren.

Die zusätzlichen #ifdefs um die include Anweisungen hat man "früher" genutzt um die builds zu beschleunigen. Mit den Normalen include guards schaut der compiler ja (theoretisch) jedes mal wieder in die Datei, nur um dann festzustellen, dass er den Inhalt ignorieren kann. Die zusätzlichen #ifdefs sollen dafür sorgen, dass der Compiler die Datei erst garnicht ein zweites mal öffnet. Moderne Compiler machen das aber im Normalfall automatisch und #pragma once sorgt ebenfalls genau dafür.
 
Zuletzt bearbeitet:
Entschuldigt bitte, das Projekt ost aus persönlichen Gründen etwas eingeschlafen. Also ich arbeite zwingend mit der alten IDE. Hab auch die neue (wirklich hübsch gemacht MS :cheerlead:) aber darf das halt nicht. Ich wollte mich nochmal bedanken für eure hilfe. Das ganze Thema eröffnet mir völlig neue herangehensweisen. Vielen Dank.

EueRolando
 
Zurück
Oben