PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C++ und "virtual"



Silver
11-07-2004, 15:09
Hallo!

Angenommen ich habe eine Klasse welche Funktionen enthält die "virtual" sind, dann müssen diese ja in den abgeleiteten "überschrieben" also definiert werden. Will ich jedoch nun von der Klasse, die die virtuellen Funktionen enthält, ein Objekt (mit new) erstellen, so bekomm ich vom g++ immer die Meldung



error: cannot allocate an object of type ` Mitarbeiter'
error: because the following virtual functions are abstract:




Oder auch wenn ich kein Objekt erstellen will und nur zb ein Array von der Klasse um später die Objekte von den abgeleiteten Klasse dort hin zu "referenzieren" funkt nicht! Ist das Wort "virtual" so ca. mit "abstract" von Java gleich zu setzen?!? Hab auch gehört wenn man eine Funktion "virtual" setzt, dann ist das jede Funktion von dieser Klasse! Stimmt das auch?!?

mfG

Ringding
11-07-2004, 15:26
Angenommen ich habe eine Klasse welche Funktionen enthält die "virtual" sind, dann müssen diese ja in den abgeleiteten "überschrieben" also definiert werden.Nein. Nur wenn sie abstrakt sind.

error: cannot allocate an object of type ` Mitarbeiter'
error: because the following virtual functions are abstract:Weil du sie abstrakt gemacht hast (mit =0).

Ist das Wort "virtual" so ca. mit "abstract" von Java gleich zu setzen?!?Nein, in Java ist einfach jede Funktion virtual.

Hab auch gehört wenn man eine Funktion "virtual" setzt, dann ist das jede Funktion von dieser Klasse! Stimmt das auch?!?Nein.

Silver
11-07-2004, 17:14
cool danke ;)!

aber was bedeutet nun virtual ohne dem =0 ?!?

mfG

tuxipuxi
11-07-2004, 17:28
Eine virtuelle Funktion kann eine Funktionsbody haben, dann musst du die Funktion in der subclass nicht überschreiben.
Wenn du die Funktion abstrakt, das heißt =0 machst, _muss_ jede subclass die Funktion implementieren, sonst ist das immer noch eine abstrakte Klasse von der man kein Objekt instanziieren kann.

Michael.

P.S: Vielleicht solltest du in deiner Signatur ein bisschen zwischen Regierung und gesamten Volk differenzieren, so klingt es ein wenig platt.

Silver
11-07-2004, 19:11
Eine virtuelle Funktion kann eine Funktionsbody haben, dann musst du die Funktion in der subclass nicht überschreiben.


aha danke, aber was bedeutet nun wenn man eine Funktion virtuell macht und nicht abstrakt?!? Kenne nur abstrakte Klassen von Java aus...

mfG

RapidMax
11-07-2004, 19:49
Folgendes Programm illustriert die Auswirkungen von virtual:

#include <iostream>

class Foo {
public:
void called() { std::cout << " Foo::called()" << std::endl; }
virtual void vcalled() { std::cout << " Foo::vcalled()" << std::endl; }
void methode1() {
std::cout << "Foo::methode1():" << std::endl;
this->called(); this->vcalled();
}
};
class Bar : Foo {
public:
void called() { std::cout << " Bar::called()" << std::endl; }
virtual void vcalled() { std::cout << " Bar::vcalled()" << std::endl; }
void methode2() {
std::cout << "Bar::methode2():" << std::endl;
this->called(); this->vcalled();
}
void methode3() {
std::cout << "Bar::methode3():" << std::endl;
Foo::called(); Foo::vcalled();
}
void methode4() {
std::cout << "Bar::methode4():" << std::endl;
this->methode1();
}
};

int main() {
Foo f;
Bar b;
f.methode1();
b.methode2();
b.methode3();
b.methode4();
return EXIT_SUCCESS;
}
Ausgabe:

Foo::methode1():
Foo::called()
Foo::vcalled()
Bar::methode2():
Bar::called()
Bar::vcalled()
Bar::methode3():
Foo::called()
Foo::vcalled()
Bar::methode4():
Foo::methode1():
Foo::called()
Bar::vcalled()
Virtuelle Methoden werden nicht direkt aufgerufen, sonderen über die Virtuelle Methodentabelle des Objekts. In dieser wird so immer die Adresse der Methode abgelegt, welche in der instanzierten Klasse definiert ist.

