Early binding (frühe Bindung) late binding (späte Bindung)

Post on 06-Apr-2015

117 views 3 download

Transcript of Early binding (frühe Bindung) late binding (späte Bindung)

early binding (frühe Bindung)late binding (späte Bindung)

Ausgangspunkt sind wieder die Klassen der letzten Präsentation:

#include "stdafx.h"#include <iostream>#include <time.h>using namespace std;

class A{ public: A(int i); void hallo(); int x;};

class B: public A{ public: B(int i); void hallo(); int x; int y;};

A::A(int i){ x = i;};

void A::hallo(){ cout << "Ich bin A" << "x=" << x << endl;};

B::B(int i):A(i-2){ x = i-1; y = i;};

void B::hallo(){ cout << "Ich bin B" << "x=" << x << "y=" << y << endl;};

Welcher Wert welcher Speicherzelle wird durch die gleich folgenden Anweisungen links verändert ? Auf welches Objekt (a oder b) zeigt ap ?

if(rand()%2)==1) ap = &a;else ap = &b;ap->x=13;

A a(10);B b(20);A *ap;

Vom Zufall abhängig, zeigt ap entweder auf das Objekt a oder auf das Objekt b.

Adr a0100 x 10

Adr b0200 A::x 18

x 19y 20

Adr ap0300 ?

Angenommen, ap zeigt auf b. Welchen Wert hat dann ap ?

Adr a0100 x 10

Adr b0200 A::x 18

x 19y 20

Adr ap0300 ?

if(rand()%2)==1) ap = &a;else ap = &b;ap->x=13;

A a(10);B b(20);A *ap;

Angenommen, ap zeigt auf b. Welchen Wert hat dann ap ?

Adr a0100 x 10

Adr b0200 A::x 18

x 19y 20

Adr ap0300

if(rand()%2)==1) ap = &a;else ap = &b;ap->x=13;

A a(10);B b(20);A *ap;

Adr a0100 x 10

Adr b0200 A::x 18

x 19y 20

Adr ap0300 0200

if(rand()%2)==1) ap = &a;else ap = &b;ap->x=13;

A a(10);B b(20);A *ap;

Auf welches x würde dann die Anweisung ap->x = 13 zugreifen ?

Es gibt 2 Möglichkeiten: b.A::x oder b.B::x

Da sich der Compiler schon beim Compilieren (und nicht erst während der Laufzeit) entscheiden muss, kann es nicht ...

Adr a0100 x 10

Adr b0200 A::x 18

x 19y 20

Adr ap0300 0200

if(rand()%2)==1) ap = &a;else ap = &b;ap->x=13;

A a(10);B b(20);A *ap;

.... B::x sein, da diese Komponente (falls ap auf a zeigt) nicht existiert. Dagegen existiert (egal ob ap auf a oder b zeigt) immer die Komponente A::x.

Diesen Vorgang des Compilers nennt man early binding (frühe Bindung).

dieses x existiert in a und b

Adr a0100 x 10

Adr b0200 A::x 18

x 19y 20

Adr ap0300 0200

if(rand()%2)==1) ap = &a;else ap = &b;ap->x=13;

A a(10);B b(20);A *ap;

Also wird der Wert von x im Objekt b verändert auf:

Adr a0100 x 10

Adr b0200 A::x 18

x 19y 20

Adr ap0300 0200

if(rand()%2)==1) ap = &a;else ap = &b;ap->x=13;

A a(10);B b(20);A *ap;

Adr a0100 x 10

Adr b0200 A::x

x 19y 20

Adr ap0300 0200

if(rand()%2)==1) ap = &a;else ap = &b;ap->x=13;

A a(10);B b(20);A *ap;

Adr b0200 A::x 13

x 19y 20

Also: Der Typ der Zeigervariablen, hier also A, und nicht der Typ des Objekts auf den ap während der Laufzeit zeigt, hier also B, bestimmt welches Mitglied (Member) von *ap angesprochen wird.

Adr ap0300 0200

Adr a0100 x 10

if(rand()%2)==1) ap = &a;else ap = &b;ap->x=13;

A a(10);B b(20);A *ap;

Neue Frage

Welcher Wert welcher Speicherzelle wird durch die folgenden Anweisungen links verändert ? Auf welches Objekt (a oder b) zeigt ap ?

