Bin ich eigentlich der einzige, der sowas macht?
-
Nur um das mit den Null Referenzen nochmal zu klären. Im Stroustrup (3.Aufl.) Steht in Kapitel 15.4.1.1 "dynamic_cast von Referenzen"
"Um ein polymorphes Verhalten zu erreichen , muss ein Objekt über einen Zeiger oder eine Referenz manipuliert werden. Wenn ein dynamic_cast für einen Zeigertyp benutzt wird, bedeutet 0 einen Fehler. Das ist bei Referencen weder möglich noch erwünscht."
und dann gehts weiter dass die stattdessen einen bad_cast werfen.
mfg, KdeE
-
Das dereferenzieren eines NULL-Zeigers erzeugt undefiniertes Verhalten. Und das machst du ja hier.
int &i(* ((int*)0) );
-
Dann besteht aber nach dem, was bisher gesagt wurde, kein Unterschied zwischen NULL-Zeiger und NULL-Referenzen. *einmisch*
Trotzdem sorge ich natürlich dafür, dass Referenzen bei mir nicht NULL sind, das widerspricht meiner Intuition.
-
Mal ne frage nebenbei: Hier ist dauernd die rede von Exception abfangen. Wie soll das gehen?
catch in der initialisierungsliste "rethrowed" automatisch, falls keine andere Exception geworfen wird. Ein Abfangen ist deshalb gar nicht möglich. ODer bin ich da falsch informiert.
-
a) new ausserhalb fangen
b) kein new in die initialisierungsliste
-
ann besteht aber nach dem, was bisher gesagt wurde, kein Unterschied zwischen NULL-Zeiger und NULL-Referenzen. *einmisch*
Nonsense. Das ist eine Nullaussage. Es gibt in Standard-C++ *keine* Nullreferenz. Demzufolge kannst du auch nichts damit vergleichen.
Nocheinmal: Es gibt *keine* Nullreferenz. Jeder denkbare Weg der zu sowas wie einer Nullreferenz (eine Referenz die nicht an ein vorhandenes Objekt gebunden wird) führen könnte, hat mindestens in einem Teilschritt undefiniertes Verhalten. Hat aber mindestens ein Teilschritt undefiniertes Verhalten, dann hat der ganze Ausdruck undefiniertes Verhalten. Einmal undefiniert, immer undefiniert. Von diesem Moment an, kannst du alle weiteres Vergessen. Du hast die Definition verletzt. Weitere Schlussfolgerungen sind sinnlos.Referenzen haben eine vollständig andere Semantik als Pointer, auch wenn sie häufig über solche realisiert werden. Eine Referenz ist ein *Alias* für ein *vorhandenes* Objekt. Eine Referenz muss *immer* an ein *zuvor* existierendes Objekt gebunden werden.
catch in der initialisierungsliste "rethrowed" automatisch, falls keine andere Exception geworfen wird. Ein Abfangen ist deshalb gar nicht möglich. ODer bin ich da falsch informiert.
Nein. Das ist schon richtig.
Aber vielleicht kannst Du mir mal einen Link zum Standard geben, wo das beschrieben ist was Du sagst
Ich habe hier gerade leider keinen Standard. Aus dem Kopf würde ich jetzt aber mal auf 8.5 (oder 8.5.3?) tippen. Such nach Referenz und Initialisierung. Nullpointer wäre auch noch ein Stichwort.
darf man etwa eine Referenz nicht mit dem Inhalt (in wirklichkeit ja dann doch der Adresse) eines Pointers initialisieren?
Darf man. Aber nur, wenn der Pointer garantiert != 0 ist.
Das ist legal:Person* getPerson(); ... Person* p = getPerson(); assert(p); Person& rp = *p;
Das hingegen:
Person& rp = *getPerson();
hat *undefiniertes Verhalten*, falls getPerson einen Nullpointer liefert und zwar bereits *bevor* die Referenz gebunden wird. Hier entsteht also *keine* Nullreferenz, da zuvor bereist die Definition von Standard-C++ verletzt wurde und demzufolge keine weiteren Aussagen abgeleitet werden können.
-
-kannst locker auf andere wege impls verstecken. mußt halt den mut haben, auch mal ne non.standard-zeile zu schreiben und dann dem sutter nicht alles glauben.
Den Mut dazu hab ich schon. Jetzt muss ich nur noch wissen, wie? :)[/quote]
noch hatte ich nicht den echten bedarf, habs also nicht ausgearbeitet.
aber den echten bedarf, nicht immer die <windows.h> mitzucompilieren, hatte ich schon. daher hab ich dort abgeholfen. wirst schon den kram ausbauen können, wie du magst. und immer dran denken: abstraktion darf das prog nicht lahm machen.CriticalSection.h
#ifndef CRITICALSECTION #define CRITICALSECTION #include "HiddenPod.h" #include "NoCopy.h" struct _CRITICAL_SECTION; class CriticalSection{ private: HiddenPod<_CRITICAL_SECTION,24,4> critSect; NOCOPY; public: CriticalSection(); ~CriticalSection(); void lock(); void unlock(); }; #endif
CriticalSection.cpp
#include "CriticalSection.h" #include <windows.h> CriticalSection::CriticalSection(){ InitializeCriticalSection(&critSect); } CriticalSection::~CriticalSection(){ DeleteCriticalSection(&critSect); } void CriticalSection::lock(){ EnterCriticalSection(&critSect); } void CriticalSection::unlock(){ LeaveCriticalSection(&critSect); }
und wenn ich in der main.cpp mal ne critsect braucht, inkludiere ich halt critsect, aber muss nicht jedesmal wegen der <windows.h> warten.
der benötigte rest (dateien einfach reinkopiert, wirst schon erkennen, wo dateigrenzen sind):
#ifndef NOCOPY_H #define NOCOPY_H class NoCopy{ private: NoCopy(NoCopy const&); NoCopy& operator=(NoCopy const&); public: NoCopy(){ } }; #ifndef NDEBUG #define NOCOPY NoCopy _noCopy##__LINE__ #else #define NOCOPY typedef int _noCopy##__LINE__ #endif #endif #ifndef HIDDENPOD_H #define HIDDENPOD_H #include "Aligned.h" template<class POD,size_t SIZE,size_t ALIGN> struct HiddenPod{ Aligned<char[SIZE],ALIGN> data; POD* operator&(){ return reinterpret_cast<POD*>(data()); } }; #endif #ifndef ALIGNED_H #define ALIGNED_H #include <cstddef> #include "StaticAssert.h" template<class T,size_t ALIGNMENT> struct Aligned{ STATIC_ASSERT(false); }; template<class T> struct Aligned<T,4>{ T data __attribute__((__aligned__(4))); T* operator()(){ return &data; } T const* operator()() const{ return &data; } }; template<class T> struct Aligned<T,8>{ T data __attribute__((__aligned__(8))); T* operator()(){ return &data; } T const* operator()() const{ return &data; } }; #endif
ps: ich könnte dich bei http://www.coollist.com/group.cgi?id=lang1 brauchen.
edit: uos, da fehlen in der CriticalSection.cpp noch zwei STATIC_ASSERT, umd zu prüfen, ob in der CriticalSection.h size und alignement korrekt geraten wurden.
-
wischmop2 schrieb:
Aber vielleicht kannst Du mir mal einen Link zum Standard geben, wo das beschrieben ist was Du sagst.
http://anubis.dkuug.dk/jtc1/sc22/open/n2356/
Unter 8.3.2§4 findest du folgenden Satz:
in particular, a null reference cannot exist in a well-
defined program, because the only way to create such a reference would
be to bind it to the "object" obtained by dereferencing a null
pointer, which causes undefined behavior.Allerdings ist das eine "Note", also kein normativer Bestandteil des Standards. Ich such gerade schon wie verrückt nach der richtigen Stelle, aber das scheint nicht ganz so einfach zu sein, wie ich mir das gedacht habe.
Was meinst Du mit "Assertions sicherstellen" ? Etwa falls p ein Pointer ist, von dem man nicht weiss ob er gültig ist, so etwas ?
assert(p); p->foo();
Naja, das wäre ja eine Überprüfung.
Technisch ja. Die Grundidee ist aber eine andere. Wenn ich eine Bedingung überprüfe, dann lasse ich zwei Ergebnisse zu. Entweder sie ist wahr oder sie ist falsch. Beide Ergebnisse sind möglich, für beide sind unterschiedliche Konsequenzen ausprogrammiert.
Eine Assertion dagegen macht eine unausgesprochene Annahme über eine bestimte Bedingung explizit. Ein Fehlschlag deutet auf einen Logikfehler im Programm hin, und die einzig mögliche Reaktion ist, den Programmierer darauf aufmerksam zu machen (idR Programmabbruch). Es handelt sich dabei um eine reine Debug-Maßnahme, im fertigen Programm werden die Assertions durch das Makro NDEBUG deaktiviert.
-
Wenn ich dein konkretes Beispiel betrachte, sehe ich überhaupt keinen Grund warum ich die Variante mit new verwenden sollte. Ich sehe keinen einzigen Vorteil, wie die Antworten jedoch zeigen sind andererseits "potentielle" Gefahren vorhanden.
Also im Sinne der Fehlervermeidung, warum sollte jemand nicht die Standard-Vorgehensweise wählen ???
-
@volkard
Gibt es einen besonderen Grund, warum du bei deinem NoCopy nicht auf die EBO baust?
-
ok, alles nochmal zurück spulen - Klar, nie Null-Referenz - http://anubis.dkuug.dk/jtc1/sc22/open/n2356/ ist akzeptiert, Stroustrup natürlich ebenfalls.
Aber zurück zum Topic, es ging darum, ob man solche Referenzen als Membervariablen machen darf, statt pointer. Sie referenzieren auf ein gültiges Objekt, haben somit ein definiertes Verhalten und eben weil Referenzen auf ein gültiges Objekt zeigen, finde ich das eingangs von Optimizer beschriebene Vorgehen besser als Pointer - einem fremden Programmierer vermittelt dies etwas mehr Information als ein Pointer und zwar dass ein Null-Pointer nicht zu beachten ist (oft hat ein Null-Pointer ja auch eine Aussage und sollte nicht durch assert behandelt werden).
Ist dieses Vorgehen nun ok oder gibt es Gründe die dagegen sprechen (bisher haben sich ja alle genannten Gegengründe aufgelöst).
-
HumeSikkins schrieb:
Gibt es einen besonderen Grund, warum du bei deinem NoCopy nicht auf die EBO baust?
EBO?
-
Na gut, dann lass ich das eben wieder sein.
Wär halt schön gewesen, weil es sich genauso benutzen lässt wie ein Member. Aber wenn volkard sagt, es ist scheiße, dann ist es scheiße.
-
Optimizer schrieb:
Wär halt schön gewesen, weil es sich genauso benutzen lässt wie ein Member.
wäre gar nicht so notwendig, daß es sich genauso wie ein member benutzen läßt.
ein früherer versuch, dir <windows.h> lozuwerden, machte es fast genauso wie du. aber mit ner kleinen syntax-änderung, daß man solche members immer mit -> oder * anfassen muß.
in der debug-version waren das simple smart-pointer, in der release-version dinge, die den pointee echt enthalten (in release wurde <windows.h> deshalb inkludiert).
doof wurde das dann bei parameterübergaben und so.
-
volkard schrieb:
HumeSikkins schrieb:
Gibt es einen besonderen Grund, warum du bei deinem NoCopy nicht auf die EBO baust?
EBO?
Ups. Sorry, Klammern vergessen. EBO = Empty base optimization.
Was ich meinte: Warum erbst du nicht von NoCopy (wo bei NoCopy im Release-Modus dann eine komplett leere Klasse ohne Copy-Ctor und Copy-Assignment-Op wäre)?
-
HumeSikkins schrieb:
volkard schrieb:
HumeSikkins schrieb:
Gibt es einen besonderen Grund, warum du bei deinem NoCopy nicht auf die EBO baust?
EBO?
Ups. Sorry, Klammern vergessen. EBO = Empty base optimization.
Was ich meinte: Warum erbst du nicht von NoCopy (wo bei NoCopy im Release-Modus dann eine komplett leere Klasse ohne Copy-Ctor und Copy-Assignment-Op wäre)?
EBO ist hier ist nur ne mikrooptimierung. erbschaft ist nicht der richtige weg, NoCopy zu beschreiben. hab's lange zeit erprobt und es fühlte sich nicht ok an. wenn meine klassen schon inhaltlich viel rumerbenb, stürt dort NoCopy.
und im release-code ist NOCOPY ja eh wegdefiniert.
-
volkard schrieb:
und im release-code ist NOCOPY ja eh wegdefiniert.
Oh man, bin ich blind. Ich habe das typedef übersehen und mich deshalb gewundert.
-
Also jetzt muss ich doch mal Optimizer zustimmen. Ich mache auch so etwas, Referenzen im Konstruktor mittels "new" auf einen gültiges Objekt referenzieren zu lassen und in Destruktor wieder zu löschen.
Was spricht dagenen?Das hier:
void foo(){ try{ throw 5; }catch(...){ throw; } } class A{ int*a; public: A():a(*(new int)){} ~A(){ foo(); delete &a; } };
In dem Fall wird a nicht deleted
-
Das Beispiel versteh ich nicht, wo wird da überhaupt ein A erzeugt, dessen Destruktor nicht aufgerufen wird?! Außerdem hast du einen Tippfehler es muss int &a heißen.
-
Dein Beispiel ist aber unrealistisch, weil Destruktoren keine Exceptions werfen sollten.