Frage zu Vererbung und Memberzugriff



  • Hallo, ich habe folgende struct erzeugt:

    struct Netz_Nachricht {
    	int groesse;
    	int MsgTyp;
    
    	Netz_Nachricht():MsgTyp(0) 
    
    	{ 
    		groesse = sizeof(Netz_Nachricht);
    	}
    };
    
    struct Konkrete_Nachricht1: Netz_Nachricht {
    
    	float wert;
    		Konkrete_Nachricht1	(float wert_){
            wert=wert_;
    		MsgTyp=1;
    		groesse = sizeof(Konkrete_Nachricht1);
    	}
    
    };
    

    Jetzt habe ich mir einen Zeiger vom Typ nachricht erzeugt, der Auf ein Objekt vom Typ Konkrete_Nachricht1 zeigt.

    Netz_Nachricht nachricht= new Konkrete_Nachricht1(7);
    

    Nun habe ich versucht den Wert aus der konkreten Nachricht auszugeben.

    cout <<"Wert: "<<	((Konkrete_Nachricht1)*nachricht).wert << endl;
    

    Hier kommt der Fehler:

    Typumwandlung': 'Netz_Nachricht' kann nicht in 'Konkrete_Nachricht1' konvertiert werden
    Quelltyp konnte von keinem Konstruktor angenommen werden, oder die Überladungsauflösung des Konstruktors ist mehrdeutig

    Aber ich habe doch ein Objekt Konkrete_Nachricht1!
    Wie kann ich auf "Wert" zugreifen?



  • dynamic_cast<Konkrete_Nachricht1&>(*nachricht).wert
    

    Aber genau um sowas nicht machen zu müssen, gibt es virtuelle Methoden!



  • Ich bin ein java Umsteiger 🙂

    Was sind denn Virtuelle Methoden?
    (Oder gibt es das in Java auch?)



  • Ich habe es jetzt so versucht:
    cout <<"Wert: "<< dynamic_cast<Konkrete_Nachricht1&>(*nachricht).wert << endl;

    Jetzt erhalte ich als fehler:

    error C2683: "dynamic_cast": "Netz_Nachricht" ist kein polymorpher Typ.
    c:\dokumente und einstellungen\ck\eigene dateien\visual studio 2005\projects\client\netz_nachricht.h(1): Siehe Deklaration von 'Netz_Nachricht'



  • Deine Netz-Nachricht braucht noch einen virtuellen Destruktor (oder eine beliebige andere, virtuelle Methode).

    Als Java-Umsteiger solltest Du Polymorphie kennen, denn IMHO sind unter Java alle Klassen per se polymorph (und alle Methoden virtuell) (kenne Java aber nicht sonderlich gut). In C++ muss das explizit angegeben werden.

    struct Netz_Nachricht {
      virtual ~Netz_Nachricht() {}
      virtual float gibFloatWert() = 0;
      /* ... */
    };
    
    struct Konkrete_Nachricht1 {
      virtual float gibFloatWert() { return wert; }
      /* ... */
    };
    


  • Kann mir BITTE jemand helfen und erklähren:

    Ich brauche 2 Klassen.
    Klasse1: Hat 2 Variablen x,y
    Klasse2: Erbt diese und hat eine weitere z

    Jetzt möchte ich einen Zeiger vom Typ 1 haben der auf ein Konkretes Objekt vom Typ 2 zeigt.

    Und nun möchte ich die Variable z ausgeben.
    (Ich bin von JAVA umgestiegen und dort wa das irgendwie nie ein Problem)



  • Danke, ich habe es jetzt von Strukt auf klassen geändert und habe es jetzt so:

    class Netz_Nachricht {
    
    public:
    	int groesse; 
    	int MsgTyp; 
    
    	virtual ~Netz_Nachricht() {} 
    
    	Netz_Nachricht():MsgTyp(0) 
        { 
            groesse = sizeof(Netz_Nachricht); 
        } 
    
    	virtual float gib_Wert() = 0; 
    
    	 int gib_groesse() 
    	{
    		return groesse;
    	}
    
    	 int gib_MsgTyp() 
    	{
    		return MsgTyp;
    	}
    };
    
    class Konkrete_Nachricht1:Netz_Nachricht{
    public:
    		float wert;
    
    		Konkrete_Nachricht1    (float wert_){ 
            wert=wert_; 
            MsgTyp=1; 
            groesse = sizeof(Konkrete_Nachricht1); 
        } 
    
    	float gib_Wert() 
    	{
    		return wert;
    	}
    };
    

    Aber es funktioniert vorne und hinten nicht:

    Siehe Deklaration von 'Netz_Nachricht::gib_Wert'
    c:\dokumente und einstellungen\ck\eigene dateien\visual studio 2005\projects\client\client.cpp(41) : error C2243: 'Typumwandlung': Konvertierung von 'Konkrete_Nachricht1 *' zu 'Netz_Nachricht *' ist bereits vorhanden, aber es kann nicht darauf zugegriffen werden.
    c:\dokumente und einstellungen\ck\eigene dateien\visual studio 2005\projects\client\client.cpp(53) : error C2440: 'Typumwandlung': 'Netz_Nachricht *' kann nicht in 'Konkrete_Nachricht1' konvertiert werden
    Quelltyp konnte von keinem Konstruktor angenommen werden, oder die Überladungsauflösung des Konstruktors ist mehrdeutig
    c:\dokumente und einstellungen\ck\eigene dateien\visual studio 2005\projects\client\client.cpp(53) : error C2227: Links von "->gib_groesse" muss sich ein Zeiger auf Klassen-/Struktur-/Union-/generischen Typ befinden.
    c:\dokumente und einstellungen\ck\eigene dateien\visual studio 2005\projects\client\client.cpp(54) : error C2440: 'Typumwandlung': 'Netz_Nachricht *' kann nicht in 'Konkrete_Nachricht1' konvertiert werden
    Quelltyp konnte von keinem Konstruktor angenommen werden, oder die Überladungsauflösung des Konstruktors ist mehrdeutig
    c:\dokumente und einstellungen\ck\eigene dateien\visual studio 2005\projects\client\client.cpp(54) : error C2227: Links von "->gib_MsgTyp" muss sich ein Zeiger auf Klassen-/Struktur-/Union-/generischen Typ befinden.
    c:\dokumente und einstellungen\ck\eigene dateien\visual studio 2005\projects\client\client.cpp(55) : error C2440: 'Typumwandlung': 'Netz_Nachricht *' kann nicht in 'Konkrete_Nachricht1' konvertiert werden
    Quelltyp konnte von keinem Konstruktor angenommen werden, oder die Überladungsauflösung des Konstruktors ist mehrdeutig
    c:\dokumente und einstellungen\ck\eigene dateien\visual studio 2005\projects\client\client.cpp(55) : error C2227: Links von "->gib_Wert" muss sich ein Zeiger auf Klassen-/Struktur-/Union-/generischen Typ befinden.



  • Vererbe doch mal public, dann sollte die Umwandlung funktionieren.



  • MisterX schrieb:

    ...
    Klasse2: Erbt diese und hat eine weitere z

    Jetzt möchte ich einen Zeiger vom Typ 1 haben der auf ein Konkretes Objekt vom Typ 2 zeigt.

    Und nun möchte ich die Variable z ausgeben....

    Wie oben schon erwähnt, ist das fachlich gesehen ein "downcast" ... und das macht man prinzipiell in Java genauso wie in C++. In C++ sollte man dafür (wie oben schon erwähnt) dynamic_cast verwenden .... allerdings auf Zeiger und nicht auf Objekte (ist aber auch in Java nicht anders).

    struct A {
        int x,y;
    };
    
    struct B : public A {
        int z;
    };
    
    int main(void) {
        A* a = new B;
        (dynamic_cast<B*>(a))->z = 3;
    ...
    

    s.a.

    http://www.edm2.com/0608/introcpp13.html

    Aber besser ist auf JEDEN Fall der vom Lord vorgeschlagene Weg der virtuellen Funktion/Polymorphie.

    Gruß,

    Simon2.



  • Was meinst du mit vererbe Public?

    Also ich habe es jetzt so:

    class Netz_Nachricht {
    
    public:
    	int groesse; 
    	int MsgTyp;
    
    	Netz_Nachricht() 
        { 
    		MsgTyp=0;
            groesse = sizeof(Netz_Nachricht); 
        } 
    	virtual float gib_Wert();
    
    	int gib_groesse() 
    	{
    		return groesse;
    	}
    
    	int gib_MsgTyp() 
    	{
    		return MsgTyp;
    	}
    
    	virtual ~Netz_Nachricht() {} 
    
    };
    
    class Konkrete_Nachricht1:Netz_Nachricht{
    public:
    
    	float wert;
    
    	Konkrete_Nachricht1    (float wert_)
    	{ 
    		MsgTyp=1;
            wert=wert_; 
            groesse = sizeof(Konkrete_Nachricht1); 
        } 
    
    	float gib_Wert() 
    	{
    		return wert;
    	}
    

    Main:

    #include "Netz_Nachricht.h"
    
    int main() 
    {
    	Netz_Nachricht *n = new Konkrete_Nachricht1(8);
    	return 1;
    }
    

    Fehler:

    :\dokumente und einstellungen\ck\eigene dateien\visual studio 2005\projects\client\main.cpp(7) : error C2243: 'Typumwandlung': Konvertierung von 'Konkrete_Nachricht1 *' zu 'Netz_Nachricht *' ist bereits vorhanden, aber es kann nicht darauf zugegriffen werden.



  • MisterX schrieb:

    Was meinst du mit vererbe Public?

    Etwa so:

    class Konkrete_Nachrricht1 : /*->*/ public Net_Nachricht
    {...};
    

    (standardmäßig erben class's privat - da ist dann keine Typumwandlung möglich)



  • Ich weiß das ich bestimmt schon nerve, aber ich brauche noch mehr Hilfe:

    Jetzt sieht es so aus:

    class Netz_Nachricht {
    
    public:
    	int groesse; 
    	int MsgTyp;
    
    	Netz_Nachricht() 
        { 
    		MsgTyp=0;
            groesse = sizeof(Netz_Nachricht); 
        } 
    	virtual float gib_Wert();
    
    	int gib_groesse() 
    	{
    		return groesse;
    	}
    
    	int gib_MsgTyp() 
    	{
    		return MsgTyp;
    	}
    
    	virtual ~Netz_Nachricht() {} 
    
    };
    
    class Konkrete_Nachricht1:public Netz_Nachricht{
    public:
    
    	float wert;
    
    	Konkrete_Nachricht1    (float wert_)
    	{ 
    		MsgTyp=1;
            wert=wert_; 
            groesse = sizeof(Konkrete_Nachricht1); 
        } 
    
    	float gib_Wert() 
    	{
    		return wert;
    	}
    };
    

    Main:

    int main() 
    {
    	Netz_Nachricht *n = new Konkrete_Nachricht1(8);
    	return 1;
    }
    

    Fehler:

    Main.obj : error LNK2001: Nicht aufgelöstes externes Symbol ""public: virtual float __thiscall Netz_Nachricht::gib_Wert(void)" (?gib_Wert@Netz_Nachricht@@UAEMXZ)".
    C:\Dokumente und Einstellungen\ck\Eigene Dateien\Visual Studio 2005\Projects\Client\Debug\Client.exe : fatal error LNK1120: 1 nicht aufgelöste externe Verweise.



  • in der Main steht noch

    #include "Netz_Nachricht.h"

    das habe ich vergessen mitanzugeben



  • Du hast die Methode gib_wert() in der Basisklasse nur definiert - da brauchst du entweder eine Implementierung dazu oder du markierst sie als pur virtuell, indem du "=0" dahintersetzt.



  • ES geht jetzt, Danke an alle:

    Ich habe noch eine Frage zu virtual:

    Also wenn ich jetzt Konkrete_Nachricht1 und Konkrete_Nachricht2 habe
    und es gibt eine Variable mit zugriffsmethoden, die aber in beiden unterschiedlich ist, muß ich dann beide Zugriffsmethoden Virtual in Netz_Nachricht einfügen?



  • Hi,

    vorab:
    ich habe den Eindruck, Du solltest Dich doch ein wenig mit Tutorials, Büchern, ... beschäftigen. Wenn Du in dem Tempo weitermachst, wirst Du dieses Jahr nicht mehr mit Deinem Program fertig ... (nicht böse gemeint, sondern ein ehrlich gemeinter Ratschlag).
    Ich habe nicht den Eindruck, dass Du wirklich verstehst, was Du da treibst, sondern versuchst, Dich mit trial&error durchzuwursteln und wenn Du über schon diese "Kleinigkeiten" stolperst, wirst Du an den echten Brocken (delete, virtueller Dtor, Kopieren, return-by-value, Referenzen, Initialisierung, overloading, templates, ....) verzweifeln. Sorry, aber C++ ist deutlich komplexer als Java ( = "C++ für Mädchen" 😉 ) und es gibt seeeehr viel mehr darüber zu wissen als Du bis jetzt tust .....und ehrlich gesagt: Ich kann mir nicht vorstellen, dass Du in Java so wirklich sattelfest bist, weil nicht wenige Probleme, die Du hier hast, mit Java genauso wären.

    Zu Deinem Problem: Mit Vererbung sagst Du aus "B ist ein A"; deshalb werden die Dinge, die ein A kann,die aber B spezieller macht, in A als "virtual" deklariert und von B überschrieben.

    Was Du da versuchst, widerspricht dem Konzept ... und das hat nicht nur ästhetische Konsequenzen.

    Mal zwei Fragen:
    - Warum sollen bei Dir überhaupt verschiedene Nachrichtentypen von einer gemeinsamen Basisklasse abgeleitet werden ?
    - Warum soll ein Aufrufer nur eine Basisklasse in die Finger bekommen, dann aber Spezialitäten verschiedener abgeleiteter Klassen nutzen ?

    Das klingt für mich nach schlechtem Design und damit nach "Entwicklunsgfingerbruch und Wartungshölle" 😉

    Gruß,

    Simon2.



  • Es geht darum, das ich versuche mit Sockets Daten zu versenden.
    Da man ja nicht immer das selbe versenden möchte, habe ich die Klasse "Netz_Nachricht" gemacht, welche die eigene Größe und den Typ enthält.

    Wenn ich also Daten empfange, behandle ich sie erst als "Netz_Nachricht"
    lese die ersten 8 byte, also größe und den konkreten Typ aus und kann somit entscheiden, was ich empfangen werde. Dann erzeuge ich mir den passenden Typ "Konkrete_Nachrichtx" und lade die restlichen Daten da rein.

    Wenn es ein besseres Konzept gibt => Bitte immer her damit, aber mir ist nix Besseres eingefallen um verschiedenartige Datenpakete versenden zu können.

    Ach und so ein Anfänger bin ich nicht. Immerhin habe ich schon mit c++ und Opengl ein verteiltes Pong spiel gemacht (es funktioniert sogar halbwegs) und da bin ich auf das Problem mit den versciedenen Daten gestoßen. Ich möchte entweden nur z.B. die Schlägerpositionen versenden oder die Nachricht das der Ball ins Aus ging. Und das Problem war dem Empfänger klarzumachen was denn nun für eine Nachricht kommt. Also habe ich mir gedacht, das jede Nachricht seinen Typ und die eigene Größe angibt. Also past ja Vererbung doch ganz gut.

    Bisher habe ich immer eine Art von Nachricht verschickt, die dann aber alles enthielt auch wenn es nicht interessierte und das erzeugte zu viel Traffic.

    Aber in Java war das mit den Casts irgendwie viel einfacher...aber jetzt weiß ich ja wie es geht.



  • Ah ja,

    so etwas in der Richtung hatte ich mir schon gedacht (wir fluchen immer noch über den externen Kollegen, der vor 5 Jahren dasselbe bei uns verbrochen hat ... übrigens auch mit "Nachrichten"). 😃

    Mein Vorschlag: NICHT vererben.

    Ich würde eine "Nachrichtenerzeuger-Klasse" machen, die den Binärstrom empfängt und anhand der "Kennung" ein Objekt der richtigen Klasse erzeugt. Ob die Ergebnisse selbst noch eine gemeinsame Basisklasse haben, würde ich daran entscheiden, ob sie große Gemeinsamkeiten haben oder nicht. In meinen Ohren klingt es so, als sei das eigentlich nicht der Fall.

    Mit dem Nachrichtenobjekt selbst ist es ja auch noch nicht getan, weil ja irgendjemand etwas mit dieser Nachricht machen soll ... deswegen würde ich das Nachrichtenobjekt (wahrscheinlich eher ein POD/struct) gleich an den Verarbeiter dieser Nachricht weiterleiten - entweder eine Klasse mit entsprechend "overloadeter" ("nicht overridden" !) "verarbeite()"-Methode oder einfach ein Satz "freier Funktionen" ...

    Gruß,

    Simon2.



  • Ich schicke mal den gesamten code bisher (Der grauenhaft ich weiß, aber funzu so halbwegs, dann kanste ja mal konkret verbesserungsvorschläge machen)

    Netznachricht:

    class Netz_Nachricht {
    
    public:
    	int groesse; 
    	int MsgTyp;
    
    	Netz_Nachricht() 
        { 
    		MsgTyp=0;
            groesse = sizeof(Netz_Nachricht); 
        } 
    	virtual float gib_Wert()
    	{
    	return 0;
    	}
    
    	int gib_groesse() 
    	{
    		return groesse;
    	}
    
    	int gib_MsgTyp() 
    	{
    		return MsgTyp;
    	}
    
    	virtual ~Netz_Nachricht() {} 
    
    };
    
    class Konkrete_Nachricht1:public Netz_Nachricht{
    public:
    
    	float wert;
    
    	Konkrete_Nachricht1    (float wert_):wert(wert_)
    	{ 
    		MsgTyp=1;
            groesse = sizeof(Konkrete_Nachricht1); 
        } 
    
    	float gib_Wert() 
    	{
    		return wert;
    	}
    };
    

    Sever.h

    #define Server__ 
    class Server {
    
    public:
    
    	HANDLE hThread[1];
    	DWORD  dwThreadID[1];	
    
    	queue<Netz_Nachricht*> *q;
    
    	Server();
    
    	void beginne_empfang();
    
    };
    
    #endif
    

    Server.cpp:

    #include "server.h"
    
    DWORD WINAPI ThreadFuncServer(LPVOID data){
    
    	long rc;
    	SOCKET acceptSocket;
    	SOCKET connectedSocket;
    	SOCKADDR_IN addr;
    	WSADATA wsa;
    
    	rc=WSAStartup(MAKEWORD(2,0),&wsa);
    	// Socket erstellen
    	acceptSocket=socket(AF_INET,SOCK_STREAM,0);
    
    	// Socket binden
    	memset(&addr,0,sizeof(SOCKADDR_IN));
    	addr.sin_family=AF_INET;
    	addr.sin_port=htons(12345);
    	addr.sin_addr.s_addr=ADDR_ANY;
    	rc=bind(acceptSocket,(SOCKADDR*)&addr,sizeof(SOCKADDR_IN));
    
    	// In den listen Modus
    	rc=listen(acceptSocket,10);
    
    	// Verbindung annehmen
    	connectedSocket=accept(acceptSocket,NULL,NULL);
    
    	queue <Netz_Nachricht*> *q= ((queue<Netz_Nachricht*>*) (data)) ;
    
    	while(rc!=SOCKET_ERROR)
    	{
    		//bisher nur das Senden Implementiert
    		if (!q->empty())
    		{
    			Netz_Nachricht *netz_nachricht = q->front();
    			send(connectedSocket,(const char*)netz_nachricht,netz_nachricht->gib_groesse(),0);
    		}
    	}
    
    		closesocket(acceptSocket);
    		closesocket(connectedSocket);
    		WSACleanup();
    		//DWORD d = (DWORD)data;
    		DWORD d=100;
    		return(d);
    
    }
    
    	Server::Server(){q = new queue<Netz_Nachricht*>();}
    
    	void Server::beginne_empfang()
    	{
    			hThread[1] = CreateThread(NULL,                        
    			0,                            
    			ThreadFuncServer,    //Hier liegt das Problem            
    			(LPVOID)q,           
    			0,                            
    			&dwThreadID[1]);
    			//hier noch weiterschreiben
    
    	}
    

    Client.h

    #include <winsock2.h>
    #include <queue> 
    #include "Netz_Nachricht.h"
    using namespace std; 
    
    #pragma comment(lib, "ws2_32.lib" ) 
    
    #ifndef Client__ 
    #define Client__ 
    class Client {
    
    public:
    
    	HANDLE hThread[1];
    	DWORD  dwThreadID[1];	
    
    	queue<int> *q;
    
    	Client();
    
    	void start();
    
    };
    
    #endif
    

    Client.cpp

    #include "Client.h"
    #include <Iostream>
    
    DWORD WINAPI ThreadFuncClient(LPVOID data)			
    {
    
    long rc;
      SOCKET s;
      SOCKADDR_IN addr;
      WSADATA wsa;
      rc=WSAStartup(MAKEWORD(2,0),&wsa);
      // Socket erstellen
      s=socket(AF_INET,SOCK_STREAM,0);
      // Verbinden
      memset(&addr,0,sizeof(SOCKADDR_IN)); 
      addr.sin_family=AF_INET;
      addr.sin_port=htons(12345); 
      addr.sin_addr.s_addr=inet_addr("192.168.2.101"); 
    
      Netz_Nachricht * nachricht = new Netz_Nachricht();
    
      while(rc!=SOCKET_ERROR)
    	{
    		int wert=0;
    		while (wert<8)
    		{
    			wert = wert+recv (s,(char*)nachricht+wert,8-wert,0);
    
    		}
    
    		int groesse_zwischenspeicher;
    		int typ_zwischenspeicher;
    
    		groesse_zwischenspeicher=nachricht->groesse;
    		typ_zwischenspeicher=nachricht->MsgTyp;
    
    		delete nachricht;
    
    		if (nachricht->MsgTyp==1) 
    		{ 
    			nachricht= new Konkrete_Nachricht1(1);
    		}
    
    			nachricht->groesse=groesse_zwischenspeicher;
    			nachricht->MsgTyp=typ_zwischenspeicher;
    
    			int laenge=0;
    			while (laenge <nachricht->groesse-8)
    			{
    				laenge=laenge+recv(s,(char*)nachricht+laenge+8,nachricht->groesse-(laenge+8),0);
    			}
    
    			cout <<"groesse: "<<nachricht->gib_groesse() << endl;
    			cout <<"Typ: "<<	nachricht->gib_MsgTyp() << endl;
    			cout <<"Wert: "<<	nachricht->gib_Wert() << endl;
    
    			cout <<""<< endl;
    			delete(nachricht);
      }
    
      closesocket(s);
      WSACleanup();
      return((DWORD)data);
    }
    
    Client::Client(){q = new queue<int>();}
    
    	void Client::start()
    	{
    			hThread[1] = CreateThread(NULL,                        
    			0,                            
    			ThreadFuncClient,    //Hier liegt das Problem            
    			(LPVOID)q,           
    			0,                            
    			&dwThreadID[1]);
    			//hier noch weiterschreiben
    
    	}
    

    Ich weiß, dürfen alles Beispiele von: "So macht man es nicht" sein, aber was soll ich konkret anders machen?



  • MisterX schrieb:

    ...was soll ich konkret anders machen?

    Wie gesagt: Erst ein sinnvolles Design überlegen.

    Gruß,

    Simon2.


Anmelden zum Antworten