Im Beispiel oben kommt das im Letzten Methodenaufruf b.methode4() zum Ausdruck: Eine nicht virtuelle Methode called() wird immer diejenige der gleichen Klasse aufgerufen, während die viruelle Methode vcalled() der instanzierten Klasse Bar aufgerufen wird, obwohl Foo noch nichts von dieser Methode wissen muss.

Eine virtuelle Klasse ist daher ein unverzichtbares Werkzeug für polymorphe Klassen.

Gruss, Andy

Lin728
11-07-2004, 20:26
Java ist daher ein unverzichtbares Werkzeug für objecktierientierte Programmierung.

.................................................. ............

Silver
12-07-2004, 20:17
Hallo!

Hab noch ein paar Fragen!
Angenommen ich habe eine Klasse:


class Auto {
public:
virtual void getKosten =0;
};



warum kann ich dann von dieser Klasse zb kein Array Objekt machen (Auto auto_array[10]) oder auch nur Objekt um dann später in diesem Array die abgeleiteten Objekte zu speichern? In Java funkt das! Was wäre ein anderer Lösungsvorschlag?!?

Hab noch ein Problem: Hab eine Klasse Mitarbeiter und eine Klasse Arbeiter welche von Mitarbeiter ableitet. Schreibe ich nun eine Methode welches eine Eigenschaft zurückgibt, bekomm ich folgenden Compiler-fehler:


mitarbeiter.o(.gnu.linkonce.r._ZTI12Angestellter+0 x8):mitarbeiter.cpp:17: undefined reference to `typeinfo for Mitarbeiter'



Vielen Dank für eure Antworten!


PS: Hätte nicht gedacht, dass der Sprung von Java auf C++ soviele Probleme bereitet....

Ringding
12-07-2004, 20:54
warum kann ich dann von dieser Klasse zb kein Array Objekt machen (Auto auto_array[10]) oder auch nur Objekt um dann später in diesem Array die abgeleiteten Objekte zu speichern? In Java funkt das!Wenn du das willst, was du in Java hast, dann brauchst du ein Array von Auto*.




mitarbeiter.o(.gnu.linkonce.r._ZTI12Angestellter+0 x8):mitarbeiter.cpp:17: undefined reference to `typeinfo for Mitarbeiter'
Hast du mit g++ gelinkt?

locus vivendi
12-07-2004, 21:30
Gegeben ist eine abstrakte Klasse:

warum kann ich dann von dieser Klasse zb kein Array Objekt machen (Auto auto_array[10]) oder auch nur Objekt um dann später in diesem Array die abgeleiteten Objekte zu speichern?
Du hast ja extra eine abstrakte Klasse gemacht, damit du kein Objekt davon direkt machen kannst!

Zur "undefined refererence for vtable...":
Du musst sicherstellen das die Übersetzungseinheit in der eine virtuelle Funktion (für pure-virtual gilt das nicht) zu deinem Programm gehört. Also entweder muss in der einzigen Übersetzungseinheit die Definition vorhanden sein, oder es muss diejenige Übersetzungeinheit welche die Definition enthält hinzugelinkt werden. Der Grund dafür ist, dass der gcc (grob gesagt) die "vtable" zusammen mit der Definiton von virtuellen Funktionen emittiert.
(Also insbesondere bitte beachten das das für andere Compiler nicht unbedingt gelten muss).

Silver
13-07-2004, 06:54
Zur "undefined refererence for vtable...":
Du musst sicherstellen das die Übersetzungseinheit in der eine virtuelle Funktion (für pure-virtual gilt das nicht) zu deinem Programm gehört. Also entweder muss in der einzigen Übersetzungseinheit die Definition vorhanden sein, oder es muss diejenige Übersetzungeinheit welche die Definition enthält hinzugelinkt werden. Der Grund dafür ist, dass der gcc (grob gesagt) die "vtable" zusammen mit der Definiton von virtuellen Funktionen emittiert.
(Also insbesondere bitte beachten das das für andere Compiler nicht unbedingt gelten muss).

ähm, jo, hmm, und wie mach ich das nun! Was meinst du genau mit der Definition?!?

mfG

locus vivendi
13-07-2004, 08:49
und wie mach ich das nun! Was meinst du genau mit der Definition?!?

