"undefined reference to vtable..." hä?
-
Hallo,
ich hab ein kleines Client-Server-Programm gescriebne bzw bin noch dabei. Dabei hab ich eine KLasse Client weclhe von der KLasse TcpClient abgeleitet ist. Der Quellcode steht unten. Beim Compilieren bekomme ich nun immer diesen Fehler:main.o(.text+0x27): In function `main': /root/Programmierung/C++/Netzwerkprogrammierung/NetAdmin/client/client/main.cpp:31: undefined reference to `Client::~Client [in-charge]()' main.o(.gnu.linkonce.t._ZN6ClientC1Ev+0x19): In function `Client::Client[in-charge]()': /usr/include/TCP-Klassen/tcptemplate.h:41: undefined reference to `vtable for Client' main.o(.gnu.linkonce.t._ZN9TcpClientC2Ev+0x19): In function `TcpClient::TcpClient[not-in-charge]()': /usr/include/TCP-Klassen/tcptemplate.h:41: undefined reference to `vtable for TcpClient' collect2: ld returned 1 exit status gmake[2]: *** [client] Error 1Ich benutze als IDE kdevelop unter Suse 9.0
Nun noch der Code:
client.h#include <TCP-Klassen/tcpclient.h> /** *@author Kante */ class Client : public TcpClient { public: Client() : TcpClient() {;} Client(char* ip) : TcpClient(ip) {;} Client(char* ip, int por) : TcpClient(ip, por) { ;} ~Client(); void action(); };client.cpp
#include "client.h" void Client::action() { }Superklasse tcpclient.h
/*/Client Basis Klasse auf TCP/IP-Basis *@author Kante */ #include "tcptemplate.h" class TcpClient : public TcpTemplate { private: struct sockaddr_in servAddr; char *destinationIp; //Server-IP public: //Konstuktor: fuellt die Variablen Ip und port bei deklaration mit den uebergebenen Werten TcpClient() : TcpTemplate() {destinationIp = "127.0.0.1";} TcpClient(char* ip) : TcpTemplate() {destinationIp = ip;} TcpClient(char* ip, int p) : TcpTemplate(p) {destinationIp = ip;} virtual ~TcpClient() {;} void initClient() { initSocket(); fillStruct();} void fillStruct() { bzero((char *) &servAddr, sizeof(servAddr)); servAddr.sin_family = AF_INET; servAddr.sin_addr.s_addr = inet_addr(destinationIp); servAddr.sin_port = htons(port);} void verbinden() { if(sock::connect(sock, (struct sockaddr*) &servAddr, sizeof(servAddr)) < 0) {perror("CONNECT: "); exit(1);}} int returnSocket() {return TcpTemplate::sock;} virtual void action(); };Superklasse tcptemplate.h
#include <iostream> #include <stdlib.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> using namespace std; class TcpTemplate { protected: int sock; //Socket int port; //Port public: TcpTemplate() { port = NORM_PORT;} TcpTemplate(int p) { port = p;} virtual ~TcpTemplate() {;} //Port setzen void setPort(int p) {port = p;} //Socket zurückgeben int getSocket() { return sock;} //Socket intialisieren void initSocket(){ if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("SOCKET: "); exit(1);}} template <class T> void recvObj(int sock,T *dataptr, int size) { int n; if ((n = read(sock,(char *) dataptr, size)) != size) { cout << "Fehler beim Datenempfang: " << n << " bytes empfangen" << endl; perror("Fehler:");}} template <class T> void sendObj(int sock,T *dataptr,int size) { int n; if ((n = write(sock,(char *) dataptr, size)) != size) { cout << "Fehler bei der uebertragung der Daten! " << n << "Bytes ?bertragen!" << endl;} perror("Fehler:");} };So, es muss irgendwie mit den virtuellen Funktionen zusammenhängen obwohl ich nicht weis wie.
Vlt weis ja jemand von euch rat (ne kleine erklärung was der Fehler überhaupt bedeutet wär auch nicht schlecht, immerhin will ich was dabei lernen
)mit freundlichen Grüßen Kante
Oh merke gerade das ich im falschen Forum gelandet bin. Habs hier reingestellt da das ganze später über eine Qt-GUI verfügen soll. Hat aber bis jetz noch nichma die ansätze da ichs nicht compl. kann. Also bitte verschieben oder so belassen....
-
Hi,
die Implementierung für ~Client() fehlt. Entweder sowie bei den anderen in .h mit rein oder besser in die .cpp, dann aber alles da rein;Und da: {;} kannste getrost das Semikolon weglassen...
Dann nochmal kompilieren und die kommenden Fehler versuchen selber zu lösen

