PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Unterschied Referenz/Pointer in C++?


Aqualon
2005-11-01, 23:58:49
Hi, folgender Code:

helloworld* myworld1 = new helloworld();
helloworld &myworld2 = (* new helloworld());
In beiden Fällen wird ein Objekt der Klasse helloworld erzeugt. Einmal hat man einen Pointer darauf und einmal eine Referenz.

Wo liegen da jetzt genau die Unterschiede und wann verwendet man was?

Aqua

Coda
2005-11-02, 00:54:03
Eine Referenz ist eine Pointer mit "eingebauter Derefferenzierung". Man könnte im Prinzip überall Pointer einsetzen, aber bei einer Funktion ist es einfach schöner Argumente per Referenz zu übergeben um nicht dauernd * und -> verwenden zu müssen.

In deinem Beispiel würde man aber auf jedenfall einen Pointer verwenden, weil das Objekt später per delete zu löschen ist. Außerdem kannst du einer Referenz nach initialisierung nicht mehr ein anderes Objekt zuweisen (ok, es geht, aber das ist undefined behaviour).

RoKo
2005-11-02, 01:02:34
Eine Referenz ist nur ein anderer Name für ein bereits bestehendes Objekt - ein Konstrukt also, von dem im Kompilat nix mehr übrig bleibt, im Gegensatz zum Pointer. Referenzen wurden zur Unterstützung des Operator-Overloading eingefügt ([] insbesondere, denke ich), können aber auch genutzt werden, um Funktionsparameter auf das Ursprungsobjekt verweisen zu lassen.
void Swap(int& a, int& b)
{
int temp = b;
b = a;
a = b;
}
In dem Fall ist es eine bessere Pointeralternative, denn wenn diese Funktion gelinlined wird, kann der Compiler jegliche Zeigerarithmetik vermeiden. Wird sie nicht geinlined, werden aus den Referenzen wahrscheinlich ganz normale Pointer machen. Auch praktisch ist, dass kein NULL übergeben werden kann.

"helloworld &myworld2 = (* new helloworld());" ist übrigens ein Speicherleck, ähnlich "helloworld myworld2 = (* new helloworld());", nur wird der Copy-Konstruktor nicht aufgerufen.
Pointer braucht man, wenn man dynamisch mit dem Speicher umgehen will, wenn man während der Kompilierung noch nicht weiß, wo der Pointer hinzeigen soll und/oder er während dem Programmablauf mal hierhin mal dorthin zeigen kann.

Coda
2005-11-02, 01:06:39
Eine Referenz ist nur ein anderer Name für ein bereits bestehendes Objekt - ein Konstrukt also, von dem im Kompilat nix mehr übrig bleibt, im Gegensatz zum Pointer.Das ist schlicht und einfach falsch. Eine Referenz ist genauso wie ein Pointer eine Speicheradresse.

RoKo
2005-11-02, 01:12:54
Das ist schlicht und einfach falsch. Eine Referenz ist genauso wie ein Pointer eine Speicheradresse.
"It is unspecified whether or not a reference requires storage" ISO/IEC 14882, Seite 132 ganz unten.

Coda
2005-11-02, 01:32:15
Ach tatsächlich? Gut in dem Fall nehm ich das zurück :redface:

(abgesehen davon wird es in jeder realen Implementierung so sein wie ich's gesagt habe ;))

RoKo
2005-11-02, 01:33:59
(abgesehen davon wird es in jeder realen Implementierung so sein wie ich's gesagt habe ;))
(keine Ahnung, aber fände ich sehr schwach ;))

Demirug
2005-11-02, 07:14:45
Da steht aber nicht das eine Referenz keinen Speicher braucht. Da steht da es nicht spezifiziert ist ob sie nun Speicher braucht oder nicht. In der Regel braucht eine Referenz genau so viel Speicher wie ein entsprechender Zeiger. Es gibt nur ganz wenige Fälle in denen der Compiler diesen Speicher wegoptimieren kann.

RoKo, für den Compiler macht es bei der Codeerzeugung überhaupt keinen Unterschied ob ich nun Zeiger oder Referenzen benutzte:

void Swap(int& a, int& b)
{
int temp = b;
b = a;
a = temp;
}

void Swap(int* a, int* b)
{
int temp = *b;
*b = *a;
*a = temp;
}

In beiden Fällen wird der exact gleiche Assemblercode erzeugt.

Aqualon, wenn es geht grundsätzlich immer Referenzen benutzten. Referenzen haben den Vorteil das sie immer auf ein gültiges Objekt zeigen. (Man kann das zwar aushebeln aber das macht man normalerweise nicht). Ein Zeiger kann dagegen auf die berühmte Null zeigen. Aus diesem Grund muss man bei Code mit Zeigern eigentlich immer prüfen ob man überhaupt eine gültige Speicheradresse bekommen hat. Das Mindeste ist eine NULL Zeiger Prüfung. Wenn man ganz sicher gehen will/muss weil man sich zum Beispiel an einer Schnittstelle befindet die Daten von fremden Code bekommt müsste man eigentlich sogar noch genauer prüfen.

Alles in allem sind Zeiger eine der besten Möglichkeiten sich bei C++ ein Loch in den Fuss zu schiesen. Das ganze bitte so auch nicht auf andere Programmiersprachen übertragen. Bei C# zum Beispiel ist das mit den Referenzen wieder anders.

Aqualon
2005-11-02, 09:26:40
"helloworld &myworld2 = (* new helloworld());" ist übrigens ein Speicherleck[...]Ok, wie instanziiere ich dann ein Objekt mit new, dass ich danach eine Referenz darauf habe?

Delete würde doch in dem Fall trotzdem mit delete &myworld2 gehen, oder lieg ich da falsch?

Aqua

Demirug
2005-11-02, 09:45:31
Ok, wie instanziiere ich dann ein Objekt mit new, dass ich danach eine Referenz darauf habe?

Gar nicht. Wenn du ein Objekt mit new Instanzierst wird das immer in einer Zeigervariable gespeichert. Ein new kann nämlich auch fehlschlagen und dann bekommst du unter Umständen eine NULL zurück. Wenn du die nun in eine Referenz wandelts hast du schon verloren den beim ersten Zugriff auf diese "Instanz" verabschiedet sich dein Programm oder es wird im besten Fall von einem Catch gefangen.

Referenzen werden zum Beispiel dann benutzt wenn du ein Objekt auf dem Stack erzeugt hast und es einer Funktion/Methode übergeben willst ohne das eine Kopie davon erzeugt wird.

Es gibt auch noch die Variante in der sich ein Objekt selbst einer Funktion als Referenz übergibt. Das macht man mit "*this". Das ist aber auch nicht ganz ungefährlich den theoretisch könnte auch this ein ungültiger Zeiger sein. Wenn das allerdings dann praktisch eintritt (Ja, ich habe das schon selbst gesehen) hat man schon vorher bezüglich des passiven programmierens versagt.

Delete würde doch in dem Fall trotzdem mit delete &myworld2 gehen, oder lieg ich da falsch?

Aqua

Aleine schon daran zu denken ist böse.

ScottManDeath
2005-11-02, 10:31:12
Das macht man mit "*this". Das ist aber auch nicht ganz ungefährlich den theoretisch könnte auch this ein ungültiger Zeiger sein. Wenn das allerdings dann praktisch eintritt (Ja, ich habe das schon selbst gesehen) hat man schon vorher bezüglich des passiven programmierens versagt.


Huh? Wie das? Exception im Destructor geworfen oder im Konstruktor eine virtuelle Methode aufgerufen und dann über den abgeleiteten Typ auf this zugegriffen?

RoKo
2005-11-02, 10:32:31
Es gibt nur ganz wenige Fälle in denen der Compiler diesen Speicher wegoptimieren kann.
Wie gesagt, wenn geinlined wird. Oder wenn man direkt in einem Codeblock Referenzen verwendet (auch wenn das wenig sinnvoll ist). Oder übersehe ich etwas? Gibt es einen Grund, weshalb Referenz-Parameter geinlineter Funktionen nicht generell wegoptimiert werden können außer faulen Compiler-Programmierern?

Demirug
2005-11-02, 10:57:02
Huh? Wie das? Exception im Destructor geworfen oder im Konstruktor eine virtuelle Methode aufgerufen und dann über den abgeleiteten Typ auf this zugegriffen?

Das geht ganz einfach.

- Methode A liefert einen Zeiger auf ein Objekt zurück.
- Für dieses Objekt wird dann Methode B aufgerufen.
- Methode B ruft eine Methode C auf und übergibt "*this".

Liefert Methode A nun einen NULL Zeiger der vor dem Aufruf von Methode B nicht geprüft wird bekommt Methode C eine "NULL-Referenz" übergeben die es eigentlich ja nicht geben dürfte. Methode C verursacht dann sobald es versucht die Referenz zu nutzen einen Speicherverletzung. In der Regel meist aber nicht an der Adresse NULL sondern meist NULL+x.

Das ist fast so schön wie Referenzcounting Fehler bei COM. Ich weiß schon warum ich es zunehmend vermeide C++ zu nehmen.

ScottManDeath
2005-11-02, 11:32:09
Das geht ganz einfach.

- Methode A liefert einen Zeiger auf ein Objekt zurück.
- Für dieses Objekt wird dann Methode B aufgerufen.
- Methode B ruft eine Methode C auf und übergibt "*this".

Liefert Methode A nun einen NULL Zeiger der vor dem Aufruf von Methode B nicht geprüft wird bekommt Methode C eine "NULL-Referenz" übergeben die es eigentlich ja nicht geben dürfte. Methode C verursacht dann sobald es versucht die Referenz zu nutzen einen Speicherverletzung. In der Regel meist aber nicht an der Adresse NULL sondern meist NULL+x.

Das ist fast so schön wie Referenzcounting Fehler bei COM. Ich weiß schon warum ich es zunehmend vermeide C++ zu nehmen.

Evil :|

Aber sollte es nicht beim Aufruf von Methode B krachen?

Jo, C# ist da robuster :)

Senior Sanchez
2005-11-02, 11:32:46
Das geht ganz einfach.

- Methode A liefert einen Zeiger auf ein Objekt zurück.
- Für dieses Objekt wird dann Methode B aufgerufen.
- Methode B ruft eine Methode C auf und übergibt "*this".

Liefert Methode A nun einen NULL Zeiger der vor dem Aufruf von Methode B nicht geprüft wird bekommt Methode C eine "NULL-Referenz" übergeben die es eigentlich ja nicht geben dürfte. Methode C verursacht dann sobald es versucht die Referenz zu nutzen einen Speicherverletzung. In der Regel meist aber nicht an der Adresse NULL sondern meist NULL+x.

Das ist fast so schön wie Referenzcounting Fehler bei COM. Ich weiß schon warum ich es zunehmend vermeide C++ zu nehmen.

Also wirklich folgen konnte ich jetzt nicht. Könnte aber auch daran liegen, dass ich das Problem nicht sehen da ich zu sehr Java gewöhnt bin ;)

