Objekte in Methoden eigener Klassen erstellen?

Guten Morgen,
ich frische grade meine C++ Kenntnisse auf und bin auf etwas gestoßen, was ich mir nicht so recht erklären kann. Ich habe eine eigene Klasse erstellt und dann diverse Methoden dazu. Bei der Erstellung eines Objekts ist mir dann aufgefallen, dass dieses Objekt nicht in anderen Methoden verfügbar ist. Demnach stellt sich die Frage, wie ich innerhalb einer Klasse dafür sorgen kann, dass die Objekte, Variablen usw. auch für andere Methoden der selben Klasse verfügbar sind.

Zur Veranschaulichung sagt etwas Code mehr als viele Worte :grinning:

test.h:

#include <Arduino.h>
#include <OneWire.h>
#include <DallasTemperature.h>

class Test {
	public:
		// Konstruktor mit Defaultargumenten
		Test (bool useOneWire = true);
		
		// Methoden werden Deklariert
   	        void setupFeatureSerial();
		void setupFeatureSerial(int BAUD_RATE);
    
               void setupFeatureOneWire();
               void setupFeatureOneWire(uint8_t OneWire_PIN, uint8_t OneWire_Resolution);
               uint8_t OneWire_getNumberOfDevices();
    
    
		
	private:
              int     BAUD_RATE;
              uint8_t OneWire_PIN;
              uint8_t OneWire_Resolution;
              bool    useOneWire; 
};

#endif

test.cpp:

#include "test.h"
#include <OneWire.h>
#include <DallasTemperature.h>

// Konstruktor für die Klasse Test (Achtung: Kein Rückgabewert, auch nicht void!!)
Test::Test(bool useOneWire)
{
  this->useOneWire  = useOneWire;
}

// Methoden (Funktionen)
void Test::setupFeatureSerial()
{
  BAUD_RATE = 115200;
  Serial.begin(BAUD_RATE);
  int br = Serial.baudRate();
  Serial.printf("INFO:  Serielle Übertragungsrate ist %d bps", br);
}

void Test::setupFeatureSerial(int BAUD_RATE)
{
  this->BAUD_RATE = BAUD_RATE;
  Serial.begin(BAUD_RATE);
  int br = Serial.baudRate();
  Serial.printf("INFO:  Serielle Übertragungsrate ist %d bps", br); 
}

void Test::setupFeatureOneWire()
{
  OneWire_PIN = 16;
  OneWire_Resolution = 10;
  OneWire oneWire(OneWire_PIN);
  DallasTemperature DS18B20(&oneWire);
  DS18B20.begin();
  DS18B20.setResolution(OneWire_Resolution);
}

void Test::setupFeatureOneWire(uint8_t OneWire_PIN, uint8_t OneWire_Resolution)
{
  this->OneWire_PIN = OneWire_PIN;
  this->OneWire_Resolution = OneWire_Resolution;
  OneWire oneWire(OneWire_PIN);
  DallasTemperature DS18B20(&oneWire);
  DS18B20.begin();
  DS18B20.setResolution(OneWire_Resolution);  
}

uint8_t Test::OneWire_getNumberOfDevices()
{
    uint8_t numberOfDevices = DS18B20.getDeviceCount();
    return numberOfDevices;
}

Offensichtlich kann ich wenn ich die test.h in meine test.ino einbinde in "void setup()" problemlos das Serielle Interface aktivieren.
Auch der Aufruf der setupFeatureOneWire geht, wenn ich aber die Methode OneWire_getNumberOfDevices in die .h/.cpp Datei reinschreibe wirft die IDE einen Fehler, weil "DS18B20" in der Methode nicht deklariert wurde.
Das ist auch einleuchtend was mich zunächst auf die Frage der "Lebensdauer" von Objekten und Variablen gebracht hat. Meine Idee ist es jetzt, das erstellte Objekt an einen in der test.h erstellten Prototyp zu übergeben, wobei ich unsicher bin ob es hilfreich wäre mit Zeigern zu Arbeiten. Möglicherweise möchte ich ja mal einen zweiten OneWire-Bus an einem anderen Pin ansprechen oder eine ScanRoutine implementieren um alle möglichen Pins nach einem vorhandenen Bus zu scannen... da muss ich demnach recht flexibel bleiben :smiley:

Also wenn mir da jemand nen Denkschubser geben kann bin ich maximal dankbar!
LG Thomas

void Test::setupFeatureOneWire(uint8_t OneWire_PIN, uint8_t OneWire_Resolution)
{
  this->OneWire_PIN = OneWire_PIN;
  this->OneWire_Resolution = OneWire_Resolution;
  OneWire oneWire(OneWire_PIN);
  DallasTemperature DS18B20(&oneWire);
  DS18B20.begin();
  DS18B20.setResolution(OneWire_Resolution);  
}

Hier erzeugst du lokale Instanzen.
Diese sind nur innerhalb der Funktion/Methode gültig.
Verfallen beim verlassen.
"Geltungsbereich" nennt sich das.


Alles in allem scheint mir, als wolltest du ein "big hairy object" bauen.
Ich rate ab.

Du erstellst in deiner Klasse eine Variable vom Typ DS18B20 und sprichst diese in den Membermethoden mit dem vergebenen Variablennamen an.

combie:
Hier erzeugst du lokale Instanzen.
Diese sind nur innerhalb der Funktion/Methode gültig.
Verfallen beim verlassen.
"Geltungsbereich" nennt sich das.

--> Super Hinweis, ich werde mich direkt damit befassen

combie:
Alles in allem scheint mir, als wolltest du ein "big hairy object" bauen.
Ich rate ab.

:smiley: Um Himmels Willen (Achtung schlechtes Wortspiel :smiley: ) das habe ich nicht vor, aber ich wollte einfach mal einiges ausprobieren und dabei verwende ich einfach das was ich sowieso schon habe und nutze. Mir ist schon klar, dass das keine gute Idee für ein echtes Programm ist.

Reiter:
Du erstellst in deiner Klasse eine Variable vom Typ DS18B20 und sprichst diese in den Membermethoden mit dem vergebenen Variablennamen an.

Dazu müsste ich dann aber erstmal wissen wie man das Objekt DS18B20 in eine Variable packe.. bisher habe ich noch nie mit Objekten auf diese Art arbeiten müssen und daher fehlt mir hier noch die Idee wie so etwas technisch umgesetzt wird.

Dazu müsste ich dann aber erstmal wissen wie man das Objekt DS18B20 in eine Variable packe.. bisher habe ich noch nie mit Objekten auf diese Art arbeiten müssen und daher fehlt mir hier noch die Idee wie so etwas technisch umgesetzt wird.

Dann erstmal die Theorie!

Denn Thermometer ist eine "Abhängigkeit"
Es hat sich als vorteilhaft erwiesen diesen "Abhängigkeiten" besondere Aufmerksamkeit zu widmen.

Lesenswert: "OOP Dependency Injection Pattern"

Im Falle C++...
constructor injection (bei der Instantiierung übergeben)
parameter injection (als Funktions/Methodenparameter übergeben)
setter injection (spezielle Settermethode anbieten)
template injection (Funktion/Struktur/Klasse mit Templateparameter spezialisieren)

Das sind die 4 Techniken, welche eigentlich allesamt günstiger sind, als eine fest eingebaute Abhängigkeit zu haben.

Okay... ich habe mal das ganze vereinfacht und habe jetzt folgendes vor:

#include <OneWire.h>
#include <DallasTemperature.h>

setup(){
   initOneWire(); // Soll ein Objekt zurückgeben, oder einen Global verfügbaren Prototypen mit 
                        //  initialisieren
   DallasTemperature sensoren(&???); // Soll das Objekt aus OneWire weiterverwenden.
   
}


void initOneWire(){

  pin = 5;
  OneWire onewireobjekt(pin);
}

Warum ich das so mache:
pin hat hier den Wert 5, soll aber später vielleicht einmal aus einer Konfiguration gelesen werden. Auch möchte ich mit der Funktion initOneWire irgendwann einmal verschiedene Pins durchprüfen ob hier onewire-Geräte gefunden werden können.

Die Gültigkeit endet nach Ende der initOneWire()-Funktion. Das ist mein Problem.
Ich habe auch schon vieles durchprobiert, zum Beispiel Dinge wie:

OneWire oneWire = OneWire(5);

Das funktioniert, aber auch hier das gleiche Problem, dass ich das "Ergebnis" nicht aus der Funktion herausbekomme...

