PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C++ Experten: Wo ist der Fehler (gcc-3..3 mag das nicht)


SimonX
2003-07-16, 17:07:19
Das ist der Code:

---------------------------
#include <string.h>
#include <stdio.h>
#include <string>


class XX
{ public:
virtual ~XX() {}
virtual void GetMsg(char *input) { printf("get1\n"); strcpy(input,"get1"); }
virtual void GetMsg(char *input, int timeout) { printf("get2 %d\n", timeout); strcpy(input,"get2"); }
};

class YY: public XX
{
public:
void GetMsg(char * input) { GetMsg(input, 10); }
};


int main(int ac, char **av)
{
char input[1024];

YY hy;

hy.GetMsg(input);

printf("%s\n", input);

hy.GetMsg(input, 0);

printf("%s\n", input);
}
----------------------------------


Das ist der Output von gcc-3.3


h1.cc: In method `void YY::GetMsg(char *)':
h1.cc:16: no matching function for call to `YY::GetMsg (char *&, int)'
h1.cc:16: candidates are: void YY::GetMsg(char *)
h1.cc: In function `int main(int, char **)':
h1.cc:30: no matching function for call to `YY::GetMsg (char[1024], int)'
h1.cc:16: candidates are: void YY::GetMsg(char *)


Wo ist der Fehler bzw. gibt es einen Compiler der das versteht?

Tom Servo
2003-07-16, 17:18:38
Ein Überladen funktioniert nicht mehr, wenn du in der abgeleiteten Klasse eine Methode mit gleichem Namen neu defenierst. Dann wird nur noch diese Methode benutzt. Folgendes wüde fehlerfrei compilieren:


#include <string.h>
#include <stdio.h>
#include <string>


class XX
{
public:
virtual ~XX() {}
virtual void GetMsg(char *input) { printf("get1\n"); strcpy(input,"get1"); }
virtual void GetMsg(char *input, int timeout) { printf("get2 %d\n", timeout); strcpy(input,"get2"); }
};

class YY: public XX
{
public:
void GetMsg(char * input) { XX::GetMsg(input, 10); } // <----------
};


int main(int ac, char **av)
{
char input[1024];

YY hy;

hy.GetMsg(input);

printf("%s\n", input);

((XX*)&hy)->GetMsg(input, 0); // <----------

printf("%s\n", input);
}

SimonX
2003-07-16, 17:32:30
Steht das wirklich so im Standard drin?

Mindestens das: ((XX*)&hy)->GetMsg(input, 0); ist nicht wartbar und sehr unfreundlich.

Tom Servo
2003-07-16, 17:53:03
Habe C++ seinerzeit nach Bjarne Stroustrups Buch und nach dem ARM gelernt, und da sollte nix falsches drinstehen. Es gibt auch einen online C++ Standard. Vielleicht mal unter www.cplusplus.com schauen. Sieht ganz brauchbar aus.

edit:
Hier steht das Problem nochmal erklärt: http://www.semantics.org/gotchas/gotcha49.pdf
Mehr Infos findet man wenn man mit Google nach "c++ overriding overloading" oder "C++ überladen überschreiben" sucht.

Wenn du virtuelle Funktionen benutzt, dann sprichst du das Objekt eigentlich über einen Zeiger der Basisklasse an. Dann kann der Cast natürlich entfallen.

Sicher würde man den Code nicht wirklich so schreiben, er compiliert aber und du solltest überlegen ob du die Sache anders lösen kannst. Vielleicht versuchst du auch, Sprachenfeatures für etwas zu benutzen, wofür sie gar nicht gedacht sind.

Vielleicht könnte auch einfaches Umschreiben mit Default Parametern helfen:

virtual void GetMsg(char *input, int timeout=0) {
if (timeout == 0)
...
else
...
}


Dann hättest du nur noch eine Funktion ohne Überladen (?). Sollte eigentlich gehen, habe ich aber nicht probiert.

Tom Servo
2003-07-16, 18:04:53
Hier mal, wie man es real machen würde, wenn man den Code in eine eigenen Funktion verfrachtet, Die bekommt dann nur noch den Zeiger auf die Basisklasse, aber dank der virtuellen Methoden wird das GetMsg() aus YY benutzt, weil das Object in Wirklichkeit ein YY ist und nicht nur ein XX.



void f(char *input, XX *xx)
{
xx->GetMsg(input);
printf("%s\n", input);
xx->GetMsg(input, 0);
printf("%s\n", input);
}

int main(int ac, char **av)
{
char input[1024];


YY hy;
f(input, &hy);

}


Mit Referenenzen statt Pointern sollte es auch gehen, wenn ich da nicht falsch liege:



void f(char *input, XX &xx)
{
xx.GetMsg(input);
printf("%s\n", input);
xx.GetMsg(input, 0);
printf("%s\n", input);
}

int main(int ac, char **av)
{
char input[1024];


YY hy;
f(input, hy);

}