EDIT: Mir fällts schwer da jetzt hinterherzukommen, welche Methoden auf dem Objekt aufgerufen werden und welche dagegen das Objekt bloß als Paramter erhalten.

Aqualon
2005-11-02, 14:11:00
Dann mal zusammengefasst:

int main(void) {

helloworld *myworld;

try {
myworld = new helloworld("hallo");
}
catch(bad_alloc) {
cerr << "kein Speicher verfuegbar" << endl; exit(1);
}
catch(...) {
cerr << "unbekannter Fehler" << endl; exit(1);
}

myworld->print();

delete myworld;
}Kann man das so machen, oder ist da wieder ein grober Fehler drin?

Aqua

Edit: exit ergaenzt.

Coda
2005-11-02, 14:15:46
RoKo, für den Compiler macht es bei der Codeerzeugung überhaupt keinen Unterschied ob ich nun Zeiger oder Referenzen benutzte:Genau darauf wollte ich hinaus. Danke.

Wie gesagt, wenn geinlined wird. Oder wenn man direkt in einem Codeblock Referenzen verwendet (auch wenn das wenig sinnvoll ist). Oder übersehe ich etwas? Gibt es einen Grund, weshalb Referenz-Parameter geinlineter Funktionen nicht generell wegoptimiert werden können außer faulen Compiler-Programmierern?Bei Objekten mit virtuellen Funktionen z.B.
Du kannst dir sicher sein, dass es vom Kompilat her völlig egal ist ob Pointer oder Referenzen verwendet werden.

Ein new kann nämlich auch fehlschlagen und dann bekommst du unter Umständen eine NULL zurück.Bei einem modernen C++ Compiler sollte da eine Exception geworfen werden.

Kann man das so machen, oder ist da wieder ein grober Fehler drin?Das ist sogar vorbildlich ;)

Demirug
2005-11-02, 14:27:06
Bei einem modernen C++ Compiler sollte da eine Exception geworfen werden.

Das kann man aber bei diesen Compilern auch abstellen. Zudem bist du sowieso angeschmiert weil dein neues Objekt hast du immer noch nicht.

Das ist sogar vorbildlich ;)

Nein, ist es nicht. Da ist immer noch ein Fehler (im Fehlerfall) drin.

Coda
2005-11-02, 14:28:36
Das kann man aber bei diesen Compilern auch abstellen. Zudem bist du sowieso angeschmiert weil dein neues Objekt hast du immer noch nicht.Natürlich nicht. Aber die Exception bemerkst du sofort im Gegensatz zum Null-Pointer

Nein, ist es nicht. Da ist immer noch ein Fehler (im Fehlerfall) drin.Ach so, da fehlt das return, hast recht...

Demirug
2005-11-02, 14:39:16
Natürlich nicht. Aber die Exception bemerkst du sofort im Gegensatz zum Null-Pointer

Ja aber manchmal hat mal "Alten Code" der nicht für eine entsprechenden Exception ausgelegt ist.

Ach so, da fehlt das return, hast recht...

Ja so geht es auch. Ich hätte es so gemacht:


int main(void)
{
try
{
helloworld *myworld = new helloworld("hallo");
myworld->print();
delete myworld;
}
catch(bad_alloc)
{
cerr << "kein Speicher verfuegbar" << endl;
}
catch(...)
{
cerr << "unbekannter Fehler" << endl;
}
}

Demirug
2005-11-02, 14:40:22
Also wirklich folgen konnte ich jetzt nicht. Könnte aber auch daran liegen, dass ich das Problem nicht sehen da ich zu sehr Java gewöhnt bin ;)

