2012-07-04 10 views
7

Powiel możliwe:
Calling class method through NULL class pointerDlaczego metoda wywoływania przez wskaźnik zerowy "działa" w C++?

#include <iostream> 
using namespace std; 
class test 
{ 
    int i; 
public: 
    test():i(0){ cout << "ctor called" << endl;} 
    void show() 
    { 
     cout<<"show fun called"<<endl; 
    } 
}; 

int main(int argc , char *argv[]) 
{ 
    test *ptr = NULL; 
    ptr->show(); 
    return 0; 
} 

wyraźnie nie konstruktor zostanie wywołany. Czy to standard? lub tylko niektóre optymalizacji kompilatora, ponieważ ten wskaźnik nie jest używany w funkcji członka show()?

+5

Dereferencja zerowy wskaźnik jest UB. – chris

+2

Dodaj 'i = 1;' wewnątrz 'show()' i spróbuj go uruchomić. –

+1

chris, UB oznacza do wdrożenia kompilatora? i używam g ++ 4.6.3. Jesse Good, oczywiście, wadę, nie ma wątpliwości. Zastanawiam się, czy kompilator wygeneruje kod bez tego dla funkcji członka, która jej nie potrzebuje. – bbc

Odpowiedz

22

Wskaźnik nie jest potrzebny do wywołania metody. Typ wskaźnika jest znany, więc kod metody jest znany. Metoda nie używa wartości this, więc działa poprawnie. Jest to niezdefiniowane zachowanie, ale jego efektywność nie polega na sprawdzaniu, czy wskaźnik ma wartość NULL, więc działa.

+0

(Jak) metoda wirtualna wpłynie na obserwowane zachowanie? –

+2

Spodziewam się, że wywołanie metody wirtualnej zakończy się niepowodzeniem, ponieważ musi wyszukać metodę w tabeli vtable, która znajdowałaby się na drugim końcu wskaźnika, ale wskaźnik ma wartość NULL. –

+0

Wada seg dla wirtualnego z g ++, nie jestem pewien, czy jest to również zależne od implementacji. czy kompilator wygeneruje vptr i vtblr bez obiektu klasy? chyba nie ze względu na efektywność? – bbc

1

To nie jest prawidłowe, zachowanie jest niezdefiniowane, a rzeczywiste wyniki zależą od kompilatora.

+0

To jest poprawne. Funkcja __thiscall, która nie ma dostępu do tego "wskaźnika", jest po prostu statyczną funkcją. Po prostu dodaj "static" - jest to najlepsze, co możesz zrobić w kodzie, który z definicji jest statyczny :) I zmusza funkcję do używania __cdecl, więc nie wymaga tego wskaźnik. Należy pamiętać, że dodanie wirtualnego spowoduje następnie bardziej skomplikowane przygotowanie, które wymaga dereferencji wskaźnika, a następnie segfault. –

-1

Po pierwsze, jest niepoprawny, ponieważ wywołuje niezdefiniowane zachowanie. Naprawdę zastanawiasz się, dlaczego kompilator na to pozwala, a odpowiedź brzmi, ponieważ jest to kod typu boneheaded, który po prostu nie pojawi się w prawdziwej aplikacji, więc po co zawracać sobie głowę? Prawdziwy problem pojawia się, gdy wskaźnik jest unieważniany w środowisku wykonawczym w sposób, którego nie można przewidzieć za pomocą statycznej analizy kodu.

Kompilator nie jest tam, aby trzymać rękę, jest tam, aby skompilować swój kod zgodnie ze standardem. Jeśli oferuje użyteczne ostrzeżenia, to świetnie, ale tam nie ma nic syntaktycznie lub semantycznie nielegalnie, po prostu piszesz kod, który powoduje niezdefiniowane zachowanie.

+0

dzięki za poradę. – bbc

8

Jeśli spojrzysz na złożenie (dla co najmniej jednego kompilatora), możesz zobaczyć, dlaczego działa (mimo że jest niezdefiniowanym zachowaniem, jak wiele osób wskazało). Z tych dwóch wierszach:

test *ptr = NULL; 
ptr->show(); 

Zespół ten jest generowany (w jednym kompilatora prostu próbowałem):

00000004: C7 45 FC 00 00 00 mov   dword ptr [ebp-4],0 
      00 
0000000B: 8B 4D FC   mov   ecx,dword ptr [ebp-4] 
0000000E: E8 00 00 00 00  call  [email protected]@@QAEXXZ 

To popycha NULL (0) na stosie i wywołuje metodę, ponieważ adres metoda jest niezależna od rzeczywistej instancji obiektu.

Powiązane problemy