Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

odc

Frischling

  • »odc« ist der Autor dieses Themas
  • Private Nachricht senden

1

05.02.2007, 16:20

Pure virtual Funktionen

Hallo community,

nach langer Abstinenz mal wieder ein Frageposting meinerseits: Ich versuche mich weiterhin an dem gewagten Projekt, Spieleprogrammierer zu werden. Immer wieder allerdings tauchen Fragen auf, die ich einfach nicht beantwortet finde. Ihr seid meine letzte Hoffnung (Obi-Wan Kenobi) :D

Diesmal geht es um Pure virtuelle Funktionen, die ja meines Wissens wie folgt deklariert werden:

C-/C++-Quelltext

1
2
3
4
5
class CMyClass
{
  public:
  virtual void pureVirtualMethod() = 0;
};


Diese Funktion lässt sich lediglich über eine abgeleitete Klasse etwa wie folgt nutzen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
class CMyDerivedClass : public CMyClass
{
  public:
  void pureVirtualMethod() {}
};

//

CMyClass *pCls = new CMyDerivedClass();
pcls->pureVirtualMethod();


Soweit, so gut, aber: Wo und wann DEFINIERE ich nun besagte rein virtuelle Funktion/Methode? In der Kind- oder in der Basisklasse? Und was bringt mir das überhaupt? Ist Polymorphie wirklich sooo superwichtig?
code is poetry

big_muff

Alter Hase

Beiträge: 460

Wohnort: Schweiz

Beruf: Informatikstudent (4. Semester)

  • Private Nachricht senden

2

05.02.2007, 16:29

Du hast es ja schon richtig gemacht. Selbstverständlich definierst du die rein virtuelle Funktion in der Basisklasse. Damit zwingst du jede davon abgeleitete Klasse diese Funktion zu implementieren.
Und ja, das ist sooo superwichtig wenn man Objektorientiert programmieren will. Man benutzt das zur Datenabstraktion.
Nehmen wir einmal an du programmierst ein Spiel in dem es viele verschiedene Raumschiffe hat (Transporter, Jäger, ...). Wenn du jetzt irgendwo alle Raumschiffe speichern willst um sie dann zu rendern müsstest du für jeden Raumschifftyp eine eigene Liste anlegen und dann jede Liste durchgehen und das Raumschiff rendern. Wenn du jetzt aber eine Basisklasse für alle Raumschiffe definierst und in dieser eine rein virtuelle Render-Funktion definierst, dann brauchst du nur eine Liste mit allen Raumschiffen und kannst die Liste einmal durchgehen und alle Raumschiffe rendern. Deine Funktion die alle Raumschiffe rendert braucht ja nicht zu wissen was für ein Typ Raumschiff es gerade darstellt.
Ein weiterer grosser Vorteil gibt es in der Erweiterbarkeit. Wenn du jetzt einen neuen Raumschifftyp hinzufügen willst, dann müsstest du ohne eine Basisklasse eine neue Liste für diesen Typ Raumschiff einfügen und eine neue Schleife in der dieser Typ gerendert wird. Mit einer Basisklasse musst du aber in diesem Fall gar nichts ändern.

Kurz: Das nimmt dir viel Arbeit ab!
Nur Idioten halten Ordnung, ein Genie beherrscht das Chaos.[size=7]

[/size]HardFate - Ein Start, Ein Ziel, Viele Wege[size=7]

[/size]Ein Mitglied der VEGeiCoUndGraSonMaWiGeS Bewegung.

odc

Frischling

  • »odc« ist der Autor dieses Themas
  • Private Nachricht senden

3

05.02.2007, 16:57

Danke für Deine Antwort. So langsam lichtet sich der Dschungel. Dennoch, ich hab arge Verständnisschwierigkeiten bei dieser ganzen "virtual"-Sache.