EDIT: Mir fällts schwer da jetzt hinterherzukommen, welche Methoden auf dem Objekt aufgerufen werden und welche dagegen das Objekt bloß als Paramter erhalten.

Damit kann man das ganze reproduzieren:

class Foo1
{
public:
void B ();

int V;
};

class Foo2
{
public:
void C (Foo1& rFoo);
};

void Foo1::B ()
{
Foo2 foo;

foo.C (*this);
}

void Foo2::C (Foo1& rFoo)
{
rFoo.V = 0;
}

Foo1* A ()
{
return NULL;
}

int _tmain(int argc, _TCHAR* argv[])
{
A()->B();
}

Aqualon
2005-11-02, 14:41:28
DNein, ist es nicht. Da ist immer noch ein Fehler (im Fehlerfall) drin.Stimmt, da muss noch ein exit rein.

Wie seht ihr das eigentlich, ist using namespace std; ok oder nicht? Da hab ich auch schon unterschiedliche Meinungen gehoert.

Aqua

Abnaxos
2005-11-02, 14:51:26
Wenn du ein Objekt mit new Instanzierst wird das immer in einer Zeigervariable gespeichert. Ein new kann nämlich auch fehlschlagen und dann bekommst du unter Umständen eine NULL zurück.
Randbemerkung: new kann eigentlich nur dann NULL zurückgeben, wenn kein Speicher mehr vorhanden ist. Das ist eine derart katastrophale Situation, dass man das Programm IMHO ruhig abschmieren lassen kann. Wenn man erstmal so weit ist, wird es nämlich sowieso sehr schwierig, überhaupt noch sauber zu beenden (ich hatte mal einen Fall, bei dem ich das abfangen wollte, "Out of memory" ausgeben und tschüss -- das Programm ist beim puts() dann doch noch abgestürzt).

Dazu kommt, dass viele OS gar nicht NULL zurückgeben, wenn ein malloc() fehlschlägt. Linux z.B. sucht beim malloc() gar nicht nach freiem Speicher, es gibt einfach einen Pointer zurück. Erst wenn auf den Speicher das erste Mal zugegriffen wird, versucht der Kernel, den Speicher auch tatsächlich zu organisieren. Sollte das fehlschlagen, killt er das betroffene Programm (also einfach das nächste, für das kein Speicher mehr vorhanden ist) schlicht mit einem SIGSEGV.

Das Verhalten kann man zwar ändern (ist bei Workstations und Servern so OK, weil ein OOM sowieso nie auftreten wird, aber z.B. bei embedded Anwendungen wäre so ein Verhalten doch eher unpassend), aber viele OS verwenden diese sogenannte "optimistic memory allocation strategy".

Meine allgemeine Meinung: Out of memory ist der Super-GAU, der Weltuntergang, das Ende des Universums. In den meisten Fällen ist es nicht nötig und nur schwer möglich, das zu behandeln (sofern das OS einem überhaupt erlaubt, diese Situation zu erkennen), man kann diese Möglichkeit daher meist getrost ignorieren und das Programm in dem Fall segfaulten lassen.

Demirug
2005-11-02, 15:08:39
Randbemerkung: new kann eigentlich nur dann NULL zurückgeben, wenn kein Speicher mehr vorhanden ist. Das ist eine derart katastrophale Situation, dass man das Programm IMHO ruhig abschmieren lassen kann. Wenn man erstmal so weit ist, wird es nämlich sowieso sehr schwierig, überhaupt noch sauber zu beenden (ich hatte mal einen Fall, bei dem ich das abfangen wollte, "Out of memory" ausgeben und tschüss -- das Programm ist beim puts() dann doch noch abgestürzt).

Dazu kommt, dass viele OS gar nicht NULL zurückgeben, wenn ein malloc() fehlschlägt. Linux z.B. sucht beim malloc() gar nicht nach freiem Speicher, es gibt einfach einen Pointer zurück. Erst wenn auf den Speicher das erste Mal zugegriffen wird, versucht der Kernel, den Speicher auch tatsächlich zu organisieren. Sollte das fehlschlagen, killt er das betroffene Programm (also einfach das nächste, für das kein Speicher mehr vorhanden ist) schlicht mit einem SIGSEGV.

Das Verhalten kann man zwar ändern (ist bei Workstations und Servern so OK, weil ein OOM sowieso nie auftreten wird, aber z.B. bei embedded Anwendungen wäre so ein Verhalten doch eher unpassend), aber viele OS verwenden diese sogenannte "optimistic memory allocation strategy".

Meine allgemeine Meinung: Out of memory ist der Super-GAU, der Weltuntergang, das Ende des Universums. In den meisten Fällen ist es nicht nötig und nur schwer möglich, das zu behandeln (sofern das OS einem überhaupt erlaubt, diese Situation zu erkennen), man kann diese Möglichkeit daher meist getrost ignorieren und das Programm in dem Fall segfaulten lassen.