Das hier ist ne Deklaration:

void func();
Und das hier ist ne Definition:

void func {}

Kurz gesagt ist die Definition einer Funktion das mit dem Funktionskörper.
Und so eine Definition musst du irgendwie in das Programm einbringen. Beispiel, das folgende funktioniert mit dem gcc nicht:


struct A
{
virtual void func();
};

int main() { A a; }

Es funktioniert nicht, weil der gcc keine Definiton für A::func() sieht, und deshalb keine vtable anlegt.

Folgendes würde gehen:


struct A
{
virtual void func();
};

void A::func() { }

int main() { A a; }

Die Definition von A::func() muss aber nicht unbedingt in der Übersetzungseinheit sein, in der sich auch main() befindet. Du kannst auch separat eine Datei übersetzen (ohne main funktion) und dann hinzulinken.

Und wie Ringding schon gesagt hat, du solltest den gcc als g++ oder c++ aufrufen, u.a. weil dann die Standardbibliothek richtig hinzugelinkt wird.

Silver
13-07-2004, 19:26
ähm ich kompiliere doch mit g++! ich weiß nicht, bin ich zu blöd für c++, oder was hat's da?!? :cool:

mfG

locus vivendi
13-07-2004, 19:44
ähm ich kompiliere doch mit g++!
Okay! Das hatte ich bloß zur Sicherheit nochmal erwähnt.

Silver
13-07-2004, 20:24
ok Problem hatte sich nun erledigt... Designfehler (tja bei Java ist es danke Interpreter bequemer...)!

Im Prinzip check ich alle grundlegenden Sachen in C++, no na hab auch 1 Jahr bis jetzt Java geproggt, aber das mit den abstrakten Methoden.... dass ich da kein Objekt draus erstellen kann und Java "irgendwie" doch:
Angenommen ich hab eine Klasse Mitarbeiter mit abstrakten Methoden und einen Constructor, wo zb Namen, etc. übergeben werden muss. Wenn ich jetzt eine Sub-Klasse erstelle und die abstrakten Methode "erstellt" und auch einen Constructor wo ich neben dem Namen noch weiter Sachen übergeben muss, dann kann ich leider nicht den Konstruktor von "Mitarbeiter" aufrufen, in Java ging das zb!
Gibts in C++ so etwas ähnliches wie super in Java?!?

mfG

PS: Bisher vielen dank für die Antworten!

locus vivendi
13-07-2004, 21:07
Wenn ich jetzt eine Sub-Klasse erstelle und die abstrakten Methode "erstellt" und auch einen Constructor wo ich neben dem Namen noch weiter Sachen übergeben muss, dann kann ich leider nicht den Konstruktor von "Mitarbeiter" aufrufen,...

Doch, das geht.



class A
{
public:
A(int x) : m(x) { }
virtual void f() = 0;
};

class B : public A
{
public:
B() : A(10) { }
virtual void f() { }
};


Sowas meintest du doch, oder?

K@sperl
13-07-2004, 21:09
Gibts in C++ so etwas ähnliches wie super in Java?!?
Ja, bei der Deklaration des constructors mach folgendes:


public:
myClass(int i) :base(i) {
...
}
Mit base() rufst du den constructor der Basisklasse auf und übergibst ihm einen bel. Parameter.


edit: @locus vivendi
Du bist wohl ein bißchen schneller gewesen ;)

panzi
13-07-2004, 22:11
class Foo {
public:
Foo( int i ) { ... }
};

class Bar : public Foo {
public:
Bar() : Foo( 5 ) { ... }
};
Genügt die "Erklärung"?

Edit:
Hups, da gab's ja ne 2. Seite... :rolleyes:

Silver
14-07-2004, 08:23
macht es eigentlich einen Unterschied ob ich z.B so initialisieren


methode(char* _name, int _alter):name(_name),alter(_alter) { }

oder so


methode(char* _name, int _alter) { name=_name; alter=_alter; }