aus Platzgründen: hier steht wie vorher: rand()%2 = =1

Vom Zufall abhängig, zeigt ap entweder auf das Objekt a oder auf das Objekt b.

Adr a0100 x 10

hallo()

Adr b0200 A::x 18

x 19y 20A::hallo()B::hallo()

Adr ap0300 ?

if(...) ap = &a;else ap = &b;ap->hallo();

A a(10);B b(20);A *ap;

Angenommen, ap zeigt auf b. Welchen Wert hat dann ap ?

Adr a0100 x 10

hallo()

Adr ap0300 ?

Adr b0200 A::x 18

x 19y 20A::hallo()B::hallo()

if(...) ap = &a;else ap = &b;ap->hallo();

A a(10);B b(20);A *ap;

Adr a0100 x 10

hallo()

Adr ap0300

Adr b0200 A::x 18

x 19y 20A::hallo()B::hallo()

if(...) ap = &a;else ap = &b;ap->hallo();

A a(10);B b(20);A *ap;

Adr a0100 x 10

hallo()

Adr ap0300 0200

Adr b0200 A::x 18

x 19y 20A::hallo()B::hallo()

if(...) ap = &a;else ap = &b;ap->hallo();

A a(10);B b(20);A *ap;

Auf welches hallo() würde dann die Anweisung ap->hallo() zugreifen ?

Es gibt 2 Möglichkeiten: b.A::hallo() oder b.B::hallo()

Da sich der Compiler schon beim Compilieren (und nicht erst während der Laufzeit) entscheiden muss, kann es nicht ...

Adr a0100 x 10

hallo()

Adr ap0300 0200

Adr b0200 A::x 18

x 19y 20A::hallo()B::hallo()

if(...) ap = &a;else ap = &b;ap->hallo();

A a(10);B b(20);A *ap;

.... B::hallo() sein, da diese Methode (falls ap auf a zeigt) nicht zu A gehört. Dagegen existiert (egal ob ap auf a oder b zeigt) immer die Methode A::hallo().

Diesen Vorgang des Compilers nennt man early binding (frühe Bindung).

dieses hallo() existiert in a und b

Adr ap0300 0200

Adr a0100 x 10

hallo()

Adr b0200 A::x 18

x 19y 20A::hallo()B::hallo()

if(...) ap = &a;else ap = &b;ap->hallo();

A a(10);B b(20);A *ap;

Also: Der Typ der Zeigervariablen, hier also A, und nicht der Typ des Objekts auf den ap während der Laufzeit zeigt, hier also b, bestimmt welches Mitglied (Member) von *ap angesprochen wird.

Adr a0100 x 10

hallo()

Adr ap0300 0200

Adr b0200 A::x 18

x 19y 20A::hallo()B::hallo()

if(...) ap = &a;else ap = &b;ap->hallo();

A a(10);B b(20);A *ap;

Mögliche Speicherbelegung beim obigen Programm (hier

nochmals das Programm)

int main(){ A a(10); B b(20); A *ap; srand((unsigned)time(NULL)); if(rand()%2==1){ ap = &a; } else{ ap = &b; } ap->x = 13; ap->hallo(); return(0);}

Annahme: 1) Die Adressen wurden willkürlich gewählt.In der 4. Spalte stehen die Werte also die Inhalte der Adressen nachdem die letzte Anweisung des Programms abgearbeitet wurde.

2) Zufallsbedingt soll der Körper des else Teils der if-Verzweigung (also ap = &b) ausgeführt werden.

Adresse Symbol besteht Inhalt0100 a x 10... ... ... ...0200 b A::x 13

B::x 19y 20

... ... ... ...0300 ap 0200... ... ... ...0500 A::hallo() Anweisungen

... ... ... ...0600 B::hallo() Anweisungen

Neue Frage

Wie kann man erreichen, dass bei einem Verweis auf ein Objekt die Methode der Klasse dieses Objektes zur Laufzeit aufgerufen wird und nicht die gleichnamige Methode der Klasse des Typs des Verweises beim Compilieren ?

Kurz:Wie kann man early binding umgehen ?Wie wird dies programmtechnisch realisiert ?

Angenommen, ap zeigt zufallsbedingt auf a. Dann soll die Anweisung ap->hallo() auf die folgende Methode zugreifen:

Die Methode hallo(), der Klasse des Objekts, auf das ap während der Laufzeit zeigt, also A::hallo().

Adr a0100 x 10

hallo()

Adr ap0300 0100

Adr b0200 A::x 18

x 19y 20A::hallo()B::hallo()

if(...) ap = &a;else ap = &b;ap->hallo();

A a(10);B b(20);A *ap;

Angenommen, ap zeigt zufallsbedingt auf b. Dann soll die Anweisung ap->hallo() auf die folgende Methode zugreifen:

Die Methode hallo(), der Klasse des Objekts, auf das ap während der Laufzeit zeigt, also B::hallo().

Adr a0100 x 10

hallo()

Adr ap0300 0200

Adr b0200 A::x 18

x 19y 20A::hallo()B::hallo()

if(...) ap = &a;else ap = &b;ap->hallo();

A a(10);B b(20);A *ap;

Das dazugehörige Programm ist das Gleiche wie oben, nur dass die Methode hallo() bei der Deklaration den Bezeichner virtual bekommt.

Das Programm

#include "stdafx.h"#include <iostream>#include <time.h>

using namespace std;

class A{ public: A(int i); virtual void hallo(); int x; };

class B: public A{ public: B(int i); virtual void hallo();

int x; int y; };

Durch den Bezeichner virtual wird hallo() eine virtuelle Funktion. Die Verwendung von virtual ist nur innerhalb einer Klassendeklaration zulässig.

Der Bezeichner virtual vererbt sich und muß daher nicht mehr (kann aber) in den Subklassen wiederholt werden.

A::A(int i){x = i;

};

void A::hallo(){ cout <<"Ich bin A und " << "x= " << x << endl;};

B::B(int i):A(i-2){ x = i-1; y = i;};

void B::hallo(){ cout << "Ich bin B" << "x=" << x << "y=" << y << endl;};

int main(){ A a(10); B b(20); A *ap; srand((unsigned)time(NULL)); if(rand()%2==1){ ap = &a; } else{ ap = &b; } ap->x = 13; ap->hallo(); return(0);}

Neue Frage

Wie wird dies technisch im Speicher realsiert ?

vt_A ist ein Verweis (Adresse) auf eine Tabelle (virtual function pointer table), in der wiederum die Adressen der virtuellen Methoden der Klasse A stehen, hier also die Adresse der Methode A::hallo()

vt_B ist ein Verweis (Adresse) auf eineTabelle (virtual function pointer table), in der wiederum die Adressen der virtuellen Methoden der Klasse B stehen, hier also die Adresse der Methode B::hallo()

Adr vt_A0500 A::hallo()

Adr vt_B0600 B::hallo()

Adr a096 &vt_A0100 x 10

Adr ap0300 ?

Adr b0196 &vt_B0200 A::x 18

x 19y 20

if(...) ap = &a;else ap = &b;ap->hallo();

A a(10);B b(20);A *ap;

Adr vt_A0500 A::hallo()

Adr vt_B0600 B::hallo()

Adr a096 &vt_A0100 x 10

Adr ap0300 ?

Adr b0196 &vt_B0200 A::x 18

x 19y 20

if(...) ap = &a;else ap = &b;ap->hallo();

A a(10);B b(20);A *ap;

Angenommen, ap zeigt zufallsbedingt auf a. Welchen Wert hat dann ap ?

Adr vt_A0500 A::hallo()

Adr vt_B0600 B::hallo()

Adr a096 &vt_A0100 x 10

Adr ap0300

Adr b0196 &vt_B0200 A::x 18

x 19y 20

if(...) ap = &a;else ap = &b;ap->hallo();

A a(10);B b(20);A *ap;

Adr vt_A0500 A::hallo()

Adr vt_B0600 B::hallo()

Adr a096 &vt_A0100 x 10

Adr ap0300 100

Adr b0196 &vt_B0200 A::x 18

x 19y 20

if(...) ap = &a;else ap = &b;ap->hallo();

A a(10);B b(20);A *ap;

Welche Adresse wird dann beim Aufruf von hallo() in der folgenden Anweisung angesprochen ? ap->hallo() ein ?

Adr vt_A0500 A::hallo()

Adr vt_B0600 B::hallo()

Adr a096 &vt_A0100 x 10