Bei einer 24/7 Serveranwendung passiert ein "Out of Memory" schneller als einen lieb ist.

Ich hatte da mal einen ganz komplexen Fall bei dem der Server mit schöner Regelmässigkeit mit einem OOM rausgefolgen ist. Dummerweise hat er dabei jedesmal auch den aktuellen Zustand mitgenommen.

Aus diesem Grund habe ich dann dort einen Globale OOM Handler eingebaut welcher sicherstellte das auch der aktuelle Zustand noch gesichert wird und dann gleich der Server neu gestartet wird.

Es war auch letzten Endes nicht der Speicher selbst der weg war. Die Applikation hat ein dermassen komplexes Allokationsschema das im immer irgendwann der Adressraum aufgrund von Fragmentierung ausgegangen ist. Natürlich war der globale Handler nur Flickwerk aber es war besser als nichts.

Trap
2005-11-02, 15:27:28
Es gibt 3 new:
placement new, nothrow new und das normale new

Das normale new wirft eine Exception bei out of memory, das nothrow gibt 0 zurück und bei placement new hat man den Speicher vorher schon.

Coda
2005-11-02, 16:07:10
Wie seht ihr das eigentlich, ist using namespace std; ok oder nicht? Da hab ich auch schon unterschiedliche Meinungen gehoert.Solange du es nicht im Header machst ist es ok. Wenn man allerdings viele namespace-Kollissionen bekommt würde ich es wieder rausnehmen.

Das normale new wirft eine Exception bei out of memory, das nothrow gibt 0 zurück und bei placement new hat man den Speicher vorher schon.Gibt es dieses "nothrow new" wirklich im Standard? Wäre mir neu.

Dazu kommt, dass viele OS gar nicht NULL zurückgeben, wenn ein malloc() fehlschlägt. Linux z.B. sucht beim malloc() gar nicht nach freiem Speicher, es gibt einfach einen Pointer zurück. Erst wenn auf den Speicher das erste Mal zugegriffen wird, versucht der Kernel, den Speicher auch tatsächlich zu organisieren. Sollte das fehlschlagen, killt er das betroffene Programm (also einfach das nächste, für das kein Speicher mehr vorhanden ist) schlicht mit einem SIGSEGV.Bist du dir da ganz sicher?

Senior Sanchez
2005-11-02, 16:08:17
Damit kann man das ganze reproduzieren:

class Foo1
{
public:
void B ();

int V;
};

class Foo2
{
public:
void C (Foo1& rFoo);
};

void Foo1::B ()
{
Foo2 foo;

foo.C (*this);
}

void Foo2::C (Foo1& rFoo)
{
rFoo.V = 0;
}

Foo1* A ()
{
return NULL;
}

int _tmain(int argc, _TCHAR* argv[])
{
A()->B();
}

Hmm, solche Spirenzien gehen unter Java denke ich nicht ;)
Unter Java würde er schon bei deinem quasi ersten Aufruf null->B(); ne NullPointerException werfen. Ist aber natürlich schwach, wenn sowas durchgeht, wobei ich mich frage warum er halt nicht gleich abschmiert, wenn du auf nem null ne Methode aufrufen willst. Er sollte ja dann eigentlich nicht mal bis zur Initialisierung der V Variable kommen.

Abnaxos
2005-11-02, 16:31:14
Es gibt 3 new:
placement new, nothrow new und das normale new
Gut möglich, dass es die (theoretisch) gibt. Meine C++-Kenntnisse sind etwas eingerostet bzw. als ich noch aktiv C++ programmiert habe, gab es noch nicht einmal Namespaces, da hat sich seither einiges getan. :biggrin:

Aber auch die gehen alle auf malloc(3) zurück, und wenn malloc(3) sich einfach weigert, eine OOM-Bedingung zu melden, was es tun wird, weil der Kernel aus Prinzip sagen wird: "OK, da hast du deinen Speicher", nützen diese verschiedenen Verhaltensweisen von new herzlich wenig.

Bist du dir da ganz sicher?
Yep. Das ist der berüchtigte OOM-Killer, google mal danach. Ausserdem, aus malloc(3):
BUGS
By default, Linux follows an optimistic memory allocation strategy. This means that when malloc() returns non-NULL there is no guarantee that the memory really is available.
/EDIT: 100%-ig korrekt war meine Behauptung dennoch nicht, es scheint, als würde ein SIGKILL gesendet, kein SIGSEGV. Das ist aber ein Detail, der Effekt ist derselbe ... ;)

Coda
2005-11-02, 16:35:48
Das wichtigste hast du weggelassen
By default, Linux follows an optimistic memory allocation strategy. This means that when malloc() returns non-NULL there is no guarantee that the memory really is available. This is a really bad bug.Wenigstens sehen sie's ein ;)