...ich möchte das wirklich lernen, daher bitte nicht meckern, weil ich auf dem OneWire so rumreite :smiley: Aber da habe ich erstmal das Hauptproblem.

Es gibt ja zum Beispiel nicht nur die Temperatursensoren, sondern auch andere OneWire-Geräte, die aber am gleichen Pin hängen, also könnte ich die Referenz auf das OneWire-Objekt "Welchen Bus sollst du verwenden" auch an andere Funktionen/Objekte weitergeben.

Warum ich das so mache:
pin hat hier den Wert 5, soll aber später vielleicht einmal aus einer Konfiguration gelesen werden. Auch möchte ich mit der Funktion initOneWire irgendwann einmal verschiedene Pins durchprüfen ob hier onewire-Geräte gefunden werden können.

Wenn du zur Laufzeit Objekte instanziieren möchtest, dann musst du das auch tun.
:o Eine recht triviale Ansage, oder? :o

Zu diesem Zwecke, kennt C++ das Schlüsselwort: "new"+"delete"

Nachtrag:
Hier mal eine (vermutlich) unsinnige Lösung, für ein (vermutlich) unnötiges Problem.
(verschiedenste Varianten, man sich ausdenken könnte)

#include <Streaming.h>
#include <OneWire.h>
#include <DallasTemperature.h>


// ----------------------------

class Sensor
{
  private:
  OneWire *onewire;
  DallasTemperature *dallas;

  void cleanup()
  {
    delete dallas;
    dallas  = nullptr;
    
    delete onewire;  
    onewire = nullptr;
  }

  public:
  Sensor():onewire(nullptr),dallas(nullptr){}

  ~Sensor()
  {
    cleanup();
  }
  
  DallasTemperature *getDallas()
  {
    if(!onewire) exit(1);
    if(!dallas)  exit(2);
    return dallas;
  }

  DallasTemperature *setPin(const byte pin)
  {
    cleanup();
    onewire = new OneWire(pin);
    dallas  = new DallasTemperature(onewire);
    return getDallas();
  }
  
  void release()
  {
    cleanup();
  }
};


// -----------------------


Sensor sensor;

const byte pinliste[] {5,6,7};


// --------------


void setup() 
{
 Serial.begin(9600);
 Serial << "Start" << endl;

 for(byte pin:pinliste)
 {
   sensor.setPin(pin)->begin();
   Serial << "es wurden "<< sensor.getDallas()->getDeviceCount() <<" Sensoren an Pin " << pin << " gefunden" << endl;
 }
 sensor.release();
 
}

void loop() 
{

}

Wow! Das ist ja mal eine Ansammlung! Also Danke dafür, jetzt muss ich es nur noch auseinandernehmen um es auch zu kapieren. Für die oneworld Geschichte ist das alles sicher nicht so relevant, weil man das ja einmal aktiviert und gut. Aber mir geht es ja tatsächlich darum das ganze zu verstehen und da brauche ich nun mal einen Anwenduungsfall der halbwegs realistisch ist.

Vielen Dank !

LG Thomas

Wow!

Schön, dass es dir gefällt!

Von dynamischer Speicherverwaltung möchte ich dir, auf kleinen µC, abraten.
Ist im Einzelfall ok, aber grundsätzlich, eher böse.

Ansonsten:
Ein fröhliches kauen, ich dir wünsche.

Guten Morgen,
jetzt bin ich eine Weile im Urlaub gewesen und wie gut es mal tut, ein Problem an dem man sich festgefressen hat einfach mal liegen zu lassen. Ich würde hier gerne noch ein paar Fragen zu der Klasse stellen, bzw. das wiedergeben, was ich verstanden habe um sicher zu gehen, dass ich das auch verstehe...

Ich lese das so:
Mit

OneWire *onewire;
DallasTemperature *dallas;

werden Zeiger auf die Werte (nicht die Speicheradressen) vom Datentyp OneWire bzw. DallasTemperature angelegt.

In der Funktion cleanup() wird die Speicheradresse wieder freigegeben, also der Zeiger dallas und onewire wird entfernt. Wie kann es dann sein, dass der folgende Aufruf bei dem die Werte an dieser Adresse mit nullptr gelöscht werden fehlerlos bleibt? Andersherum würde es mir viel logischer erscheinen.