Adr ap0300 100

Adr b0196 &vt_B0200 A::x 18

x 19y 20

if(...) ap = &a;else ap = &b;ap->hallo();

A a(10);B b(20);A *ap;

*(ap-4)

0100

096

0500

Welche Methode wird also aufgerufen ?

A::hallo()

Adr vt_A0500 A::hallo()

Adr vt_B0600 B::hallo()

Adr a096 &vt_A0100 x 10

Adr ap0300 ?

Adr b0196 &vt_B0200 A::x 18

x 19y 20

if(...) ap = &a;else ap = &b;ap->hallo();

A a(10);B b(20);A *ap;

Angenommen, ap zeigt zufallsbedingt auf b. Welchen Wert hat dann ap ?

Adr vt_A0500 A::hallo()

Adr vt_B0600 B::hallo()

Adr a096 &vt_A0100 x 10

Adr ap0300

Adr b0196 &vt_B0200 A::x 18

x 19y 20

if(...) ap = &a;else ap = &b;ap->hallo();

A a(10);B b(20);A *ap;

Adr vt_A0500 A::hallo()

Adr vt_B0600 B::hallo()

Adr a096 &vt_A0100 x 10

Adr ap0300 200

Adr b0196 &vt_B0200 A::x 18

x 19y 20

if(...) ap = &a;else ap = &b;ap->hallo();

A a(10);B b(20);A *ap;

Welche Adresse wird dann beim Aufruf von hallo() in der folgenden Anweisung angesprochen ? ap->hallo() ein ?

Adr vt_A0500 A::hallo()

Adr vt_B0600 B::hallo()

Adr a096 &vt_A0100 x 10

Adr ap0300 200

Adr b0196 &vt_B0200 A::x 18

x 19y 20

if(...) ap = &a;else ap = &b;ap->hallo();

A a(10);B b(20);A *ap;

*(ap-4)

0200

0196

0600

Welche Methode wird also aufgerufen ?

B::hallo()

Mögliche Speicherbelegung beim obigen Programm (hier

nochmals das Programm)

Annahme: 1) Die Adressen wurden willkürlich gewählt.In der 4. Spalte stehen die Werte, also die Inhalte der Adressen nachdem die letzte Anweisung des Programms abgearbeitet wurde.

2) Zufallsbedingt soll der Körper der if-Verzweigung (also ap = bp) ausgeführt werden.

3) Zwischen den Zeilen stehen platzbedingt keine (durch ... getrennten) weiteren Adressen.

Adresse Symbol besteht Inhalt096 a &vt_A 05000100 x 100196 b &vt_B 06000200 A::x 13

B::x 19y 20

0300 ap 02000500 vt_A &A::hallo() 07000600 vt_B &B::hallo() 08000700 A::hallo() Anweisungen

0800 B::hallo() Anweisungen

Ist early binding (frühe Bindung) auch einfacher möglich ? (ohne Pointer)

Das (einfachere) Programm ist das Gleiche wie oben, nur dass statt dem Pointer *ap ein Objekt der Klasse A verwendet wird.

#include "stdafx.h"#include <iostream>#include <time.h>

using namespace std;

class A{ public: A(int i); virtual void hallo(); int x; };

class B: public A{ public: B(int i); virtual void hallo();

int x; int y; };

virtual

virtual

A::A(int i){x = i;

};

void A::hallo(){ cout <<"Ich bin A und " << "x= " << x << endl;};

B::B(int i):A(i-2){ x = i-1; y = i;};

void B::hallo(){ cout << "Ich bin B" << "x=" << x << "y=" << y << endl;};

int main(){ A a(10); B b(20); A z(123); srand((unsigned)time(NULL)); if(rand()%2==1){ z = a; } else{ z = b; } z.x = 13; z.hallo(); return(0);}

Warum wird hier kein late Binding gemacht ?

Warum wird hier kein late Binding gemacht ?

int main(){ A a(10); B b(20); A z(123); srand((unsigned)time(NULL)); if(rand()%2==1){ z = a; } else{ z = b; } z.x = 13; z.hallo(); return(0);}

Durch diese Deklaration ist das Objekt z immer vom Klassentyp A. Deshalb wird hier immer early binding gemacht, ganz egal, ob man der Methode hallo den Bezeichner virtual gegeben hat.