Demirug
2005-11-02, 17:03:58
Hmm, solche Spirenzien gehen unter Java denke ich nicht ;)
Unter Java würde er schon bei deinem quasi ersten Aufruf null->B(); ne NullPointerException werfen. Ist aber natürlich schwach, wenn sowas durchgeht, wobei ich mich frage warum er halt nicht gleich abschmiert, wenn du auf nem null ne Methode aufrufen willst. Er sollte ja dann eigentlich nicht mal bis zur Initialisierung der V Variable kommen.

Warum Initialisieren? Bei C++ werden member Variablen nicht automatisch Initialisiert. Zudem geht er bei dem Aufruf von B davon aus das ein vollständiges Objekt vorliegt.

Er wäre bei dem Aufruf von B abgeschmiert wenn B virtuel wäre den dann hätte er für den Aufruf die VTable auslesen müssen und das hätte schon die Speicherverletzung hervorgerufen. Bei nicht virtuellen Methoden wird der Objekt Zeiger (this) aber einfach nur mit übergeben. Das ist sozusagen der erste Parameter bei jeder Member Funktion.

C++ ist im Prinzip so aufgebaut das man es durch einen Crosscompiler komplett in C-Code wandeln kann der dann compiliert werden kann. Zudem wurde Sicherheit der Performances geopfert.

Coda
2005-11-02, 17:07:34
C++ ist im Prinzip so aufgebaut das man es durch einen Crosscompiler komplett in C-Code wandeln kann der dann compiliert werden kann. Zudem wurde Sicherheit der Performances geopfert.Bei welcher nicht-VM Sprache ist das nicht möglich? :rolleyes:

Neomi
2005-11-02, 17:08:54
Hmm, solche Spirenzien gehen unter Java denke ich nicht ;)
Unter Java würde er schon bei deinem quasi ersten Aufruf null->B(); ne NullPointerException werfen. Ist aber natürlich schwach, wenn sowas durchgeht, wobei ich mich frage warum er halt nicht gleich abschmiert, wenn du auf nem null ne Methode aufrufen willst. Er sollte ja dann eigentlich nicht mal bis zur Initialisierung der V Variable kommen.

Warum sollte er vorher abschmieren? Der this-Pointer wird beim ersten Methodenaufruf (Foo1::B) zwar übergeben, auf ihn wird aber nicht zugegriffen. Beim zweiten Aufruf (Foo2::C) wird trotz *this auch nur die Adresse übergeben, ist ja eine Referenz. Erst in Foo2::C wird dann darauf zugegriffen.

Der this-Pointer ist kein magisches Ding, sondern ein simpler Funktionsparameter, der nur nicht explizit auftauchen muß. Objektorientierung ist nur eine Vereinfachung für den Programmierer, entspricht aber nicht der Funktionsweise eines Computers.

Senior Sanchez
2005-11-02, 17:26:36
Warum Initialisieren? Bei C++ werden member Variablen nicht automatisch Initialisiert. Zudem geht er bei dem Aufruf von B davon aus das ein vollständiges Objekt vorliegt.

Er wäre bei dem Aufruf von B abgeschmiert wenn B virtuel wäre den dann hätte er für den Aufruf die VTable auslesen müssen und das hätte schon die Speicherverletzung hervorgerufen. Bei nicht virtuellen Methoden wird der Objekt Zeiger (this) aber einfach nur mit übergeben. Das ist sozusagen der erste Parameter bei jeder Member Funktion.

C++ ist im Prinzip so aufgebaut das man es durch einen Crosscompiler komplett in C-Code wandeln kann der dann compiliert werden kann. Zudem wurde Sicherheit der Performances geopfert.

Initialisieren war das falsche Wort, sry. Ich meinte natürlich wenn in der Methode C der Wert für V gesetzt wird.


Warum sollte er vorher abschmieren? Der this-Pointer wird beim ersten Methodenaufruf (Foo1::B) zwar übergeben, auf ihn wird aber nicht zugegriffen. Beim zweiten Aufruf (Foo2::C) wird trotz *this auch nur die Adresse übergeben, ist ja eine Referenz. Erst in Foo2::C wird dann darauf zugegriffen.

Der this-Pointer ist kein magisches Ding, sondern ein simpler Funktionsparameter, der nur nicht explizit auftauchen muß. Objektorientierung ist nur eine Vereinfachung für den Programmierer, entspricht aber nicht der Funktionsweise eines Computers.


Ich meinte als ersten Aufruf aber A()->B().
Und da wird auf einem Null-Zeiger eben ne Member-Methode von Foo1 aufgerufen was meines Verständnisses nach eben humbug ist (Demirug hats ja schon entkräftigt und auf virtual hingewiesen). Unter Java wird sowas sofort per NullPointerException weggefangen. Ich meine, ist ja auch irgendwie logisch, da es dem OOP gedanken doch widerspricht auf einem nicht-existenten Objekt ne Methode einer Klasse aufzurufen.