Öffentlich zugegriffen werden kann auf die Funktion release(), die wiederum nur die interne methode cleanup() aufruft. Dadurch wird dann auch klar, dass ich zunächst ein Objekt erstelle, das wiederum später intern zerstört wird. Ein Aufruf ohne vorher ein Objekt erstellt zu haben würde einen Fehler produzieren, da ja kein Speicher freizugeben ist.

Die Funktionen getDallas() und setPin() sind auch öffentlich zugänglich, wobei diese aber beim Aufruf durch den Operator "*" jeweils einen Zeiger auf ein Objekt vom Typ DallasTemperature zurückliefern, womit dann auch sichergestellt ist, dass diese Objekte einen Geltungsbereich auch außerhalb der Klasse besitzen, da diese ja erst durch die Cleanup() Funktion endgültig vernichtet werden.

Ist das so richtig?

(Danke schon mal :D)

Ach ja... ich komme noch nicht so ganz zurecht mit den Zeilen public-Teil:

Sensor():onewire(nullptr),dallas(nullptr){};

~Sensor() {
      cleanup();
    }

~Sensor() ist der Destructor, soweit verstehe ich das ja, aber der Konstruktor ist für mich nicht ganz klar, durch die geschweiften Klammern am Ende würde ich ja eine Inlineartige Funktion generieren. Ich vermute mal ohne es wirklich zu wissen: Wenn ich Sensor() aufrufe, dann erzeuge ich direkt auch interne: weil diese im private-Teil deklariert sind und durch die Übergabe von nullptr "leere" Objekte für onewire und dallas. Ist das so richtig? Bzw. ist das die "ganze Wahrheit"?

Bzw. ist das die "ganze Wahrheit"?

Der Konstruktor hat eine Initialisierungsliste.
Findest du auch so im modernen C++ Handbuch.

Dort werden die beiden privaten Elemente mit nullptr initialisiert.

Alternativ:

Sensor()
{
  onewire = nullptr;
  dallas  = nullptr;
}

Ich bevorzuge die Initialisierungsliste, wo immer, mir das möglich ist.
Denn sie kann auch Dinge, welche als const(read only) deklariert sind, und Referenzen, initialisieren. Ebenso bevorzuge ich Referenzen (vor Zeigerwirtschaft), aber in diesem speziellen Fall ist das wohl alternativ los.

In der Funktion cleanup() wird die Speicheradresse wieder freigegeben, also der Zeiger dallas und onewire wird entfernt. Wie kann es dann sein, dass der folgende Aufruf bei dem die Werte an dieser Adresse mit nullptr gelöscht werden fehlerlos bleibt? Andersherum würde es mir viel logischer erscheinen.

Hier ist der Text nicht in Ordnung!
(oder die Idee dahinter)

Der Speicherbereich wird frei gegeben (delete), nicht die Adresse und damit auch nicht der Zeiger an sich.
Der Zeiger, und damit die Adresse, wird auf nullptr gesetzt, und somit kenntlich gemacht, dass diese Zeiger ab jetzt auf Nix vernünftiges zeigen.
Klarer?

werden Zeiger auf die Werte (nicht die Speicheradressen) vom Datentyp OneWire bzw. DallasTemperature angelegt.

Es werden Zeiger definiert, welche die Adresse einer Instanz der betreffenden Klasse halten können.
Speicherplatz. für die Instanzen. wird an der Stelle noch nicht reserviert. Das erfolgt erst später per new.

Ich hoffe, dass ich einige grundlegende Klarheiten beseitigen konnte.

Was man bei Initialisierungslisten verstehen muss ist das Variablen und Konstanten direkt beim Erstellen des Objekts initialisiert werden. Wenn man im Konstruktor ist, ist es schon zu spät. Da unterscheidet sich C++ deutlich von anderen Sprachen.
Man kann Dinge im Konstruktor-Körper initialisieren, aber die wurden schon vorher Default-initialisiert. Bestenfalls macht man es also doppelt. Und Konstanten oder Referenzen müssen eben sofort initialisiert werden und können danach nicht mehr andere Werte zugewiesen bekommen. Da muss man dann die Liste verwenden