Ist das nur eine andere Syntax?!?`

mfG

K@sperl
14-07-2004, 09:31
Nein, das macht keinen Unterschied, es ist nur eine andere Schreibweise.
Du kannst aber anstatt mit _name einen unterschiedlichen Var.namen zu verwenden, das mit this machen, z.B.

methode(char* name, int alter) {
this.name = name;
this.alter = alter;
}

edit: Wobei es nicht wirklich geschickt ist, einem char* einen anderen zuzuweisen, da nicht der Wert kopiert wird sondern beide Var. auf den selben Speicherbereich zeigen. Besser wär es string zu verwenden oder mit strcpy() den Inhalt der char* Variable zu kopieren.

Mit this spricht du die Komponenten dieser Klasse an, die du vorher deklariert hast.

wraith
14-07-2004, 09:39
Es besteht mehr als nur ein syntaktischer Unterschied, aber klar wird es erst, wenn du die Ebene nativer Typen (int,char,...) verlässt.

Nimm statt char* mal std::string an, dann wird in deinem ersten Codebeispiel der Copyconstructor aufgerufen.
In deinem zweiten Beispiel aber zuerst der Standardkonstruktor (falls vorhanden, sonst gibt es bereits Kompilerfehler), und dann wird der Zuweisungsoperator aufgerufen.
Du hast hier als zwei Operationen statt nur einer.

Im Falle nativer Typen sieht es etwas anders aus, weil der Standardkonstruktor nur generiert wird, wenn er von der Implementierung benötigt wird, das ist bei int,char... usw nicht der Fall.

Also benutze immer die Initialisierungsliste, es schadet nie, und hilft oft.

Ringding
14-07-2004, 10:59
Außerdem kann's ja sein, dass der Member gar keinen Default-Constructor hat, dann musst du die Initialisierungsliste verwenden.

Silver
14-07-2004, 11:11
Außerdem kann's ja sein, dass der Member gar keinen Default-Constructor hat, dann musst du die Initialisierungsliste verwenden.

wenn man keinen Constructor selbst definiert hat, erstellt der Compiler dann nicht selbst einen?!? Zum. ist das in Java so!
Für was gibts eigentlich einen Kopierconstructor?!

mfG

wraith
14-07-2004, 14:14
wenn man keinen Constructor selbst definiert hat, erstellt der Compiler dann nicht selbst einen?!? Zum. ist das in Java so!

Ja, es ist genau wie in Java.
Und das heißt, wenn man einen Defaultconstructor deklariert hat, dann wird dieser vom Compiler benutzt.
Ansonsten wird einer vom Compiler generiert, es sei denn es wurde ein anderer Constructor (mit Parametern) deklariert (oder man hat den Defaultconstructor private/protected deklariert).
Also selbes Verhalten wie in Java.


Für was gibts eigentlich einen Kopierconstructor?!

Der vom Compiler generierte Copy-Constructor macht nur eine flache Kopie, daher muß man in den Fällen, wo das nicht ausreichend ist selber den Copy-Constructor implementieren, und eine tiefe Kopie anfertigen.

anda_skoa
14-07-2004, 14:22
Außerdem ist die Initialisierungsliste die einzige Möglichkeit konstante Members zu initialisieren, weil es eben eine Initialisierung ist und keine Zuweisung, die ja bei einer Konstanten vom Compiler nicht erlaubt wird.

Ciao,
_

anda_skoa
14-07-2004, 14:25
Im Prinzip check ich alle grundlegenden Sachen in C++, no na hab auch 1 Jahr bis jetzt Java geproggt, aber das mit den abstrakten Methoden.... dass ich da kein Objekt draus erstellen kann und Java "irgendwie" doch:

Man kann auch in Java keine Instanz einer abstrakten Klasse erzeugen.

Eine abstrakte Klasse ist ja per Definition noch nicht fertig definiert, hellsehen kann der Compiler nicht :)

Ciao,
_

panzi
14-07-2004, 17:38
Man kann auch folgendes schreiben (funkt. zumindest mit g++ und msvcpp):

class Foo {
private:
int i;
string str;

public:
Foo( int i, string str ) : i( i ), str( str ) {}
};
Sprich Parameter und Member gleich benennen und trotzdem funktioniert die Initialisierung einwndfrei, ist an der Stelle ja eindeutig, kann ja nicht der Parameter gemeint sein.

Ringding
14-07-2004, 20:39
Außerdem ist die Initialisierungsliste die einzige Möglichkeit konstante Members zu initialisieren, weil es eben eine Initialisierung ist und keine Zuweisung, die ja bei einer Konstanten vom Compiler nicht erlaubt wird.

Ciao,
_
Ahja, genau. Für Referenzen gilt das gleiche.