Coda
2005-11-02, 17:31:46
Alles was runtime-kosten verursacht wurde bei C++ bewusst weggelassen. Hat Vor- und Nachteile.

Abnaxos
2005-11-02, 17:51:13
Und da wird auf einem Null-Zeiger eben ne Member-Methode von Foo1 aufgerufen was meines Verständnisses nach eben humbug ist (Demirug hats ja schon entkräftigt und auf virtual hingewiesen). Unter Java wird sowas sofort per NullPointerException weggefangen. Ich meine, ist ja auch irgendwie logisch, da es dem OOP gedanken doch widerspricht auf einem nicht-existenten Objekt ne Methode einer Klasse aufzurufen.
Das ist eine philosophische Frage. Python z.B. kennt gar kein "kein Objekt", NULL wird durch das Objekt None repräsentiert. Entsprechend wird so ein Aufruf in Python auch keine NullPointerException oder so werfen, dafür wird Python dich anschreien, weil die Methode, die du aufzurufen versuchst, im None-Objekt nicht definiert ist. Objective-C macht es (je nach Implementation) wieder anders, die GNU- und AFAIK auch die Apple-ObjC-Runtime machen einfach nichts, wenn man eine Nachricht an NULL zu schicken versucht. Auch dieser Ansatz ist in vielen Fällen durchaus sinnvoll, man erspart sich damit viele Abfragen auf NULL, da das häufig genau das ist, was man erreichen möchte. Auf der anderen Seite kann es natürlich auch gefährlich sein, weil man so NULLs verpassen könnte, die man nicht verpassen will, bzw. das Programm evtl. an einer ganz anderen Stelle abschmiert, als wo der Fehler passiert ist.

Ich finde, dass keiner der obigen Ansätze dem OOP-Gedanken widerspricht: Java meint: "Njet, da hat es gar kein Objekt, also kann ich auch keine Methode aufrufen", Python meint: "Falsches Objekt, das hat keine solche Methode", ObjC lässt sämtliche Nachrichten an NULL im Nirvana verschwinden. That's it. :)

Senior Sanchez
2005-11-02, 18:40:58
Das ist eine philosophische Frage. Python z.B. kennt gar kein "kein Objekt", NULL wird durch das Objekt None repräsentiert. Entsprechend wird so ein Aufruf in Python auch keine NullPointerException oder so werfen, dafür wird Python dich anschreien, weil die Methode, die du aufzurufen versuchst, im None-Objekt nicht definiert ist. Objective-C macht es (je nach Implementation) wieder anders, die GNU- und AFAIK auch die Apple-ObjC-Runtime machen einfach nichts, wenn man eine Nachricht an NULL zu schicken versucht. Auch dieser Ansatz ist in vielen Fällen durchaus sinnvoll, man erspart sich damit viele Abfragen auf NULL, da das häufig genau das ist, was man erreichen möchte. Auf der anderen Seite kann es natürlich auch gefährlich sein, weil man so NULLs verpassen könnte, die man nicht verpassen will, bzw. das Programm evtl. an einer ganz anderen Stelle abschmiert, als wo der Fehler passiert ist.

Ich finde, dass keiner der obigen Ansätze dem OOP-Gedanken widerspricht: Java meint: "Njet, da hat es gar kein Objekt, also kann ich auch keine Methode aufrufen", Python meint: "Falsches Objekt, das hat keine solche Methode", ObjC lässt sämtliche Nachrichten an NULL im Nirvana verschwinden. That's it. :)


Ist natürlich was dran, bloß dann finde ich es inkonsequent von C/C++ bei Zugriffen auf Elementvariablen dann rumzumeckern (weil ja keine entsprechende Speicheradresse bzw. reservierter Speicher auffindbar ist), aber andererseits dann Zugriffe auf Elementmethoden zuzulassen, zu mal die Wahrscheinlichkeit ja recht hoch ist, dass es in der Methode spätestens krachen wird.

Coda
2005-11-02, 19:28:22
C++ überprüft gar nichts und meckert auch nicht. Du bekommst bloß eine Zugriffsverletzung um die Ohren geschmissen die der Code eben erst an diesem Punkt produzieren wird.

Eine Memberfunktion verwendet "this" nur wenn auf Variablen der Klasse zugegriffen wird und sonst nicht. Wenn das nicht geschieht kann this beliebig sein und es wird nichts passieren.

Senior Sanchez
2005-11-02, 19:39:09
C++ überprüft gar nichts und meckert auch nicht. Du bekommst bloß eine Zugriffsverletzung um die Ohren geschmissen die der Code eben erst an diesem Punkt produzieren wird.

Eine Memberfunktion verwendet "this" nur wenn auf Variablen der Klasse zugegriffen wird und sonst nicht. Wenn das nicht geschieht kann this beliebig sein und es wird nichts passieren.

Jops, soweit bin ich jetzt auch ;)
Ob das gut ist, sei mal dahin gestellt *g*