Serenifly:
Was man bei Initialisierungslisten verstehen muss ist das Variablen und Konstanten direkt beim Erstellen des Objekts initialisiert werden. Wenn man im Konstruktor ist, ist es schon zu spät. Da unterscheidet sich C++ deutlich von anderen Sprachen.
Man kann Dinge im Konstruktor-Körper initialisieren, aber die wurden schon vorher Default-initialisiert. Bestenfalls macht man es also doppelt. Und Konstanten oder Referenzen müssen eben sofort initialisiert werden und können danach nicht mehr andere Werte zugewiesen bekommen. Da muss man dann die Liste verwenden

...verstanden! Alles was man explizit vorgibt schützt einen später vor Überraschungen :smiley:

combie:
Hier ist der Text nicht in Ordnung!
(oder die Idee dahinter)

Der Speicherbereich wird frei gegeben (delete), nicht die Adresse und damit auch nicht der Zeiger an sich.
Der Zeiger, und damit die Adresse, wird auf nullptr gesetzt, und somit kenntlich gemacht, dass diese Zeiger ab jetzt auf Nix vernünftiges zeigen.
Klarer?

Ja, so verstehe ich es und bin damit einer unglücklichen Formulierung (die ich vorher beim Recherchieren gefunden hatte) aufgesessen. "Klarheit" beseitigt :wink: Danke.

combie:
Es werden Zeiger definiert, welche die Adresse einer Instanz der betreffenden Klasse halten können.
Speicherplatz. für die Instanzen. wird an der Stelle noch nicht reserviert. Das erfolgt erst später per new.

Und auch hier habe ich es jetzt wohl verstanden, das ist auch durchaus praktisch, weil ich so ja viel flexibler bin und wenn etwas im Programmverlauf nicht gebraucht wird, dann muss ich dafür auch nicht (möglicherweise große) Speichermengen nutzlos verbraten, die an anderer Stelle gebraucht werden.

Eine Frage zu:

 DallasTemperature *getDallas() {
    if(!onewire) exit(1);
    if(!dallas) exit(2);
    return dallas;
  }

 DallasTemperature *setPin(const byte pin) {
    cleanup();
    onewire = new OneWire(pin);               
    dallas  = new DallasTemperature(onewire); 
    return getDallas();                        
  }

Beide Funktionen sind public. Wenn ich also die Methode setPin aufrufe, dann wird das Objekt onewire erstellt und danach daraus das Objekt dallas. Zuletzt wird die Funktion getDallas() aufgerufen und das Objekt dallas zurückgegeben, sofern die Überprüfung ob onewire oder dallas existieren (oder Werte beinhalten?) nicht vorher mit einem exit die Funktion verlassen haben. Das erscheint mir an dieser Stelle noch nicht so fürchterlich sinnvoll, da ich aber mit "dallas" ja ein Objekt habe das individuell gefertigt wurde kann ich damit gut weiterarbeiten und in Zukunft dieses Objekt über die Funktion getDallas() direkt ansprechen. Also könnte man sagen die Funktion getDallas() ist eigentlich eher eine Funktion useDallas() :smiley: Richtig?

Üblicherweise werden Methoden, die etwas (ein existierendes Objekt) zurück liefern, getXyz() genannt. Methoden die ein Objekt erzeugen, eher createXyz().
Sieh es aus der Sicht des Aufrufers: Bei "machWasMitDallas()" ist unklar was überhaupt gemacht werden soll.

Dass eine public Methode Tests gegen falsche Verwendung enthält, ist übrigens sehr löblich.

Also könnte man sagen die Funktion getDallas() ist eigentlich eher eine Funktion useDallas() :smiley: Richtig?

Kann man...
Dann versteht dich nur keiner...

Man hat sich weltweit, und Sprachenübergreifend auf Getter und Setter geeinigt.
Hier eine Erklärung für PHP
Wikipedia sagt

ist übrigens sehr löblich.

Ich gehe einen Schritt weiter uns sage:
Wenn ein Getter einen ungültigen Pointer liefert, dann sollte der Programmierer einen 5 wöchigen Urlaub auf der Streckbank seiner Wahl zugesprochen bekommen.

Es muss in einer solchen Situation eine Exception geworfen werden!
Das geht auf einem AVR nicht, also hier der harte Programmabbruch mit exit().

Dieses Verfahren, Daten in Objekte/Instanzen einzuschleusen, nennt sich "Dependency injektion".
Hier: Setter Injection. Also einschleusen über einen Setter.
Das Objekt sensor verhält sich ein wenig nach dem Konzept "Fabrik" und "Registry".
Ist lose an diese Konzepte angelehnt.

