Faktycznie, standard ISO 14882 stwierdza (rozdział 8.3.2, punkt 4):
"A reference shall be initialized to refer to a valid object or function. [Note: 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."
Ale standard sobie, a implementacje sobie, więc to "only way" wcale nie jest takie only :) Podwińcie rękawy i zasłońcie oczy, bo pięknie to tu nie będzie ;)
W klasycznym podejściu można by próbować uzyskać referencję do NULL, dokonując derefencji takiego wskaźnika:
int* p = NULL;
int& r = *p;
Od razu widać, że to nie pójdzie,
NullPointerException
gwarantowany :) My zastosujemy inny trik, który wynika ze szczegółów implementacji referencji w większości kompilatorów. Mianowicie "pod spodem" są one implementowane z użyciem najzwyklejszych wskaźników. Oznacza to, że dwie następujące struktury:
struct APtr
{
int* p_a;
};
struct ARef
{
int& a;
};
jeżeli chodzi o "bitowy" wygląd są identyczne - mimo że jedna zawiera wskaźnik na
int
, a druga referencję do tego typu. Uzbrojeni w tą wiedzę przyszykujmy więc
APtr
ze wskaźnikiem na NULL:APtr aptr;
aptr.p_a = NULL;
Niestety zwykłe rzutowanie z jednego typu na drugi nie zadziała:
ARef aref((ARef)aptr);
Ale możemy spróbować zrzutować miedzy sobą wskaźniki na oba typy z użyciem
reinterpret_cast
:ARef aref(*reinterpret_cast<ARef*>(&aptr));
Czyli najpierw rzutując uzyskujemy wskaźnik na
ARef
ze wskaźnika na typ APtr
, a następnie z pomocą operatora * uzyskujemy wskazywany obiekt (czyli aptr
) jako obiekt klasy ARef
, który to przekazujemy do konstruktora kopiującego. I...Sukces! Tzn. brak błędów kompilacji. To teraz wystarczy sprawdzić, czy faktycznie
aref.a
zawiera referencję NULL:int& ref = aref.a;
std::cout << "Adres referencji to: " << &ref << std::endl;
A efekt to (w Visual C++ 2008 Express):
Adres referencji to: 00000000
Czyli nie ma rzeczy niemożliwych ;) I tym optymistycznym akcentem...
Cały kody źródłowy to zwykłego przeklejenia i sprawdzenia samemu w swoim ulubionym kompilatorze:
#include <iostream>
using namespace std;
struct APtr
{
int* p_a;
};
struct ARef
{
int& a;
};
int main()
{
APtr aptr;
aptr.p_a = NULL;
ARef aref(*reinterpret_cast<ARef*>(&aptr));
int& ref = aref.a;
std::cout << "Adres referencji to: " << &ref << std::endl;
return 0;
}
All credit shall go to JKop:
http://www.velocityreviews.com/forums/showpost.php?p=1512585&postcount=7
Kumle CPluPlusowcy mówili, że jak przeprowadzają rozmowę kwalifikacyjną i się pytają, jaka jest różnica pomiędzy wskaźnikiem a referencją, to jedną z częstszych odpowiedzi jest to, że referencja nie może być null - wtedy to uświadamiają takiego delikwenta, że jednak może być :)
OdpowiedzUsuńHey, I enjoyed this blog, where you talk about the growth of freelancers and web developers in India. One of the great freelancing platforms for eCommerce developers is Eiliana.com. Take a look at the freelancing platform too. It is a new yet great platform for technical experts.
OdpowiedzUsuń