Hier eine kleine Klasse, die ich gebastelt habe. Es geht darum, eine Basisklasse (Nummer) zu erstellen, aus der ich eine Integer- und eine Float-Klasse ableite. Beide abgeleiteten Klassen greifen auf die gleiche virtuelle printData()-Methode zu. Im ersten Fall wird die Integer-Zahl ausgegeben, im zweiten Fall natürlich dann die Float-Zahl.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <iostream>

using namespace std;

//classes

//1st: BASECLASS

class CNumber
{
protected:

    //memberdata

    int m_number;

public:

    //memberfunctions

    CNumber();
    virtual int printData(int integer_number) = 0;                      //This is the purely virtual Method

    
};

CNumber::CNumber()
{
    cout << "Standard-Constructor called.." << endl;
}


//2nd: DERIVED CLASS NO. 1

//THE INTEGER-CLASS

class CInteger : public CNumber
{
private:
    
    //memberdata

    int m_number;

public:

    //memberfunctions

    CInteger();                                     //the constructor

    int printData(int integer_number)               //here is the purely virtual Method again : Only in the derived class possible!

    {
        m_number = integer_number;
        cout << "The Integer-number is: " << m_number << endl;
        return 0;
    }               
};

CInteger::CInteger()
{
    cout << "Constructor for Integer called. "<< endl;
}

//main program

//

int main()
{
    //variables

    CNumber *pNummer;
    pNummer = new CInteger();
    pNummer->printData(13);

    return 0;
}


Nun - wo definiere und deklariere ich jetzt den Zugriff auf die printData-Methode für die Float? Muss ich hierfür eine zweite "virtual float printData() = 0;"-Methode in der Basisklasse einbinden?

Danke für jegliche Hilfe.
code is poetry

David_pb

Community-Fossil

Beiträge: 3 886

Beruf: 3D Graphics Programmer

  • Private Nachricht senden

4

05.02.2007, 17:07

Zitat von »"odc"«


Nun - wo definiere und deklariere ich jetzt den Zugriff auf die printData-Methode für die Float? Muss ich hierfür eine zweite "virtual float printData() = 0;"-Methode in der Basisklasse einbinden?


Ja, dann muss CNumber diese Funktion aber auch überschreiben. Das doofe ist halt das es sich hierbei um zwei ganz verschiedene Funktionen handelt.

Zitat von »"big_muff"«


Du hast es ja schon richtig gemacht. Selbstverständlich definierst du die rein virtuelle Funktion in der Basisklasse. Damit zwingst du jede davon abgeleitete Klasse diese Funktion zu implementieren.


Nö, selbstverständlich is das garnich. Er KANN die Funktion für die Basisklasse definieren er muss aber nicht. Aber selbstverständlich wird die Funktion in der Basisklasse deklariert.
@D13_Dreinig

big_muff

Alter Hase

Beiträge: 460

Wohnort: Schweiz

Beruf: Informatikstudent (4. Semester)

  • Private Nachricht senden

5

05.02.2007, 17:14

Zitat von »"David_pb"«

Nö, selbstverständlich is das garnich. Er KANN die Funktion für die Basisklasse definieren er muss aber nicht. Aber selbstverständlich wird die Funktion in der Basisklasse deklariert.

Sorry, ich bringe deklarieren und definieren immer durcheinander...

Du solltest das vielleicht mal an einem sinnvolleren Beispiel probieren :roll:.

Also erstmal: Die Klasse CInteger sollte doch einen Integer repräsentieren, das tut sie zur Zeit aber nicht wirklich. Die Variable m_number ist in deinem Code total nutzlos.
Ich würde diese im Konstruktor initalisieren.
In der Basisklasse ist die Variable m_number sowieso deplaziert. Diese Klasse soll ja abstrakt sein, hier legt sie sich aber schon auf einen int fest.
Ich würde das so machen

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class CNumber
{
public:
    virtual void printData() = 0;
}

class CInteger : public CNumber
{
private:
    int m_number;

public:
    CInteger(int integer_number) {m_number=integer_number;}
    void printData()
    {
        cout << "The Integer-number is: " << m_number << endl;
    }
};