Coda
2005-11-02, 19:52:24
C++ hat den Anspruch so schnell wie möglich zu sein. Falls man das nicht braucht dann nehme man bitte C# oder Java.

Senior Sanchez
2005-11-02, 20:21:06
C++ hat den Anspruch so schnell wie möglich zu sein. Falls man das nicht braucht dann nehme man bitte C# oder Java.

Joar, ok, damit seis mal entschuldigt *g*

RoKo
2005-11-02, 21:27:59
Bei Objekten mit virtuellen Funktionen z.B.
Warum?
Du kannst dir sicher sein, dass es vom Kompilat her völlig egal ist ob Pointer oder Referenzen verwendet werden.
Wieso?

Ich habe es übrigens eben in VC++7.1 ausprobiert und es kommt weder Variante a noch Variante b raus...sondern die Pointer werden auch komplett wegoptimiert. Nett. Sowohl bei einem einfachen swap, einem swap, das eine Eingabe mit verrechnet und auch bei Objekten mit virtuellen Funktionen. Bei den Pointern kann das freilich nur funktionieren, wenn man sie praktisch nur wie Referenzen verwendet.

Coda
2005-11-02, 21:51:23
Warum?Weil sich virtuelle Funktionen generell nicht inlinen lassen, aus verständlichen Gründen

Wieso?Weil beides im Compilerjargon sogenannte lvalues sind. Das läuft einfach auf den gleichen Code raus. Wenn die Referenz wegoptimiert werden kann geht es auch mit Pointern und andersrum.

RoKo
2005-11-02, 22:23:27
Weil sich virtuelle Funktionen generell nicht inlinen lassen, aus verständlichen Gründen
Dass es mit nicht-inline-Funktionen nicht klappen kann, habe ich in meinem ersten Post schon geschrieben. Es ging aber doch um die Parameter.
Weil beides im Compilerjargon sogenannte lvalues sind. Das läuft einfach auf den gleichen Code raus. Wenn die Referenz wegoptimiert werden kann geht es auch mit Pointern und andersrum.
Du wolltest mir doch sagen, dass man das alles nicht wegoptimieren kann, oder? Ich wiederhole nochmal meine Frage von der ersten Seite, damit Du weißt, in welchem Diskussionsstrang wir uns befinden: "Gibt es einen Grund, weshalb Referenz-Parameter geinlineter Funktionen nicht generell wegoptimiert werden können außer faulen Compiler-Programmierern?"

Coda
2005-11-02, 23:00:23
Dass es mit nicht-inline-Funktionen nicht klappen kann, habe ich in meinem ersten Post schon geschrieben. Es ging aber doch um die Parameter.Äh und wie willst du die sonst übergeben? Kopieren oder Referenz drauf, mehr Möglichkeiten gibt's nicht.

Du wolltest mir doch sagen, dass man das alles nicht wegoptimieren kann, oder? Ich wiederhole nochmal meine Frage von der ersten Seite, damit Du weißt, in welchem Diskussionsstrang wir uns befinden: "Gibt es einen Grund, weshalb Referenz-Parameter geinlineter Funktionen nicht generell wegoptimiert werden können außer faulen Compiler-Programmierern?"Nein. Das gleiche gilt aber auch für Pointer.

Trap
2005-11-02, 23:29:50
Gibt es dieses "nothrow new" wirklich im Standard? Wäre mir neu.
Ja, siehe:
http://www.gotw.ca/publications/mill15.htm
http://www.gotw.ca/publications/mill16.htm

Außerdem gibt es im Header <new> eine Funktion std::set_new_handler, der man eine Funktion als Parameter übergeben kann, die bei out-of-memory Situationen aufgerufen wird.
Ausprobiert hab ich das aber noch nicht...

RoKo
2005-11-03, 00:13:43
Äh und wie willst du die sonst übergeben? Kopieren oder Referenz drauf, mehr Möglichkeiten gibt's nicht.
Ich verstehe den Zusammenhang der Frage nicht.
Nein.
Aha. Dann hat sich ja auch das mit den Objekten mit virtuellen Funktionen erledigt.
Das gleiche gilt aber auch für Pointer.
Nur wenn man sie wie Referenzen einsetzt.

Coda
2005-11-03, 00:16:38
Nur wenn man sie wie Referenzen einsetzt.Natürlich. Aber andernfalls must du eh Pointer einsetzen X-D

Bleiben wir dabei das Referenzen bei gleichem Einsatzweck auch gleich viel Resourcen verbrauchen wie Pointer.

RoKo
2005-11-03, 01:15:44
Natürlich. Aber andernfalls must du eh Pointer einsetzen X-D

Bleiben wir dabei das Referenzen bei gleichem Einsatzweck auch gleich viel Resourcen verbrauchen wie Pointer.
Gerne. Und dabei, dass sie ein anderes Konzept modellieren ;)