Hier findest du die wichtigsten oop design Pattern

Vielen Dank für diese vielen nützlichen Informationen! Ich glaube ich habe jetzt relativ gut verstanden wie das Konzept funktioniert.

ABER: Es fehlt mir wohl doch noch an Grundverständnis in einigen Punkten, denn ich bekomme die Übertragung auf andere Problemstellungen nicht hin....

Ich habe jetzt einen Versuch gestartet und habe in meinem Programm folgendes stehen:

// Hauptprogramm bzw. haupt.ino 
// [...] Code den ich auslasse
OneWire                *onewire;
//[...] Noch mehr Code

Ich habe also einen Zeiger auf eine Variable vom Typ OneWire erstellt. Dieser Zeiger hat einen Namen, aber ich habe noch keine Variable festgelegt auf die dieser Zeiger zeigt. Und einen Wert oder Inhalt gibt es auch erst einmal nicht. (

combie:
Es werden Zeiger definiert, welche die Adresse einer Instanz der betreffenden Klasse halten können. Speicherplatz. für die Instanzen. wird an der Stelle noch nicht reserviert. Das erfolgt erst später per new.

Später habe ich dann eine eigene Klasse in einer Headerdatei erstellt und in dieser selbst gibt es jetzt wieder einen (Klasseninternen/private) Zeiger *onewire (Muss man gut aufpassen weil so nicht ganz klar ist von welchem Zeiger wir gerade sprechen).

Weiter habe ich eine Methode setOneWire(), die ich jetzt auch mal zusammenschrumpfe:

Zeile 1:   OneWire *setPin(const byte pin){
Zeile 2:   onewire = new OneWire(pin);
Zeile 3:   return onewire;

Zeile 1: Rückgabetyp ist OneWire, Aufrufparameter ist ein byte, das als const deklariert wird und somit innerhalb der Funktion nicht verändert wird.

Zeile 2: Der interne Zeiger "onewire" erhält jetzt Speicherplatz zugewiesen, da ein neues Objekt vom Typ OneWire mit dem Parameter pin erstellt wird.

Zeile 3: Das Objekt onewire wird zurückgegeben.

Meine Fragen sind jetzt:

Zeile 1: Der * vor der Funktion bedeutet für mich, dass es sich um eine Zeigerfunktion handelt und eigentlich nur um einen Prototypen. Eigentlich müsste ich jetzt zu der .h Datei eine .cpp Datei erstellen in der ich die Funktion definiere, in diesem Fall ist aber die Definition direkt dabei (in den {} Klammern). Mir ist aber völlig unverständlich, warum das überhaupt notwendig ist. Wenn ich diese Funktion "normal" verwenden möchte bekomme ich bei der Programmüberprüfung einen Fehler:

exit status 1
invalid conversion from 'OneWire*' to 'uint8_t {aka unsigned char}' [-fpermissive]

Zeile 2: Wird hier tatsächlich ein Objekt bzw. Variable erstellt, oder nur Speicher zugewiesen?
Zeile 3: Was genau wird denn hier zurückgegeben?

// Prototype / Deklaration
OneWire *setPin(const byte pin);


// Funktion / Definition
OneWire *setPin(const byte pin)
{
// hier tuwas
}

Wenn ich diese Funktion "normal" verwenden möchte bekomme ich bei der Programmüberprüfung einen Fehler:

Ich habe keine Ahnung, was für dich normal ist.
Aber den Fehler solltest du beheben.

Zeile 1: Rückgabetyp ist OneWire,

Der Rückgabewert, ist ein Zeiger auf eine OneWire Instanz

Zeile 2: Wird hier tatsächlich ein Objekt bzw. Variable erstellt, oder nur Speicher zugewiesen?

Es wird Speicher reserviert, damit der Zeiger einen Ort hat, worauf er zeigen kann, und damit ein Platz existiert, wo die Instanz ihre Daten ablegen kann.
Es wird der Konstruktor der Instanz aufgerufen.
Das Objekt wird also vollständig konstruiert und ist danach verwendbar.

Zeile 3: Was genau wird denn hier zurückgegeben?

Ein Zeiger!

Bzw. die typisierte Adressangabe der gerade erzeugten Instanz.
Der Zeiger/Pointer ist ein konkreter Hinweis auf den Ort, wo die Instanz im Speicher zu finden ist.