Jetzt kannst du ganz leicht eine CFloat-Klasse dazu schreiben...
Nur Idioten halten Ordnung, ein Genie beherrscht das Chaos.[size=7]

[/size]HardFate - Ein Start, Ein Ziel, Viele Wege[size=7]

[/size]Ein Mitglied der VEGeiCoUndGraSonMaWiGeS Bewegung.

odc

Frischling

  • »odc« ist der Autor dieses Themas
  • Private Nachricht senden

6

05.02.2007, 17:22

Zitat von »"big_muff"«


Du solltest das vielleicht mal an einem sinnvolleren Beispiel probieren :roll:.


Wenn ich ein "sinnvolleres" Beispiel hätte, hätte ich das entsprechend gepostet. :roll:

Zitat von »"big_muff"«


Also erstmal: Die Klasse CInteger sollte doch einen Integer repräsentieren, das tut sie zur Zeit aber nicht wirklich. Die Variable m_number ist in deinem Code total nutzlos.
Ich würde diese im Konstruktor initalisieren.
In der Basisklasse ist die Variable m_number sowieso deplaziert. Diese Klasse soll ja abstrakt sein, hier legt sie sich aber schon auf einen int fest.
Ich würde das so machen

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class CNumber
{
public:
    virtual void printData() = 0;
}

class CInteger : public CNumber
{
private:
    int m_number;

public:
    CInteger(int integer_number) {m_number=integer_number;}
    void printData()
    {
        cout << "The Integer-number is: " << m_number << endl;
    }
};


Jetzt kannst du ganz leicht eine CFloat-Klasse dazu schreiben...


Nein, die Idee ist ja eben, eine postData()-Funktion zu haben, und dies nicht über den Konstruktor abzuwickeln. Sonst wäre diese Funktion ja auch hinfällig.
Die Variable in der Basisklasse war ein Überbleibsel von einem älteren Versuch.
code is poetry

David_pb

Community-Fossil

Beiträge: 3 886

Beruf: 3D Graphics Programmer

  • Private Nachricht senden

7

05.02.2007, 17:23

Und warum gibst du 0 zurück? Ist das auch ein überbleibsel?
@D13_Dreinig

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

8

05.02.2007, 17:23

@odc:
Ich verstehe den Sinn deiner Klassen nicht ganz.
Anscheinend willst du eine abstrakte Klasse Number machen, die Basisklasse für Integer und Float ist. In die Basisklasse sollte aber dann nur das rein, was auch für alle Unterklassen gleich ist. Indem du deiner "printData"-Methode einen int-Parameter verpasst, machst du dir das kaputt. Und die private Variable in der Basisklasse ebenfalls!

Logisch wäre für mich so etwas:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Number {
public:
    virtual ~Number() { }
    virtual void print() const = 0;
};

class Integer : public Number {
public:
    Integer( int i ) : myValue( i ) { }

    void print() const {
        std::cout << myValue;
    }

private:
    int myValue;
};

class Float : public Number {
public:
    Float( float f ) : myValue( f ) { }

    void print() const {
        std::cout << myValue;
    }

private:
    float myValue;
};


Das könntest du dann mit Templates aber noch leichter machen. Statt für alle möglichen Zahlentypen die Klassen zu schreiben, könntest du dir das Leben leichter machen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template<typename T> class ConcreteNumber : public Number {
public:
    ConcreteNumber( const T& value ) : myValue( value ) { }

    void print() const {
        std::cout << myValue;
    }

private:
    T myValue;
};

typedef ConcreteNumber<int> Integer;
typedef ConcreteNumber<float> Float;
typedef ConcreteNumber<double> Double;

BlackSnake

Community-Fossil

Beiträge: 1 549

Beruf: Student

  • Private Nachricht senden

9

05.02.2007, 17:30

@ David
ich weiß nicht ob es da schon war im lehrbuch ;) . vielleicht kennt er templates noch gar nicht und jetzt soll er welche anwenden... schon bissen dumm :)

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

10

05.02.2007, 17:40

Na und?
Außerdem war das nur eine Alternative.

Werbeanzeige