-
Dieser Thread wurde von Moderator/in GPC aus dem Forum Andere GUIs - Qt, GTK+, wxWidgets in das Forum C++ verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.
-
Hy,
also den destruktor hab ich implementiert aber dann kommt diese Fehlermeldung:client.o(.gnu.linkonce.r._ZTI6Client+0x8):/usr/include/g++/cmath:477: undefined reference to `typeinfo for TcpClient' client.o(.gnu.linkonce.t._ZN9TcpClientD2Ev+0xb): In function `TcpClient::~TcpClient [not-in-charge]()': /usr/include/g++/cmath:477: undefined reference to `vtable for TcpClient' main.o(.gnu.linkonce.t._ZN9TcpClientC2Ev+0x19): In function `TcpClient::TcpClient[not-in-charge]()': /usr/include/TCP-Klassen/tcptemplate.h:41: undefined reference to `vtable for TcpClient' collect2: ld returned 1 exit statusalso das selbe in grün blos eine "schicht" tiefer.
Doch der Destrukto ist ja implementiert.
was heist eigentlich dieser Fehler (voralllem was ist die vtable)?
Würds gern verstehen.
mfg Kante
-
Hallo,
vtable steht für virtuelle Methodentabelle. Das ist ein Implementierungsartefakt, dass von Compilern gerne für die Umsetzung von virtuellen Methoden verwendet wird.Für jede Klasse mit vituellen Methoden wird eine (Ausnahmen bewusst ignoriert) solche Tabelle angelegt. Die Einträge in der Tabelle sind die Adressen der virtuellen Funktionen. Überschreiben einer virtuellen Methode heißt dann nichts weiter, als das in der Tabelle die Adresse der neuen Funktion gespeichert wird. Der Aufruf einer virtuellen Methode ist dann ein zweistufiger Prozess. Erst wird in der passenden virtuellen Methodentabelle an der passenden Stelle die richtige Adresse rausgesucht, dann wird die durch die Adresse spezifizierte Funktion indirekt aufgerufen.
Zu deinem Problem: einige Compiler erzeugen die virtuelle Methodentabelle für eine Klasse in der cpp-Datei, in der die erste virtuelle Methode dieser Klasse definiert ist. Definierst du nun alle virtuellen Methoden einer Klasse in einer h-Datei, dann weiß der Compiler nicht, wo er die VMT ablegen soll. Beim Versuch die VMT aufzulösen stellt der Linker dann fest, das selbige fehlt -> der Fehler.
Die Lösung: stell sicher, dass jede deiner Klassen mindestens eine ihrer virtuellen Methoden in einer cpp-Datei definiert.
-
Also langsam verzweifel ich wirklich.
Wenn ich alle virtuellen Methoden in die Cpp-datei packe und implementiere ändert sich nihct viel. Wenn ich dann die Destruktoren von TcpClient und TcpTemplate nicht als virtual deklariere, bekomme ich wieder einen anderen Fehler:client.o(.gnu.linkonce.r._ZTI6Client+0x8): In function `__static_initialization_and_destruction_0': /usr/include/g++/cmath:477: undefined reference to `typeinfo for TcpClient' collect2: ld returned 1 exit status gmake[2]: *** [client] Error 1warum ist das nur so schwierig? hatte och nie solche probleme mit virtuellen Fnktionen.
mfg KanteHier nochma den neue Code
tcptemplate.h#include <iostream> #include <stdlib.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> using namespace std; class TcpTemplate { protected: int sock; //Socket int port; //Port public: TcpTemplate() { port = NORM_PORT;} TcpTemplate(int p) { port = p;} ~TcpTemplate() ; //Port setzen void setPort(int p) {port = p;} //Socket zurückgeben int getSocket() { return sock;} //Socket intialisieren void initSocket(){ if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("SOCKET: "); exit(1);}} template <class T> void recvObj(int sock,T *dataptr, int size) { int n; if ((n = read(sock,(char *) dataptr, size)) != size) { cout << "Fehler beim Datenempfang: " << n << " bytes empfangen" << endl; perror("Fehler:");}} template <class T> void sendObj(int sock,T *dataptr,int size) { int n; if ((n = write(sock,(char *) dataptr, size)) != size) { cout << "Fehler bei der uebertragung der Daten! " << n << "Bytes ?bertragen!" << endl;} perror("Fehler:");} };tcptemplate.cpp
#include "tcptemplate.h" TcpTemplate::~TcpTemplate() {;}tcpclient.h
#include "tcptemplate.h" class TcpClient : public TcpTemplate { private: struct sockaddr_in servAddr; char *destinationIp; //Server-IP public: //Konstuktor: fuellt die Variablen Ip und port bei deklaration mit den uebergebenen Werten TcpClient() : TcpTemplate() {destinationIp = "127.0.0.1";} TcpClient(char* ip) : TcpTemplate() {destinationIp = ip;} TcpClient(char* ip, int p) : TcpTemplate(p) {destinationIp = ip;} ~TcpClient(); void initClient() { initSocket(); fillStruct();} void fillStruct() { bzero((char *) &servAddr, sizeof(servAddr)); servAddr.sin_family = AF_INET; servAddr.sin_addr.s_addr = inet_addr(destinationIp); servAddr.sin_port = htons(port);} void verbinden() { if(sock::connect(sock, (struct sockaddr*) &servAddr, sizeof(servAddr)) < 0) {perror("CONNECT: "); exit(1);}} int returnSocket() {return TcpTemplate::sock;} virtual void action(); };tcpclient.cpp
#include "tcpclient.cpp" TcpClient::~TcpClient() {;} void TcpClient::action() {;}am Code von Client hat sich nichts geändert.
mg Kante
-
Hallo,
bist du sicher, dass dein Makefile korrekt ist? Also das es alles neu baut, was neu gebaut werden muss? Linkst du gegen irgendwelche Bibliotheken? Wurden die vielleicht mit einer anderen Compiler-Version übersetzt?
-
Na ja
ganz sicher bin ich mir nich. Macht halt alles kdevelop. aber selbst wenn ich ein neues Projekt anlege und per copy&paste alles verschiebe klapts nicht.
Der Quellcode sollte iegentlich ok sien, oder?
komme einfach nicht weiter
mfg Kante
-
Kante23 schrieb:
Der Quellcode sollte iegentlich ok sien, oder?
Was das obrige Proglem angeht schon.
Aber naja son paar kleinigkeiten sind bei mir zumindest noch falsch,- wo wird NORM_PORT wird definiert ?
- dein socket::connect ist mir nicht klar..
- du hast keinen virtuellen destruktor, obgleich virtuelle Methoden.
4)! Achja, du machst
#include "tcpclient.cpp"in der "tcpclient.cpp".
Das hat meine compiler verwirrt ... sollte deinen aber auch stören..Naja, bei Kdevelop kannst du afaik das "Projekt aufräumen" und sogar nen "distclean" machen, dann geht nen neues autoconf und configure durch und
dann sollte auch alles richtig gebaut werden, wenn der code so ist, wie hier.
( bis auf die "Kleinigkeiten" oben)
-
hallo,
vielen dank erstma.
@
ZU deinen beanstandeungen
:
also NORM-poRt wird in tcptemplate definiert ist aber leider auf dem COde ausschnitt nicht mit drauf (hab ich vergessen zu kopieren).
Das mit dem sock:: hat die Bewandniss, das der Compiler mir da schon vorher ma rumgemeckert hat, als die Klasse zusätzlich noch einer subclass von QWidget war und da gab es namenskonflikt emit connect() von Qt. Deswegen hab ich in der Include datei nen kurzen Namensraum für connect definiert (is vlt nich die beste lösung, aber mir viel keine andere ein).
Die Virtuellen Destruktoren hatte ich ja am anfang, aber da gabs halt diese Probleme mit der vtable, nach dem ich das virtuell rausgenommen habe, waren es ein par weniger
Bei kdevelop alles neu erstellen hab ich schon probiert hat aber ichts groß gebracht (der Fehler blieb der selbe).Das mit "tcptemplate.cpp" verwundert mich auch grad, is aber nur nen schreibfehler (aiuch wenn der Compiler mir das bis jetz noch nicht beanstandet hat).
Nun gibt es noch einen kurzen nachtrag: hab das ganze jetz ma außerhalb von kdevelop probiert, also einfach ne kurze main.cpp (mit der Klasse Client) mitlles g++ compilert. Und da klapts komischerweise, aber acuh nur wenn ich ein CLient objekt nicht mittels new "anfordere", also nur mit
Client c();und bei
Client* c = new Client();kommt wieder die "vtable" meldung. sehr komisch,
na ja vlt gehts ja noch ma
mfg Kante