SimonX
2003-07-16, 19:07:55
Original geschrieben von Tom Servo

Vielleicht könnte auch einfaches Umschreiben mit Default Parametern helfen:

virtual void GetMsg(char *input, int timeout=0) {
if (timeout == 0)
...
else
...
}


Dann hättest du nur noch eine Funktion ohne Überladen (?). Sollte eigentlich gehen, habe ich aber nicht probiert.

Leider geht das mit dem Defaultparameter nicht, denn die abgeleitete Klasse soll genau diesem umbiegen. Weil der Caller aber nur die Basisklasse kennt, wird er immer den Default der Basisklasse nehmen.

Ich habe es trotzdem erstmal mit dem Defaultparameter gemacht und einen unnatürlichen Defaultwert genommen. Dann kann die eigentliche Methodenimplementation entscheiden welchen Wert sie wirklich einsetzt.

Achill
2003-07-16, 19:10:30
ich würde gern mal wissen, warum du das überhaupt machen möchtest - sinn des erbens ist ja nicht, das man eine Funktion von einer geerbten Klasse überläd, um dann in dieser genau diese überladene Funktion wieder auf zu rufen...

eine alternative wäre noch das einbetten der Klasse XX in YY, somit kannst du in YY auf ein Objekt von XX ansprechen und deren deren Funktionen nutzen.

SimonX
2003-07-16, 19:11:37
Es geht bei dem Problem um eine ServerConnection und eine ClientConnection. Bei beiden kann man GetMsg() machen, aber beim Server soll ein blockierendes GetMsg() unmöglich sein (durch umbiegen der GetMsg(input, -1) auf GetMsg(input)). Und für die ClientConnection soll GetMsg(input) für immer warten (also GetMsg(input, -1) aufrufen)

Tom Servo
2003-07-16, 19:41:33
Original geschrieben von SimonX
Leider geht das mit dem Defaultparameter nicht, denn die abgeleitete Klasse soll genau diesem umbiegen. Weil der Caller aber nur die Basisklasse kennt, wird er immer den Default der Basisklasse nehmen.

Ich habe es trotzdem erstmal mit dem Defaultparameter gemacht und einen unnatürlichen Defaultwert genommen. Dann kann die eigentliche Methodenimplementation entscheiden welchen Wert sie wirklich einsetzt.

Wenn der Caller nur die Basisklasse kennt, dann brauchst du doch auch den "nicht wartbaren" Cast nicht. Das Problem war doch nur, dass du versuchst hy.GetMsg aufzurufen anstatt über einen Pointer oder eine Referenz auf die Basisklasse. virtuelle Methoden haben doch genau die Aufgabe, dass du eine Methode über die Referenz der Basisklasse aufrufen kannst, aber trotzdem die Methode der abgeleiteten Klasse benutzt wird:

#include <stdio.h>

class B
{
public:
virtual void m() {
puts("B::m() called");
}
virtual void m(int n) {
puts("B::m(int n) called");
}
};

class D : public B
{
public:
void m() {
puts("D::m() called");
}
};

void f(B &obj)
{
obj.m();
}
void g(B &obj)
{
obj.m(1);
}

int main() {
B b;
D d;
B &r = d;

f(b); // B::m() called
f(d); // D::m() called
f(r); // D::m() called
g(b); // B::m(int n) called
g(d); // B::m(int n) called
g(r); // B::m(int n) called

}

Achill
2003-07-16, 21:10:38
Original geschrieben von SimonX
Es geht bei dem Problem um eine ServerConnection und eine ClientConnection. Bei beiden kann man GetMsg() machen, aber beim Server soll ein blockierendes GetMsg() unmöglich sein (durch umbiegen der GetMsg(input, -1) auf GetMsg(input)). Und für die ClientConnection soll GetMsg(input) für immer warten (also GetMsg(input, -1) aufrufen)

Warum basteslst du dir dann nicht eine klasse connection, die alle allgemeinen funktionen besitzt, die server und client gemeinsam haben.
Dann erstellst du zwei neue Klassen - einmal Server und einmal Client, erbst von oberen und fügst spezifische funktionen hinzu.

Im programm würde ich zwei Zeiger erzeugen, einmal ein von typ Server und einmal von Client - zur Laufzeit könntest du dann ohne weiteres ein objekt von der jeweils benötigten Klasse erzeugen bzw. sogar beide nutzen - hättest aber beide getrennt - auch in logischer hinsicht.

Nasenbaer
2003-07-21, 19:53:15
Original geschrieben von Tom Servo
Habe C++ seinerzeit nach Bjarne Stroustrups Buch und nach dem ARM gelernt

Ich habe auch gerade Bjarnes Buch am Wickel und kann guten Gewissens sagen, dass es das beste Buch zum Thema Programmieren ist, das ich je gelesen habe.
Es ist jeden Cent wert.

Mfg Nasenbaer