Stefan: Constructor in einer Methode der Klasse erneut aufrufen?

Hallo,

ich habe mal wieder eine allgemeine Frage. Da ich hier bisher immer alle meine Fragen beantwortet bekommen habe, versuche ich es auch diesmal. Ich versuch mal zu erklären was mich beschäftigt:

Ich habe eine MySQL-Klasse, in der sich drei Methoden befinden.

1. __construct()
2. connect()
3. query()

Im Constructor führe ich nun die connect()-Methode aus, um die Verbindung zur Datenbank zu initialisieren und eine Datenbank auszuwählen. In der query()-Methode überprüfe ich mit der Funktion mysql_ping, ob eine Datenbank Verbindung besteht und wenn nicht, möchte ich diese erneut herstellen.

Nun zu meiner eigentlichen Frage:
Ist es legitim in der query()-Methode den Constructor mit $this->__construct(); neu auszuführen, falls die Datenbank-Verbindung nicht mehr besteht oder ist das ein schlechter Stil den Constructor in einer Methode der Klasse neu aufzurufen?

Eigentlich ist der Constructor ja eine normale Methode der Klasse. Von der Seite betrachtet dürfte es ja kein Problem sein ihn erneut aufzurufen. Auf der anderen Seite ist ein Constructor ja dazu gedacht Werte zu initialisieren, die die Klasse auf jedenfall benötigt. Den Constructor dann in einer Methode der Klasse aufzurufen spricht ja dann gegen die Logik, wofür der Constructor eigentlich gedacht ist.

