Andi: Klassenverständnis?

Hallo,

ich habe folgende Klassenstruktur:

class A
{
  function getText()
  { return "A"; }
  function A() {
    return $this->getText();
  }
}

class B extends A
{
  function getText()
  { return "B"; }
  function B() {
    return parent::A().$this->getText();
  }
}

$B = new B();
echo $B->B();

Laut meinem Klassenverständnis sollte hier "AB" ausgegeben werden. Es wird aber "BB" ausgegeben, weil beidemale die Funktion getText von B aufgerufen wird.

Was mache ich hier falsch? Wir befinden uns unter php4

Grüße
Andi

  1. hi,

    Laut meinem Klassenverständnis sollte hier "AB" ausgegeben werden. Es wird aber "BB" ausgegeben, weil beidemale die Funktion getText von B aufgerufen wird.

    Das dürfte daran liegen, dass in parent::A() das Ergebnis von $this->getText() zurückgegeben wird - $this ist hier aber deine Instanz von B().

    gruß,
    wahsaga

    --
    /voodoo.css:
    #GeorgeWBush { position:absolute; bottom:-6ft; }
    1. Hallo,

      Laut meinem Klassenverständnis sollte hier "AB" ausgegeben werden. Es wird aber "BB" ausgegeben, weil beidemale die Funktion getText von B aufgerufen wird.

      Das dürfte daran liegen, dass in parent::A() das Ergebnis von $this->getText() zurückgegeben wird - $this ist hier aber deine Instanz von B().

      Wie kann ich das ganze umgehen? Die Methode ist leider nicht statisch.

      Gruß
      Andi

    2. hi,

      Laut meinem Klassenverständnis sollte hier "AB" ausgegeben werden. Es wird aber "BB" ausgegeben, weil beidemale die Funktion getText von B aufgerufen wird.

      Das dürfte daran liegen, dass in parent::A() das Ergebnis von $this->getText() zurückgegeben wird - $this ist hier aber deine Instanz von B().

      Was auch noch unverständlich ist wenn ich auf eine Variable mit $this zugreife, dann wird die Variable der Elternklasse zurückgegeben.

      Gruß
      Andi

      1. echo $begrüßung;

        Laut meinem Klassenverständnis sollte hier "AB" ausgegeben werden. Es wird aber "BB" ausgegeben, weil beidemale die Funktion getText von B aufgerufen wird.

        Das dürfte daran liegen, dass in parent::A() das Ergebnis von $this->getText() zurückgegeben wird - $this ist hier aber deine Instanz von B().

        Was auch noch unverständlich ist wenn ich auf eine Variable mit $this zugreife, dann wird die Variable der Elternklasse zurückgegeben.

        Wenn Kindklassen-Methoden die Elternklassen-Methoden überschreiben, dann werden auch von Methoden, die von der Elternklasse geerbten wurden, die überschreibenden Kindklassen-Methoden aufgerufen. Deswegen überschreibt man sie ja. Wenn man die Funktionalität der überschriebenen (Eltern-)Methode haben möchte, muss man diese in der überschreibenden Methode mit parent::Methodenname() ansprechen. Es gibt keine Möglichkeit dynamisch und gezielt auf eine bestimmte Methode einer Klasse zuzugreifen.

        In deinem Beispiel verwendest du außerdem Methodennamen, die dem Klassennamen entsprechen. Diese Bezeichnung ist jedoch für den Konstruktor reserviert.
        Unter PHP hat ein Konstruktor keine so gavierende Aufgabe wie in anderen objektorientierten Sprachen. Es ist einfach nur eine Methode, die automatisch beim Instantiieren aufgerufen wird. Deswegen kann man den Konstruktor auch noch weitere Male nach Instantiieren der Klasse aufrufen. Beim automatischen Erstaufruf wird augenscheinlich der Rückgabewert ignoriert, sonst hättest du in $B kein Objekt sondern den String 'BB' stehen.
        Obwohl das beschriebene Verhalten möglich ist, ist es doch kein guter Stil, es so anzuwenden.

        echo "$verabschiedung $name";

  2. Hi Andi,

    Was mache ich hier falsch?

    Ich würde sagen, dass hier die Klasse B beim erweitern von A, die Methode getText() der Klasse A mit der neuen Methode überschreibt. Wenn du die getText() Methode von Klasse B mal in getText2() oder so umbenennst sollte es gehen würde ich sagen.

    Das ist ein Nachteil von OOP in PHP4, ich würde dir deshalb nahelegen, wenn du wirklich mit OOP in PHP anfangen willst zu arbeiten auf PHP5 umzusteigen - du hast dort einfach mehr Möglichkeiten.

    MfG, Dennis.

    1. echo $begrüßung;

      [...] ich würde dir deshalb nahelegen, wenn du wirklich mit OOP in PHP anfangen willst zu arbeiten auf PHP5 umzusteigen - du hast dort einfach mehr Möglichkeiten.

      Ich stimme zu. Im konkreten Beispiel deklariert man die Methode getText() der Klasse A als private, was dann zum gewünschten Ergebnis 'AB' führt. Durch das private kann B::getText() A::getText() nicht überschreiben, weil aus B ja A::getText() nicht sichtbar ist. Befindet man sich während der Ausführung nun in A::A() (was man auch über $this->A() statt parent::A() aufrufen kann), ist A::getText() sichtbar und wird aufgerufen.

      echo "$verabschiedung $name";

  3. du unterscheidest nicht zwischen dem statischen aufruf und einem methodenaufruf in einer instanz.

    du hast die instanz b.

    was soll der statische aufruf von a::a() bewirken?
    er führt zum aufruf der methode getText() der eigenen instanz. diese methode ist aber in b überschrieben, so daß diese in b aufgerufen wird.

    um das von dir gewünschte zu erreichen, mußt du in a einen statischen aufruf machen.
    also:
      function A() {
        return A::getText();
      }

    nur so ist es möglich, gezielt unterschiedliche methoden aufzurufen. eine instanz gibts ja nur einmal. und eine methode kann es nur einmal in einer instanz geben, wenn sich diese nicht wie in java durch unterschiedliche signaturen unterscheiden. aber soweit ist php noch nicht.

    1. echo $begrüßung;

      du unterscheidest nicht zwischen dem statischen aufruf und einem methodenaufruf in einer instanz.

      du hast die instanz b.

      was soll der statische aufruf von a::a() bewirken?

      In dem angegebenen Beispiel gab es keinen statischen Methodenaufruf. parent::A() ist kein statischer Aufruf. Wenn dem so wäre, gäbe es in A() $this nicht und statt dessen eine Fehlermeldung, was aber nicht der Fall ist.

      um das von dir gewünschte zu erreichen, mußt du in a einen statischen aufruf machen.

      Das hat er meiner Meinung nach schon verstanden. Allerdings gibt es dann $this nicht.

      echo "$verabschiedung $name";

      1. hallo dedlfix,

        schau dir mal klassen und objekte genau an. insbesondere wann wo irgendwo speicher verbraten wird, und was dann da drin steht.

        was glaubst du wofür das a in dem aufruf a::methde() steht?

        hiermit wird explizit die klasse benannt, aus welcher die gewünschte methode ausgeführt werden soll. dies ist quasi als namensraum zu sehen.
        anders müßte jede methode unique sein. aber wer sollte das koordinieren?

        das this bezieht sich immer auf die augenblickliche instanz, welche gerade ausgeführt wird. this kann es dann nicht geben, wenn keine instanz erzeugt wurde und diese nicht die kontrolle erhalten hat.

        this hat also nichts mit statischen aufrufen zu tun.

        bekannte beispiele für statische klassen sind math, io und utilities, welche keine instanzen zur ausführung benötigen.

        in php wird aber nicht explizit zwischen statischen klassen unterschieden. daher kann in php eine klasse instantiiert werden und zugleich auch als statische klasse benutzt werden.

        1. echo $begrüßung;

          was glaubst du wofür das a in dem aufruf a::methde() steht?

          Ja, ich weiß, wofür das steht.
          Ich bin ja ein bisschen betriebsblind, aber ich habe in Andis Quelltext keinen Aufruf dieser Art gefunden.

          das this bezieht sich immer auf die augenblickliche instanz, welche gerade ausgeführt wird. this kann es dann nicht geben, wenn keine instanz erzeugt wurde und diese nicht die kontrolle erhalten hat.

          this hat also nichts mit statischen aufrufen zu tun.

          Du weißt, dass das so ist, und ich weiß das auch. Es scheint mir aber so, als ob es hier ein Nicht-Verständigungspotitial für weniger Erfahrene gibt. Deswegen würde ich hier gern noch ein kleines Beispiel bringen.

          class foo {  
            
            var $x;  
            
            function baz() {  
              echo $this->x;  
            }  
            
            function bar() {  
              $this->x = 42;  
              foo::baz();  
            }  
          }  
            
          $bla = new foo();  
          $bla->bar();  
            
          //foo::baz();
          

          Wir haben nun eine Instanz von foo, also quasi auch ein $this. Dieses benutzen wir in bar() um $this->x einen Wert zuzuweisen. Soweit so gut. Nun folgt jedoch ein statischer Methodenaufruf foo::baz(); und man könnte meinen, da $this eben noch existiert hat, lässt es sich auch in baz() verwenden. Es ist ja schließlich auch dieselbe Klasse. Doch das täuscht. Ruft man eine Methode statisch auf, so wird diese Methode so angesehen, als ob es eine ganz normale sonstwo definierte Funktion sei, also ohne Klasse drumrum. In "normalen" Funktionen gibt es unter PHP aber per Default keinen Zugriff auf Variablen, die nicht innerhalb der Funktion definiert wurden. Also existiert auch kein $this und echo $this->x; erzeugt eine Fehlermeldung.

          Soweit die Theorie. Noch schnell ein Test mit dem Code ausgeführt und ... keine Fehlermeldung, stattdessen wird 42 ausgegeben. Was nun? War da nicht ein statischer Methodenaufruf foo::baz()?

          Des Rätsels Lösung ergibt sich aus dem Aufruf-Kontext von foo::baz(). Im Beispiel wurde es aus einer  Methode (bar()) einer vorhandenen Instanz ($bla) heraus aufgerufen. Dabei erfolgt also kein statischer Aufruf trotz der statischen Notation klassenname::methode().

          Entfernt man nun die Kommentarzeichen der letzten Zeile, gibt es wie erwartet eine Fehlermeldung. Dieser Aufruf ist nun wirklich statisch. (Zum Nachvollziehen der Fehlermeldungen bitte das error_reporting auf E_ALL stellen. Die Meldung ist "nur" eine Notice, die standardmäßig nicht ausgegeben werden.)

          Soweit so eigenartig. Nehmen wir ein zweites Beispiel

            
          class foo1 {  
            
            var $x;  
            
            function bar() {  
              $this->x = 42;  
              foo2::baz();  
            }  
          }  
            
          class foo2 {  
            
            var $x = 43;  
            
            function baz() {  
              echo $this->x;  
            }  
          }  
            
          $bla = new foo1();  
          $bla->bar();  
            
          //foo2::baz();
          

          Die Klassen foo1 und foo2 sind eigenständig. Keine verwandschaftlichen Beziehungen (Vererbung) bestehen zwischen ihnen. Trotzdem ergibt es keine Fehlermeldung sondern 42. Wieso kann die Methode einer Klasse auf Eigeschaften einer anderen Klasse und noch dazu über $this zugreifen? Hier wird offensichtlich die Methode baz() von foo2 einfach so in den Kontext von $bla gebracht. Also wieder kein statischer Aufruf.
          Der Test mit der auskommentierten Codezeile ergibt wie erwartet eine Fehlermeldung beim Zugriff auf $this. Dieser Aufruf von außerhalb einer Klasse war also statisch.

          echo "$verabschiedung $name";

          1. der aufuf klasse::methode() ist immer ein statischer aufruf.
            er erfolgt in dem namensraum (und damit dem speicher) in welchem dieser aufruf steht.

            du könntest ebenso anstatt des statischen aufrufes den programmquelltext der statischen methode hier hin schreiben, und es führt genau zu dem identischen ergebnis.

            daher ist auch die verwendung von this außerhalb einer instanz nicht möglich.

    2. Hallo,

      und eine methode kann es nur einmal in einer instanz geben, wenn sich diese nicht wie in java durch unterschiedliche signaturen unterscheiden. aber soweit ist php noch nicht.

      Das hat nichts mit »weit sein« zu tun - PHP ist eine Scriptsprache und besteht nicht auf festen Datentypen [1] - dafür kann man halt Funktionen schreiben, die dynamisch den Typ ihrer Argumente prüfen (ungetesteter (!) Beispielcode):

      function Funktion ($arg) {  
        if (gettype($arg) == 'int') {  
           // war ein integer  
        } else if (gettype($arg) == 'object') {  
          // war ein object  
          switch(strtolower(get_class($arg)) {  
            case 'TolleKlasse1':  
              // mach was tolles  
              break;  
            case 'TolleKlasse2':  
              // mach was tolles  
              break;  
            default:  
              // oops  
          }  
        } else {  
          // oops  
        }  
      }
      

      Viele Grüße,
      Christian

      [1] Ja, ich weiß, in PHP 5 kann man forcieren, dass ein Parameter ein Objekt einer bestimmten Klasse sein soll, allerdings geht dies m.W. nicht für primitive Datentypen.

      --
      "I have always wished for my computer to be as easy to use as my telephone; my wish has come true because I can no longer figure out how to use my telephone." - Bjarne Stroustrup
      1. genau so mache ichs auch.
        aber signaturen sind auch bei interpretern möglich. dies ist kein konzeptionelles problem, sondern eine sache des wollens.

        zu signaturen gehören nicht nur datentypen, sondern auch parameteranzahl und positionen. aber das ist ja erst einmal egal.

        signaturen wollen ja bewirken, daß eine methode mehrfach deklariert werden kann (logischer use-case), aber in der konkretisierung den umständen (parametern) angepaßt werden muß.

        dann könnte der interpreter zur laufzeit den passenden methodenaufruf benutzen.

        dies macht aber php noch nicht, daher werden methoden bisher immer überschrieben, so daß es immer nur eine gibt.

        1. echo $begrüßung;

          aber signaturen sind auch bei interpretern möglich. dies ist kein konzeptionelles problem, sondern eine sache des wollens.

          zu signaturen gehören nicht nur datentypen, sondern auch parameteranzahl und positionen. aber das ist ja erst einmal egal.

          Lassen wir mal den "Spezialfall" Type Hinting (gibt es seit PHP5) außer Betracht, dann bleibt nur die Möglichkeit übrig, anhand der Anzahl der Parameter verschiedene Methoden auszuwählen. Ansonsten sind weder beim Rückgabewert noch bei den Parametern Typangaben möglich. Es ist noch nicht einmal nötig, für alle beim Aufruf übergebenen Argumente einen Parameter in der Funktionsdefinition zu notieren.

          function foo() { ... }

          Man kann nun foo(), foo($arg), foo($arg1, $arg2) usw. aufrufen. (An die übergebenen Argumente gelangt man mit func_get_arg()/func_get_args().)

          Ebenso kann man foo beispielsweise mit n Parameter deklarieren

          function foo($param1, ..., $paramn) { ... }

          und mit n + x Argumenten aufrufen:

          foo($arg1, ..., $argn)
            foo($arg1, ..., $argn, $argx1, ... $argxn)

          Wenn man nun foo($arg1, ..., $argn) aufrufen möchte, wie soll PHP nun entscheiden, ob es die erste Deklaration (foo()) aufrufen soll oder doch die zweite (foo($param1, ..., $paramn))? Gut, man könnte festlegen, dass zuerst die Methode mit der passenden Parameteranzahl und dann die mit der nächstniedrigeren Parameteranzahl herangezogen wird. Aber warum sollte man solch eine Komplexität in dieses Thema bringen, wo es doch ausreichend ist, eine einzige Methode zu definieren und eine variable Anzahl Argumente zu übergeben?

          Man sollte bei solchen Diskussionen immernoch bedenken, dass PHP als "Personal Home Page Tools" entworfen wurde und dass dies der Haupteinsatzzweck ist. Wenn man signaturabhängige Methodenaufrufe benötigt sollte man zu einem Werkzeug greifen, dass dieses Feature bietet.

          echo "$verabschiedung $name";

          1. interpreter bedeutet nicht automatisch php. interpreter gibt es jede menge.

            signaturen sind üblicherweise statisch deklariert und werden zur compilezeit zugeordnet.
            interpreter compilieren zwar auch (intime), aber nicht zwingend.
            das problem ist vielmehr die nicht zwingende datendeklaration.

            trotzdem hat jede variable einen datentyp. dieser ergibt sich aus der zuweisung. der datentyp einer variablen kann mit utils ermittelt werden. daher sollte auch ein interpreter dies können. in php gibts da get_type oder so ähnlich.

            auch parameteranzahl gehören zur signatur. sonst würde sich diese ja nicht unterscheiden. dies ist ebenfalls eine statisch deklarierte angelegenheit.
            wenn der php interpreter die möglichkeit bietet, auf die deklaration der anzahl parameter zu verzichten, bedeutet dies nicht, das ein interpreter nicht in der lage wäre, unterschiedliche parameteranzahl als signaturen zu unterstützen.

            wenn also in php auf die formelle angabe der anzahl parameter verzichtet wird, muß und kann diese während der laufzeit von der methode selbst ermittelt werden. daher muß und kann nur die methode selbst unterscheiden, welche signatur aktuell gemeint ist, und mit einem switch-case gebilde selbst umgesetzt werden. so mache ich das auch.