Ich weiß, in meinem Beispiel könnte ich in der query()-Methode auch einfach die connect()-Methode aufrufen und damit erübrigt sich meine Frage aber das soll nur ein Beispiel sein um meine Frage zu verdeutlichen. Es ist mehr eine genrelle Frage bezüglich des Constructors.

  1. Tach!

    Ist es legitim in der query()-Methode den Constructor mit $this->__construct(); neu auszuführen, falls die Datenbank-Verbindung nicht mehr besteht oder ist das ein schlechter Stil den Constructor in einer Methode der Klasse neu aufzurufen?

    Im Prinzip ist das nur schlechter Stil.

    Eigentlich ist der Constructor ja eine normale Methode der Klasse. Von der Seite betrachtet dürfte es ja kein Problem sein ihn erneut aufzurufen.

    Richtig. Rein technisch sehe ich auch nichts, was dagegen spricht, den Constructor als normale Methode anzusehen. Wenn du den Parent-Constructor aufrufst, findet dabei auch nur ein normaler Methodenaufruf statt. Die Sonderstellung nimmt er nur im Zusammenhang mit dem new-Operator ein, als automatisch aufgerufene Methode nach dem Instantiieren des Objekts als solches.

    Auf der anderen Seite ist ein Constructor ja dazu gedacht Werte zu initialisieren, die die Klasse auf jedenfall benötigt. Den Constructor dann in einer Methode der Klasse aufzurufen spricht ja dann gegen die Logik, wofür der Constructor eigentlich gedacht ist.

    Eigentlich. Du kannst ja die Aufgaben so trennen, dass der Constructor selbst die nur einmalig benötigten Dinge ausführt und für weitergehende Sachen eigene Methoden aufruft. Diese Methoden können dann auch von anderswo aufgerufen werden, wenn du dir die Gewissensbisse bei Missbrauch des Constructors ersparen möchtest. Zudem ist der Name der Methode dann sprechender als "__construct".

    Ich weiß, in meinem Beispiel könnte ich in der query()-Methode auch einfach die connect()-Methode aufrufen und damit erübrigt sich meine Frage aber das soll nur ein Beispiel sein um meine Frage zu verdeutlichen. Es ist mehr eine genrelle Frage bezüglich des Constructors.

    In dem speziellen Fall kann es sinnvoll sein, die Verbindung erst herzustellen, wenn sie benötigt wird (lazy connect), also frühestens beim Maskieren (mysql(i)_real_escape_string).

    dedlfix.

  2. hi,

    Eigentlich ist der Constructor ja eine normale Methode der Klasse.

    I.d.R. ist der Konstruktor eine Klassenmethode, d.h., er wird mit dem Klassen-Namen aufgerufen. Andere Methoden sind Objekt-Methoden, die werden mit der Instanz der Klasse aufgerufen.

    Von der Seite betrachtet dürfte es ja kein Problem sein ihn erneut aufzurufen.

    Es gibt zwei Möglichkeiten: Entweder liefert der Konstruktor eine zweite Instanz der Klasse (Clone) oder er liefert exakt dieselbe Instanz (Singleton-Class).

    Um was für eine Klasse gehts bei Dir?

    Hotti

    1. Tach!

      I.d.R. ist der Konstruktor eine Klassenmethode, d.h., er wird mit dem Klassen-Namen aufgerufen. Andere Methoden sind Objekt-Methoden, die werden mit der Instanz der Klasse aufgerufen.

      PHP! Der Konstruktor kann erst aufgerufen werden, wenn die Instanz existiert. Anderenfalls könnte man im Konstruktor nicht auf deren Mitglieder zugreifen. Demzufolge ist der Konstruktor eine Objekt-Methode. Klassen-Mitglied ist alles was statisch ist, der Rest ist Objekt-Mitglied. Es gibt auch keine statischen Konstruktoren in PHP. Der Konstruktor ist lediglich eine speziell benannte Methode, die nach der Instantiierung aufgerufen wird.

      Es gibt zwei Möglichkeiten: Entweder liefert der Konstruktor eine zweite Instanz der Klasse (Clone) oder er liefert exakt dieselbe Instanz (Singleton-Class).

      Das ist bezogen auf PHP falsch. Eine Instantiierung ist kein Clone. Nicht die Konstruktor-Methode liefert die Instanz, sie wird aufgrund des Operators new erzeugt. Es ist nicht einmal notwendig, eine Konstruktor-Methode zu haben. Wenn ein Singleton gewünscht ist, muss man dazu eine statische Methode verwenden. Die Konstruktor-Methode ist dafür nicht geeignet.

      dedlfix.

      1. hi,

        Nicht die Konstruktor-Methode liefert die Instanz, sie wird aufgrund des Operators new erzeugt.

        Ah, verstehe. In PHP wird die Instanz mit new() erzeugt, nicht jedoch mit dem Konstruktor. Danke für die Info.

        Viele Grüße,
        Hotti

        1. Hello,

          Nicht die Konstruktor-Methode liefert die Instanz, sie wird aufgrund des Operators new erzeugt.

          Ah, verstehe. In PHP wird die Instanz mit new() erzeugt, nicht jedoch mit dem Konstruktor. Danke für die Info.

          Wie läuft denn eine Instantiierung deiner Meinung nach ab?

          Liebe Grüße aus dem schönen Oberharz

          Tom vom Berg

          --
           ☻_
          /▌
          / \ Nur selber lernen macht schlau
          http://bergpost.annerschbarrich.de
          1. hi Tom,

            Nicht die Konstruktor-Methode liefert die Instanz, sie wird aufgrund des Operators new erzeugt.

            Ah, verstehe. In PHP wird die Instanz mit new() erzeugt, nicht jedoch mit dem Konstruktor. Danke für die Info.

            Wie läuft denn eine Instantiierung deiner Meinung nach ab?

            der Sven hat zwar gesgt, ich soll meine Fresse halten, aber weil Du's bist:

            In c++ und in Perl wird die Instanz der Klasse mit dem Konstruktor erzeugt. In beiden Programmiersprachen gibt es auch den Destruktor. Mein c++ liegt schon etwas länger zurück, daher ein bischen mehr zu Perl: Der Konstruktor als Klassenmethode kann den Namen 'new()' kriegen, der Name kann aber auch anders lauten. In Perl-Klassen, die Variablen binden, heißen die Konstruktoren TIEHASH, TIEARRAY oder TIESCALAR, je nachdem. Der Destruktor jedoch heißt immer DESTROY und in meinen Modulen bekommt der auch immer was zu tun *G. Meiner Meinung nach ergibt das alles auch einen Sinn.

            Des Weiteren macht es in Perl auch gelegentlich Sinn, den Konstruktor so zu programmieren, dass dessen Aufruf über eine bereits erzeugte Instanz einen Clone zurückgibt, in diesem Fall erwartet der Konstruktor entweder den Namen der Klasse oder er ermittelt den Namen der Klasse selbst anhand der übergebenen Instanz. Ergo ist der Konstruktor in Perl keine Instanz(Objekt)-Methode, auch dann nicht, wenn er eine Instanz übergeben bekommt: Aus dieser wird lediglich der Name der Klasse ermittelt.

            Dass der Kontruktor in PHP eine völlig andere Bedeutung hat, wusste ich bis gestern tatsächlich nicht. Möglich, dass der POST aus meiner Unkenntnis heraus für Verwirrung gesorgt hat, letzeres war aber nicht meine Absicht und PHP sorgt selbst für genügend Verwirrung, was die Anzahl der Anfragen hier belegen.

            Allen PHP-Fuzzies wünsche ich viel Spaß bei der Arbeit ;)

            Hotti

            1. Hello Hotti,

              das scheint ja langsam ein philosophisches oder archäoligisches Problem zu werden. Graben wir doch mal in den Bits.

              Meiner Meinung nach könnte es so zusammenhängen:

              Eine Klasse ist nur ein Baumuster. Das liegt statisch vor, wenn das Programm läuft. Nun wird eine neue Instanz angefordert. Das geschieht durch

              newinstance = new(baumuster);

              Was passiert hier?
              Ich sag mal so: es kommt darauf an, nämlich ob es ein generisches Klassensystem (C++), ein typisierendes Klassensystem (Delphi) oder ein Interpretersystem (z.B. PHP) ist. Was in allen aber gleich ist:

              es wird Speicherplatz für die Klassenvariable allokiert, egal, wieviel daws jetzt ist.
                 Darüberhinaus wird i.a. auch Speicherplatz für die statischen Teile der Klasse allokiert
                 und diese werden dort hineinkopiert. Das sind einfache Variablen und z.B. die Adressen zu
                 den Methoden.

              Damit ist die (Kern-)Instanz gebildet, noch bevor irgendwelche Initialisierungen stattgefunden haben, mit Ausnahem der statischen Werte. Die stehen jetzt bereits hardcoded im Speicher.

              und jetzt kommt die Philosophie

              für standadisierte dynamische Teile der Klasse könnte bereits jetzt Speicherplatz allokiert
                 werden, aber vielleicht auch erst dann, wenn er tatsächlich angefordert wird von der Klasse

              Damit ist aber klar, der Konstruktor war noch gar nicht tätig. Dieser kann nämlich erst ins Spiel kommen, wenn er selber abgebildet worden ist, entweder durch Codekopie, oder durch Referenz auf den Standardkonstruktor.

              Wenn jetzt als nächstes die Kontrolle an den Konstruktor übergeben wird, kann er für neue Speicherall0kation sorgen und diese Fragmente innerhalb der statisch vorbereiteten Teile der Instanz eintragen. Wohin sollte der Konstruktor denn die Referenzen schreiben, wenn noch kein Variablenplatz ("Ankeradresse") dafür vorgesehen wäre?

              Ich mag OOP-Programmierung nicht, weil alle Systeme es "innen drin" anders machen. Aber das hat Bjarne Stroustup auch selber schon eingeräumt. Wenn er C++ nochmal entwickeln könnte, würd er es wieder ganz anders machen...

              Das große Problem ist jetzt nur, dass man wissen muss, welches System dem anderen (teilweise) zugrunde liegt. PHP ist nunmal in C programmier und das hat auch schon ein paar Eigenarten.

              So! Nun haben wir genügend Stoff, damit sich auch noch ein paar Andere über uns hermachen können :-)

              Liebe Grüße aus dem schönen Oberharz

              Tom vom Berg

              --
               ☻_
              /▌
              / \ Nur selber lernen macht schlau
              http://bergpost.annerschbarrich.de
              1. hi Tom,

                das scheint ja langsam ein philosophisches oder archäoligisches Problem zu werden. Graben wir doch mal in den Bits.

                Gerne. Aber eines vorweg: Das 'P' in Perl steht für Praxis und so halte ich das auch, ich bin ein Praktiker. Es stört mich nicht im Geringsten, wenn böse Zungen behaupten, dass Perl überhaupt kein OOP kann. Ich programmiere nicht der OOP willen, sondern um Aufgaben zu lösen.

                Meiner Meinung nach könnte es so zusammenhängen:

                Eine Klasse ist nur ein Baumuster. Das liegt statisch vor, wenn das Programm läuft. Nun wird eine neue Instanz angefordert. Das geschieht durch

                newinstance = new(baumuster);

                Was passiert hier?
                Ich sag mal so: es kommt darauf an, nämlich ob es ein generisches Klassensystem (C++), ein typisierendes Klassensystem (Delphi) oder ein Interpretersystem (z.B. PHP) ist. Was in allen aber gleich ist:

                es wird Speicherplatz für die Klassenvariable allokiert, egal, wieviel daws jetzt ist.
                   Darüberhinaus wird i.a. auch Speicherplatz für die statischen Teile der Klasse allokiert
                   und diese werden dort hineinkopiert. Das sind einfache Variablen und z.B. die Adressen zu
                   den Methoden.

                Damit ist die (Kern-)Instanz gebildet, noch bevor irgendwelche Initialisierungen stattgefunden haben, mit Ausnahem der statischen Werte. Die stehen jetzt bereits hardcoded im Speicher.

                Siehst Du, da ist einiges im Argen. Aus dem Source aller Methoden der Klasse wird CODE erzeugt, der im Speicher rumliegt. Es werden jedoch nicht immer alle Methoden gebraucht. Außerdem kostet das Compilieren Zeit. Wie wärs damit, den Code erst zu erzeugen, wenn die Methode gebraucht wird? In Perl geht das prima zu machen, dafür gibts AutoLoader, SelfLoader.

                Zugegeben, so manches Verfahren wirkt in Perl einwenig verstaubt und meine Module/Scripts erwecken mitunter den Anschein eines fossilen Charakters ;) Aber sie erfüllen ihre Bestimmung plattformunabhängig ung abwärtskompatibel bis zu einer Perl-Version, die es seit 2001 gibt.

                Z.B. bin ich seit gestern dran, den AutoLoader so einzusetzen, dass Methoden, welche nicht immer gebraucht werden (Content-Management...) als Source aus einem Hash geladen werden, welcher über eine TIE-Klasse an eine Binär-Datei gebunden ist. Ein Bench, der Vergleich, zehntausend (!) Klassenmethoden auf einmal zu kompilieren um dann nur eine auszuführen, viel erwartungsgemäß um Längen schlechter aus, als eben nur das Compilieren der einen verlangten Methode. Wobei meine TIE-Klasse nicht die komplette Datei in den Hauptspeicher lädt, sondern nur den Index mit Offsetangaben zur Binary, somit wird auch erst auf Anforderung die Source der Methode in den RAM geladen.

                und jetzt kommt die Philosophie

                für standadisierte dynamische Teile der Klasse könnte bereits jetzt Speicherplatz allokiert
                   werden, aber vielleicht auch erst dann, wenn er tatsächlich angefordert wird von der Klasse

                Was heißt 'könnte'!?, machen! Siehe oben ;)

                Damit ist aber klar, der Konstruktor war noch gar nicht tätig. Dieser kann nämlich erst ins Spiel kommen, wenn er selber abgebildet worden ist, entweder durch Codekopie, oder durch Referenz auf den Standardkonstruktor.

                Mein Konstruktor heißt TIEHASH und der war bis hierhin sehr wohl schon unverschämt aktiv ;)

                Wenn jetzt als nächstes die Kontrolle an den Konstruktor übergeben wird, kann er für neue Speicherall0kation sorgen und diese Fragmente innerhalb der statisch vorbereiteten Teile der Instanz eintragen.

                Nein. Mein Konstruktor hat ferig, der kriegt nichts mehr übergeben.

                Ich mag OOP-Programmierung nicht, weil alle Systeme es "innen drin" anders machen.

                Programmiere nicht der OOP Willen.

                Das große Problem ist jetzt nur, dass man wissen muss, welches System dem anderen (teilweise) zugrunde liegt. PHP ist nunmal in C programmier und das hat auch schon ein paar Eigenarten.

                Kein Problem, Perl ist auch in c geschrieben. Wenn ich in c Speicher allociere, muss ich die Länge und den Datentyp angeben, dann kriege ich einen Zeiger auf die Speicheradresse. Perl allociert selbst den Speicher, in dem Moment, wo eine Variable ins Leben gerufen wird. Erst dann kann ich eine Referenz erzeugen, welche auf den Speicherplatz zeigt, aber auf diese Referenz kann ich in Perl später alles Mögliche schreiben (was in c nicht zu machen ist) und ich kann es mir aussuchen, ob ich eine neue Variable ins Leben rufe oder in einer bestehenden Referenz weitere Referenzen hinterlege.

                Perl-Praxis: Die Instanz einer Klasse ist eine Referenz. Attribute sind nichts Anderes als Namen für weitere Referenzen. Attribute können auch Instanzen fremder Klassen sein, deren Methoden entweder geerbt oder delegiert werden.

                So! Nun haben wir genügend Stoff, damit sich auch noch ein paar Andere über uns hermachen können :-)

                Lass die nur machen. Im Gegenzug dürfen auch wir uns über die Anderen lustig machen, heimlich oder offen. Allein die Namen der in PHP unzähligen Built-In-Funktionen haben mit schon so manches Schmunzeln abgerungen ;)

                void gaeste_buch_mysql(firstname, lastname, email, subject, dbh, charset, message, link, link_title, link_descr);

                Hotti

              2. Tach!

                das scheint ja langsam ein philosophisches oder archäoligisches Problem zu werden. Graben wir doch mal in den Bits.

                Zu tief graben ist für das allgemeine Verständnis zunächst nicht notwendig. Für den Anfang reicht zu wissen, dass 1 + 1 eine 2 als Ergebnis hat und nicht was der Prozessor dabei in seinen Registern anstellt. Das ist nicht komplett uninteressant, nur eben nicht jetzt.

                Eine Klasse ist nur ein Baumuster. Das liegt statisch vor, wenn das Programm läuft.

                Das Wort statisch würde ich an dieser Stelle meiden. Nahezu jeder Programmcode liegt statisch vor. Es ist also keine Besonderheit des Baumusters. Den Begriff statisch brauchen wir aber noch an anderer Stelle, wenn es als Synonym für Klassen-Mitglieder verwendet wird (im Gegensatz zu den Objekt-Mitglieder). Der Code einer statischen Methode (also Klassenmethode) liegt ebenso fest im Code-Teil wie der einer Instanzmethode.

                Auch ist eine Klasse weit mehr als ein Baumuster. Der Bauplan eines Gebäudes beschreibt die Räume und andere Einrichtungen (zum Beispiel dass Toilettenspülkästen eingemauert sind). Er sagt aber nichts zur anschließenden Verwendung aus. Er beschreibt also nur Eigenschaften und es fehlen ihm alle Methoden. Beim Instantieren ist allerdings nur der Bauplan wichtig für das Erstellen der Instanz und ihrer Eigenschaften. Der Konstruktor ist dann der Einzug mit dem Platzieren der Möbel im Haus. Danach kommt das Wohnen.

                Nun wird eine neue Instanz angefordert. Das geschieht durch
                   newinstance = new(baumuster);

                Die PHP-Syntax ist zwar eine andere, aber egal.

                Was passiert hier?
                Ich sag mal so: es kommt darauf an, nämlich ob es ein generisches Klassensystem (C++), ein typisierendes Klassensystem (Delphi) oder ein Interpretersystem (z.B. PHP) ist. Was in allen aber gleich ist:

                Du deutest zwar hier eine Trennung nach Systemen an, aber führst in den nachfolgenden Teilen nicht aus, was dabei die Unterschiede sind/sein sollen.

                es wird Speicherplatz für die Klassenvariable allokiert, egal, wieviel daws jetzt ist.

                "Klassenvariable"? Den Begriff verwendet man üblicherweise für eine statische Variable einer Klasse. Du meinst, es wird eine Instanz erstellt. Ob dabei Speicher benötigt wird, ist für die Diskussion nicht weiter relevant. Ebensowenig muss die Instanz in einer Variable abgelegt werden. Die Instanz kann auch direkt erzeugt, verwendet und anschließend fallengelassen werden, ohne dass jemals eine Variable darauf verwiesen hätte ((new Foo)->bar(); in PHP erst ab 5.4). Deswegen spreche ich von Instanz und nicht Instanzvariable, denn dieser Begriff wäre eher ein Synonym für eine Variable in einer Instanz (auch Objekt-Variable). Allerdings gebe ich zu, dass die Sprache hier nicht eindeutig genug verwendet wird. Eine Integer-Variable ist eine Variable, die einen Integer-Wert enthält, eine Instanz-Variable demzufolge eine Variable, die auf eine Instanz verweist. Hier muss man dann aus dem Kontext erkennen, was gemeint ist.

                Darüberhinaus wird i.a. auch Speicherplatz für die statischen Teile der Klasse allokiert
                   und diese werden dort hineinkopiert. Das sind einfache Variablen und z.B. die Adressen zu
                   den Methoden.

                Es sieht mir so aus, als ob das ein Zitat ohne Quellenangabe ist, oder ist das einfach nur eine Aussage deinerseits, die eingerückt wurde? Statische Teile einer Klasse werden sinnvollerweise zum Programmstart oder bei nachgeladenen Code-Bibliotheken direkt nach dem Laden initialisiert, ansonsten wären die statischen Teile aus der Sicht des Programms ähnlich wie Schrödingers Katze, vielleicht schon initialisert, vielleicht aber auch nicht.

                Damit ist die (Kern-)Instanz gebildet, noch bevor irgendwelche Initialisierungen stattgefunden haben, mit Ausnahem der statischen Werte. Die stehen jetzt bereits hardcoded im Speicher.

                Was wie im Speicher steht, ist für unsere Betrachtungen nicht relevant. Hartkodiert sind eher Konstanten in kompilierten Programmen. Alles andere wird vermutlich zur Laufzeit im Datenteil des Speichers angelegt. Aber um diese Feinheiten brauchen sich PHP- und Programmierer anderer Systeme üblicherweise keine Sorgen zu machen, wenn sie darauf keinen Einfluss nehmen können. (Vielleicht ist C hier die Ausnahme - das kenne ich kaum.)

                Damit ist aber klar, der Konstruktor war noch gar nicht tätig. Dieser kann nämlich erst ins Spiel kommen, wenn er selber abgebildet worden ist, entweder durch Codekopie, oder durch Referenz auf den Standardkonstruktor.

                Welches System kopiert denn Code in die Instanzen eines Objekts? Gängiges Vorgehen bei der OOP ist, dass für die Klasse eine Tabelle existiert, die zu jeder Methode auf den jeweiligen Code verweist (virtual method table). Durch Vererbung können ja Teile des Codes den Elternklassen "gehören".

                Was ist jetzt für dich der Standardkonstruktor? Ist das der Teil im System, der die Instanz erzeugt oder die Konstruktor-Methode einer Superklasse, von der alle anderen Klassen abgeleitet werden?

                Wenn jetzt als nächstes die Kontrolle an den Konstruktor übergeben wird, kann er für neue Speicherall0kation sorgen und diese Fragmente innerhalb der statisch vorbereiteten Teile der Instanz eintragen. Wohin sollte der Konstruktor denn die Referenzen schreiben, wenn noch kein Variablenplatz ("Ankeradresse") dafür vorgesehen wäre?

                Auch hier gehst du wieder zu sehr ins Detail. Ob Speicher allokiert werden muss (für die Instantiierung von Referenztypen), bereits da ist (Wertetypen, deren Größe beim Erzeugen der Instanz ja bereits bekannt ist und deren Speicher gleich mit angefordert werden kann), von einer Wurst abgeschnitten wird oder aus der Cloud kommt, ist alles nicht so wichtig für das Verständnis der Aufgaben einer Konstruktor-Methode. Vereinfacht würde ich das so formulieren:

                Wenn jetzt als nächstes die Kontrolle an den Konstruktor übergeben wird, kann er für weitere Initialisierungen innerhalb der Instanz sorgen. Wie soll der Konstruktor denn die Eigenschaften ansprechen, wenn diese noch nicht zusammen mit der Instanz existiert?

                Ich mag OOP-Programmierung nicht, weil alle Systeme es "innen drin" anders machen.

                Na das ist ja kein Alleinstellungsmerkmal zwischen OOP-Systemen. Ziemlich viele Systeme (Programmiersprachen, unterschiedliche Compiler einer Sprache, Betriebssysteme, Anwendungen) machen "innen drin" so manches anders als andere. Du dürftest mit der Logik also gar nichts mögen.

                Das große Problem ist jetzt nur, dass man wissen muss, welches System dem anderen (teilweise) zugrunde liegt.

                Nein, muss man nicht. Man kann durchaus die Dinge im Untergrund wegabstrahieren.

                dedlfix.

                1. Hello,

                  das scheint ja langsam ein philosophisches oder archäoligisches Problem zu werden. Graben wir doch mal in den Bits.

                  Zu tief graben ist für das allgemeine Verständnis zunächst nicht notwendig. Für den Anfang reicht zu wissen, dass 1 + 1 eine 2 als Ergebnis hat und nicht was der Prozessor dabei in seinen Registern anstellt. Das ist nicht komplett uninteressant, nur eben nicht jetzt.

                  Hier geht es nicht um oberflächliches "allgemeines Verständnis in der Anwendug" sondern um das eigentliche Problem: wie funktieriert das überhaupt im Hintergrund.

                  Eine Klasse ist nur ein Baumuster. Das liegt statisch vor, wenn das Programm läuft.

                  Das Wort statisch würde ich an dieser Stelle meiden. Nahezu jeder Programmcode liegt statisch vor. Es ist also keine Besonderheit des Baumusters.

                  Genau! es ist eine kennzeichnende Eigenschaft. Das Baumuster (zumindest der Masterklasse) ist schon bei Programmstart im Speicher nachlesbar.

                  Nun wird eine neue Instanz angefordert. Das geschieht durch

                  newinstance = new(baumuster);

                  Die PHP-Syntax ist zwar eine andere, aber egal.

                  Von PHP sprach ich eben auch noch nicht. Da lautet die Syntax aber ziemlich ähnlich:

                  $newinstance = new Baumuster();

                  Was passiert hier?
                  Ich sag mal so: es kommt darauf an, nämlich ob es ein generisches Klassensystem (C++), ein typisierendes Klassensystem (Delphi) oder ein Interpretersystem (z.B. PHP) ist. Was in allen aber gleich ist:

                  Du deutest zwar hier eine Trennung nach Systemen an, aber führst in den nachfolgenden Teilen nicht aus, was dabei die Unterschiede sind/sein sollen.

                  Das würde auch zu weit führen, es auszuführen. Es muss reichen, das hier anzudeuten.

                  es wird Speicherplatz für die Klassenvariable allokiert, egal, wieviel daws jetzt ist.

                  "Klassenvariable"?

                  Korrektur: Für die Instanzvariable der Klasse. Nun zufrieden? :-)

                  Darüberhinaus wird i.a. auch Speicherplatz für die statischen Teile der Klasse allokiert
                     und diese werden dort hineinkopiert. Das sind einfache Variablen und z.B. die Adressen zu
                     den Methoden.

                  Statische Teile einer Klasse werden sinnvollerweise zum Programmstart oder bei nachgeladenen Code-Bibliotheken direkt nach dem Laden initialisiert, ansonsten wären die statischen Teile aus der Sicht des Programms ähnlich wie Schrödingers Katze, vielleicht schon initialisert, vielleicht aber auch nicht.

                  Diese Aussage verstehe ich jetzt überhaupt nicht

                  Damit ist die (Kern-)Instanz gebildet, noch bevor irgendwelche Initialisierungen stattgefunden haben, mit Ausnahem der statischen Werte. Die stehen jetzt bereits hardcoded im Speicher.

                  Was wie im Speicher steht, ist für unsere Betrachtungen nicht relevant.

                  Nur das ist hier relevant: Was ist hart und was ist weich?

                  Hartkodiert sind eher Konstanten in kompilierten Programmen.

                  Oder sogenannte "variable Konstenten"? Was es da alles gibt, würde ein eigenes Buch füllen.

                  Alles andere wird vermutlich zur Laufzeit im Datenteil des Speichers angelegt.

                  Genau darum geht es. Zur Laufzeit wird alles, außer dem Baumuster, veranlasst. Aber was wird jetzt bereits von außen bei der Bildung der Instanz und was wird erst aus der Instanz heraus nach Bildung dieser veranlasst. DAS ist hier die Kernfrage gewesen.

                  Aber um diese Feinheiten brauchen sich PHP- und Programmierer anderer Systeme üblicherweise keine Sorgen zu machen, wenn sie darauf keinen Einfluss nehmen können. (Vielleicht ist C hier die Ausnahme - das kenne ich kaum.)

                  **********
                  Und darum ging es auch gar nicht. Also lenke bitte nicht ab. Es ging einzig und alleine darum, ob die Instanz durch "new" gebildet wird, oder aber durch den Konstruktor einer Klasse!
                  **********

                  Damit ist aber klar, der Konstruktor war noch gar nicht tätig. Dieser kann nämlich erst ins Spiel kommen, wenn er selber abgebildet worden ist, entweder durch Codekopie, oder durch Referenz auf den Standardkonstruktor.

                  Welches System kopiert denn Code in die Instanzen eines Objekts? Gängiges Vorgehen bei der OOP ist, dass für die Klasse eine Tabelle existiert, die zu jeder Methode auf den jeweiligen Code verweist (virtual method table). Durch Vererbung können ja Teile des Codes den Elternklassen "gehören".

                  Dazu muss dieser Code aber für die Klasse angepasst vorliegen. Also entweder als Kopie oder als Verweis auf den vorbereiteten Code. Nix anderes habe ich gesagt.

                  Was ist jetzt für dich der Standardkonstruktor? Ist das der Teil im System, der die Instanz erzeugt oder die Konstruktor-Methode einer Superklasse, von der alle anderen Klassen abgeleitet werden?

                  Kannst Du jetzt programmieren oder nicht? Was macht denn ein leerer Konstruktor einer Klasse?
                  Das ist nichts anderes, als ein FarReturn, wenn Du mal genauer hingesehen hast.

                  Erst ein angepasster Kondtruktor einer Klasse tut etwas. I.d.R. initialisiert und erzeugt/initilisiert er diverse Variablen und Datenkonstrukte.

                  Also muss der angepasste Konstruktor irgendwo als Codeblock bestehen. Der kann statisch sein.

                  Wenn dieser nun noch weitere übergeornete Kontruktoren anzieht (i.d.R. wegen Vererbung), dann entsteht der endgültige Code im Speicher erst bei der Instantiierung des Konstruktors. Vorher war der noch gar nicht zusammngebaut.

                  Wenn jetzt als nächstes die Kontrolle an den Konstruktor übergeben wird, kann er für neue Speicherallokationen sorgen und diese Fragmente innerhalb der statisch vorbereiteten Teile der Instanz eintragen. Wohin sollte der Konstruktor denn die Referenzen schreiben, wenn noch kein Variablenplatz ("Ankeradresse") dafür vorgesehen wäre?

                  Auch hier gehst du wieder zu sehr ins Detail.

                  Ich versuche, genauer hinzugucken, und mich nicht in Allgemeinplätzen zu halten. Denn die eigentliche Frage war ja:

                  *********
                  Es ging einzig und alleine darum, ob die Instanz durch "new" gebildet wird, oder aber durch den Konstruktor einer Klasse!
                  *********

                  Ob Speicher allokiert werden muss (für die Instantiierung von Referenztypen), bereits da ist (Wertetypen, deren Größe beim Erzeugen der Instanz ja bereits bekannt ist und deren Speicher gleich mit angefordert werden kann), von einer Wurst abgeschnitten wird oder aus der Cloud kommt, ist alles nicht so wichtig für das Verständnis der Aufgaben einer Konstruktor-Methode. Vereinfacht würde ich das so formulieren:

                  Nö, Du redest im Kreis. Genau davon hängt die Beantwortung der Frage ab.

                  Es ist immer wichtig, was zuerst da war: die Wurst oder der Bauch.

                  Liebe Grüße aus dem schönen Oberharz

                  Tom vom Berg

                  --
                   ☻_
                  /▌
                  / \ Nur selber lernen macht schlau
                  http://bergpost.annerschbarrich.de
                  1. Tach!

                    Zu tief graben ist für das allgemeine Verständnis zunächst nicht notwendig. Für den Anfang reicht zu wissen, dass 1 + 1 eine 2 als Ergebnis hat und nicht was der Prozessor dabei in seinen Registern anstellt. Das ist nicht komplett uninteressant, nur eben nicht jetzt.
                    Hier geht es nicht um oberflächliches "allgemeines Verständnis in der Anwendug" sondern um das eigentliche Problem: wie funktieriert das überhaupt im Hintergrund.

                    Nicht oberflächlich sondern abstrahiert. Selbst mit deiner Beschreibung des Hintergrundes berührst du nur eine Möglichkeit, die sich noch dazu auf kein konkretes System bezieht, obwohl du ja selbst eingeräumt hast, dass jedes System intern anders arbeitet.

                    Eine Klasse ist nur ein Baumuster. Das liegt statisch vor, wenn das Programm läuft.
                    Das Wort statisch würde ich an dieser Stelle meiden. Nahezu jeder Programmcode liegt statisch vor. Es ist also keine Besonderheit des Baumusters.
                    Genau! es ist eine kennzeichnende Eigenschaft. Das Baumuster (zumindest der Masterklasse) ist schon bei Programmstart im Speicher nachlesbar.

                    Ja, es ist vorhanden. Mehr muss man nicht wissen. Ob es vom System im Arbeitsspeicher gehalten oder von einem anderen Medien gelesen oder zwischen ihnen hin- und hergeschubst wird, spielt keine Rolle. Wenn du mit Details anfängst, musst du sie auch vollständig klären, sonst wirst du "oberflächlich". Oder du lässt es weg, weil du eben grad nicht die Rolle eines Compilerbauers oder Betriebssystemprogrammierer einnimmst, sondern aus der Perspektive eines Hochsprachenprogrammierers schaust.

                    Ich sag mal so: es kommt darauf an, nämlich ob es ein generisches Klassensystem (C++), ein typisierendes Klassensystem (Delphi) oder ein Interpretersystem (z.B. PHP) ist. Was in allen aber gleich ist:
                    Du deutest zwar hier eine Trennung nach Systemen an, aber führst in den nachfolgenden Teilen nicht aus, was dabei die Unterschiede sind/sein sollen.
                    Das würde auch zu weit führen, es auszuführen. Es muss reichen, das hier anzudeuten.

                    Schade, denn ich wüsste grad nicht, was da die Unterschiede sind und inwieweit sie relevant sind.

                    es wird Speicherplatz für die Klassenvariable allokiert, egal, wieviel daws jetzt ist.
                    "Klassenvariable"?
                    Korrektur: Für die Instanzvariable der Klasse. Nun zufrieden? :-)

                    Nein, nur Instanz, nicht Instanzvariable. Ansonsten wäre jeder auch nur intern und temporär verwendeter Datenspeicher bereits eine Variable, was aus Sicht des Hochsprachenprogrammierers nicht der Fall ist. Eine Variable kommt entweder gar nicht oder erst nach dem Instantiieren (nebst Konstruktoraufruf) ins Spiel, wenn im Code eine Zuweisung notiert ist und diese ausgeführt wird.

                    Darüberhinaus wird i.a. auch Speicherplatz für die statischen Teile der Klasse allokiert
                       und diese werden dort hineinkopiert. Das sind einfache Variablen und z.B. die Adressen zu
                       den Methoden.
                    Statische Teile einer Klasse werden sinnvollerweise zum Programmstart oder bei nachgeladenen Code-Bibliotheken direkt nach dem Laden initialisiert, ansonsten wären die statischen Teile aus der Sicht des Programms ähnlich wie Schrödingers Katze, vielleicht schon initialisert, vielleicht aber auch nicht.
                    Diese Aussage verstehe ich jetzt überhaupt nicht

                    Nimm einfach mit, dass statische Dinge zum Programmstart (oder spätenstens wenn der Code der Klasse geladen wird) initialisiert werden, bevor die Kontrolle an das eigentliche Programm übergeben wird, und nicht erst, wenn die erste Instanz einer Klasse erzeugt wird. Denn die Initialisierung muss bereits stattgefunden haben, oder das Programm kann sich nicht darauf verlassen, dass statische Dinge da sind. Du hast das so beschrieben, dass die statischen Dinge die Folge einer Instantiierung ist. Sie existieren jedoch auch ohne jegliche Instanzen.

                    Genau darum geht es. Zur Laufzeit wird alles, außer dem Baumuster, veranlasst. Aber was wird jetzt bereits von außen bei der Bildung der Instanz und was wird erst aus der Instanz heraus nach Bildung dieser veranlasst. DAS ist hier die Kernfrage gewesen.

                    Und die Klärung ist recht einfach. Spezialfälle außen vor gelassen, wird alles, was durch Syntax im Klassencode notiert ist und weder statisch noch Methode ist, bei der Instantiierung bereitgestellt. Das "wo" und "wie" ist für eine Hochsprache, die sich nicht um die Speicherverwaltung kümmern muss, uninteressant. Dann kommt eine Konstruktor-Methode zur Ausführung, so sie existiert, die dann das macht, was noch individuell nötig oder aus Sicht der Anwendung komplexer zu initialisieren ist.

                    Was ist jetzt für dich der Standardkonstruktor? Ist das der Teil im System, der die Instanz erzeugt oder die Konstruktor-Methode einer Superklasse, von der alle anderen Klassen abgeleitet werden?
                    Kannst Du jetzt programmieren oder nicht? Was macht denn ein leerer Konstruktor einer Klasse?

                    Hätte ja sein können, dass du wieder ungenau mit den Begriffen hantierst. Bei PHP gibt es nur einen oder keinen Konstruktor (PHP4 und seine Hinterlassenschaft außen vor gelassen). Der Begriff Standard-Konstruktor ist damit gegenstandslos. In anderen Systemen, die Methoden gleichen Namens mit unterschiedlichen Signaturen erlauben, kann es einen Standard-Konstruktor geben, explizit in der Klasse oder implizit durch den Compiler. Hier, nehme ich an, hat der Konstruktor auch die Aufgabe, die Instanz zu erstellen, bevor der Code in ihm ausgeführt wird. Ich habe es auch so gelernt: "Der Konstruktor erstellt die Instanz." Das mag bei einigen Systemen zutreffen, bei PHP aber eben nicht, denn da gibt es keinen Konstruktor-Zwang. Wer es letzlich macht und ob überhaupt ein impliziter Standard-Konstruktor vom Compiler erstellt wird, ist auch nicht weiter wichtig, weil man es nicht beeinflussen kann.

                    Das ist nichts anderes, als ein FarReturn, wenn Du mal genauer hingesehen hast.

                    Ach du Heimatland, jetzt steigst du auch noch zum Prozessor hinab. Geh ruhig, ich bleibe hier oben. Katakomben in Paris anschauen mag interessant und an heißen Sommertagen erfrischend sein, aber zum täglichen Leben braucht man es nicht (es sei denn, man ist Stadtführer oder Historiker).

                    Erst ein angepasster Kondtruktor einer Klasse tut etwas. I.d.R. initialisiert und erzeugt/initilisiert er diverse Variablen und Datenkonstrukte.
                    Also muss der angepasste Konstruktor irgendwo als Codeblock bestehen. Der kann statisch sein.
                    Wenn dieser nun noch weitere übergeornete Kontruktoren anzieht (i.d.R. wegen Vererbung), dann entsteht der endgültige Code im Speicher erst bei der Instantiierung des Konstruktors. Vorher war der noch gar nicht zusammngebaut.

                    Bitte was? Du beschreibst das ja so, als ob der Code im Speicher erst sortiert werden müsse, bevor er abgearbeitet werden kann. Warum machst du so etwas? Der Code wird irgendwie im Speicher stehen und der Compiler weiß genau, welche Instruktionen er in welcher Reihenfolge erzeugen und mit welchen Sprungbefehlen versehen muss. Da habe ich volles Vertrauen in ihn.

                    dedlfix.

            2. Tach!

              Sofern nicht anders angegeben beziehe ich mich im folgenden nicht speziell auf eine bestimmte Sprache.

              In c++ und in Perl wird die Instanz der Klasse mit dem Konstruktor erzeugt. In beiden Programmiersprachen gibt es auch den Destruktor.

              So wird es allenthalben verkündet. Ich denke, es ist angebracht zwischen dem Teil, der ein Objekt instantiiert und der Methode, die danach vom System aufgerufen wird, zu unterscheiden. Auch andere Sprachen können nur dann auf Instanzmitglieder zugreifen, wenn die Instanz existiert. Somit ist ein Konstruktor, der nach dem Instantiieren aufgerufen wird immer eine Instanzmethode.

              Weiterhin gibt es (vielleicht nicht in allen Sprachen) statische Teile in Klassen. Auch statische Konstruktoren sind bekannt (z.B. C#), welche aber nur auf statische Teile der Klasse zugreifen können. Statische Konstruktoren werden zum Programmstart aufgerufen. (Ob es davon Abweichungen gibt, ist mir nicht bekannt. Sinnvoll wärte es jedenfalls, dass die statischen Teile intialisiert sind, bevor der Rest vom Programm darauf zugreift.)

              Auch Destruktoren sind genau genommen nur spezielle Methoden, die vor dem Aufräumen durch das System aufgerufen werden und sich nicht selbst wegräumen sondern nur die vom Objekt (nebst seiner Konstruktor-Methode) erzeugten Teile.

              Des Weiteren macht es in Perl auch gelegentlich Sinn, den Konstruktor so zu programmieren, dass dessen Aufruf über eine bereits erzeugte Instanz einen Clone zurückgibt

              Clone sind auch unter PHP gelegentlich sinnvoll und bei Objekten möglich. Auch dabei kann (aber muss nicht) es eine speziell benannte Methode geben, die nach dem Erzeugen der Kopie aufgerufen wird. Die Kopie ist natürlich eine neue Instanz und die spezielle Methode hat ähnliche Aufgaben wie die Konstruktor-Methode, weswegen sie auch Copy-Konstruktor genannt wird. Aber auch hier ist diese Methode nicht notwendig, um eine Instanz zu erzeugen sondern ebenfalls nur optionales Beiwerk.

              in diesem Fall erwartet der Konstruktor entweder den Namen der Klasse oder er ermittelt den Namen der Klasse selbst anhand der übergebenen Instanz. Ergo ist der Konstruktor in Perl keine Instanz(Objekt)-Methode, auch dann nicht, wenn er eine Instanz übergeben bekommt: Aus dieser wird lediglich der Name der Klasse ermittelt.

              Vielleicht ist dieser spezielle Konstruktor eine Klassenmethode, aber eine "ganz normale" Konstruktor-Methode kann nur eine Instanzmethode sein, weil sie auf die Instanz angewiesen ist. Auch PHPs __clone()-Methode (der Copy-Konstruktor) kann nur eine Instanzmethode sein, weil auch zu dessem Aufruf die Instanz bereits kopiert sein muss. Die Kopie selbst wird aufgrund des Schlüsselwortes clone erzeugt.

              Dass der Kontruktor in PHP eine völlig andere Bedeutung hat, wusste ich bis gestern tatsächlich nicht.

              Völlig anders ist die Bedeutung nicht, aber man kann eben nicht mit Nichtwissen auf die Gegebenheiten einer anderen Sprache schließen und diese als ganz selbstverständlich darstellen.

              dedlfix.

    2. Moin!

      Eigentlich ist der Constructor ja eine normale Methode der Klasse.

      I.d.R. ist der Konstruktor eine Klassenmethode, d.h., er wird mit dem Klassen-Namen aufgerufen. Andere Methoden sind Objekt-Methoden, die werden mit der Instanz der Klasse aufgerufen.

      Von der Seite betrachtet dürfte es ja kein Problem sein ihn erneut aufzurufen.

      Es gibt zwei Möglichkeiten: Entweder liefert der Konstruktor eine zweite Instanz der Klasse (Clone) oder er liefert exakt dieselbe Instanz (Singleton-Class).

      Um was für eine Klasse gehts bei Dir?

      Wenn du von PHP keine Ahnung hast: Einfach mal Fresse halten!

      - Sven Rautenberg

  3. Moin!

    Ich habe eine MySQL-Klasse, in der sich drei Methoden befinden.

    1. __construct()
    2. connect()
    3. query()

    Das ist mindestens noch eine Methode zu wenig, denn escaping ist zwingend notwendig, benötigt dafür aber ebenfalls die bestehende Connection.

    Im Constructor führe ich nun die connect()-Methode aus, um die Verbindung zur Datenbank zu initialisieren und eine Datenbank auszuwählen. In der query()-Methode überprüfe ich mit der Funktion mysql_ping, ob eine Datenbank Verbindung besteht und wenn nicht, möchte ich diese erneut herstellen.

    Nun zu meiner eigentlichen Frage:
    Ist es legitim in der query()-Methode den Constructor mit $this->__construct(); neu auszuführen, falls die Datenbank-Verbindung nicht mehr besteht oder ist das ein schlechter Stil den Constructor in einer Methode der Klasse neu aufzurufen?

    Du hast die Methode connect(). Rufe die auf.

    Besser noch: Habe eine Methode getConnection(), die alle notwendigen Prüfungen erledigt. Der Constructor legt dann nur noch die übergebenen DB-Zugangsdaten in der Klasse ab, und alle öffentlichen Methoden rufen intern die getConnection-Methode auf, um das Connection-Handle oder die Mysqli-Klasseninstanz zu erhalten.

    Die Methode getConnection() prüft zunächst, ob eine Connection existiert. Wenn nein, verbindet sie die Datenbank, speichert das Handle intern ab, und gibt es zurück.

    Wenn ja, wird die Connection gepingt. Ist das erfolgreich, gibts die Connection ebenfalls zurück, ansonsten wird neu connected.

    Vorteil: Die DB-Connection wird nur dann aufgebaut, wenn sie wirklich benötigt wird, nämlich beim ersten Query oder Escaping.

    Ich weiß, in meinem Beispiel könnte ich in der query()-Methode auch einfach die connect()-Methode aufrufen und damit erübrigt sich meine Frage aber das soll nur ein Beispiel sein um meine Frage zu verdeutlichen. Es ist mehr eine genrelle Frage bezüglich des Constructors.

    Ich halte es für sehr kritisch, im Konstruktor intensivere Arbeiten zu erledigen. Ich habe schon Klassen gesehen, die einfach nur durch new Klasse() komplette Seiten mit allen DB-Abfragen ausgegeben haben, ohne dass nach dem Instanziieren noch irgendeine Methode aufgerufen wurde.

    Das Instanziieren einer Klasse sollte außer dem Speicherverbrauch für das neue Objekt und den notwendigen Operationen, um die Konstruktor-Parameter zu sichern, keinerlei Ressourcen benötigen. Das Instanziieren ist Teil des Wiederherstellen des Bauplans der Objektabhängigkeiten, damit danach dann die Applikation aktiv werden und die jetzt arbeitsfähigen Methoden benutzen kann.

    - Sven Rautenberg

    1. Hi,

      Ich halte es für sehr kritisch, im Konstruktor intensivere Arbeiten zu erledigen. [...] Das Instanziieren einer Klasse sollte außer dem Speicherverbrauch für das neue Objekt und den notwendigen Operationen, um die Konstruktor-Parameter zu sichern, keinerlei Ressourcen benötigen. Das Instanziieren ist Teil des Wiederherstellen des Bauplans der Objektabhängigkeiten, damit danach dann die Applikation aktiv werden und die jetzt arbeitsfähigen Methoden benutzen kann.

      bezieht sich diese Aussage nur auf PHP oder auf alle Sprachen? Inwiefern verhält sich diese Aussage dann zum RAII-Idiom?

      Bis die Tage,
      Matti

      1. Moin!

        Ich halte es für sehr kritisch, im Konstruktor intensivere Arbeiten zu erledigen. [...] Das Instanziieren einer Klasse sollte außer dem Speicherverbrauch für das neue Objekt und den notwendigen Operationen, um die Konstruktor-Parameter zu sichern, keinerlei Ressourcen benötigen. Das Instanziieren ist Teil des Wiederherstellen des Bauplans der Objektabhängigkeiten, damit danach dann die Applikation aktiv werden und die jetzt arbeitsfähigen Methoden benutzen kann.

        bezieht sich diese Aussage nur auf PHP oder auf alle Sprachen? Inwiefern verhält sich diese Aussage dann zum RAII-Idiom?

        Nur auf PHP. PHP hat Mechanismen, RAII bzw. genauer die Freigabe von Ressourcen ohne Programmierereingriff sicherzustellen. Die die Situation sieht für PHP im Web-Kontext sowieso anders aus, als für andere Sprachen, weil jeder Request bei Null startet. Man hat also einen Vorteil, Dinge nur dann zu tun, wenn der aktuelle Request sie wirklich erfordert.

        Abgesehen davon wäre das Beispiel der Datenbank-Verbindung vermutlich auch in langlaufenden kompilierten Programmiersprachen jeweils "anders" gelöst worden als simpel in einem Konstruktor. Begriffe wie Connection-Pool kommen mir da in den Sinn, ohne dass ich da in die Tiefe gehen würde.

        - Sven Rautenberg