Newsgroup: it.comp.lang.c++
Titolo: chiamare funzione membro da puntatore null

Salve a tutti!!
Ho il seguente codice:
===============================
#include <iostream>
class T
{
    T* p;
    unsigned int h;
    public:
	T() : p(0), h(0){}
    private:
       	static unsigned int get_height(T* t){
             if(t)
		return(t->h);
	     else
		return(0);
	};
    public:
	unsigned int get_height()
	{
	    return(get_height(this));
	}
	void set_height()
	{
	    h = 1 + p->get_height();
	}
};
int main()
{
	T t;
	t.set_height();
	std::cout << "HEIGHT:" << t.get_height() << std::endl;
}
====================================
Questo codice compilato ed eseguito stampa:
HEIGHT: 1
Io invece mi aspettavo che andasse in SEGFAULT alla riga p->get_height
essendo p un puntatore a NULL. Subito ho pensato che essendo
get_height() una funzione da una riga sola il compilatore la mettesse
inline chiamando quindi static get_height(p) cosi ho disabilitato
l'inline ma il programma continua a funzionare....
Qualcuno sa dirmi se questo comportamento è normale e perchè !? :)
Grazie

 Qualcuno sa dirmi se questo comportamento è normale e perchè !? :)
Sì, certo che funziona. Se chiami le funzioni membro di NULL queste 
funzioni vengono eseguite normalmente con this=NULL. Almeno questa è 
l'implementazione più semplice della chiamata di funzioni membro, non so 
se lo standard lo garantisce o meno.
E.

 so se lo standard lo garantisce o meno.
Quindi i segfault che avvengono quando si chiama una funzione da un
puntatore nullo non avvengono al momento della chiamata ma all'interno
della chiamata quando si cerca di assegnare this->h essendo this=null
(se non si effettuano controlli sulla validità del puntatore).
Non avevo mai fatto caso a questo comportamento.
Qualcuno può confermare che sia standard?
Ciao

 Qualcuno può confermare che sia standard?
Non saprei, probabilmente si, ma non mi sembra utile farci
affidamento...
Scrivere funzioni tipo la
static unsigned int get_height(T* t)
solo per controllare che il puntatore sia valido... A questo punto è
meglio incorrere nel "segfault", almeno ti accorgi che c'è qualcosa da
correggere da qualche altra parte...
Ciao

 so se lo standard lo garantisce o meno.
Quanto è ortodosso questo sistema per evitare di fare sempre controlli
sulla validità del puntatore!?! Ovviamente non è da usare in tutti i
contesti ma in questo mio particolare mi sembra utile ma non vorrei
essere messo alla forca :)

 essere messo alla forca :)
Guarda, non te la prendere, ma penso che il suo grado di ortodossia
sia prossimo a quello di un satanista ai tempi dell'inquisizione.
:-)

 (se non si effettuano controlli sulla validità del puntatore).
Oppure quando provi a scrivere/leggere un attributo della classe e 
quindi dereferenzi NULL.
E.


Tu stai facendo
class T {
	int val;
	public:
	int getVal() {
		if (this) return val;
		else return 0;
	};
};
e poi
main () {
	T *p;
	p=0;
	int n=p->getVal();
}
Mi pare un comportamento molto pericoloso. Soprattutto perché hai 
l'impressione di poter utilizzare p anche per altre operazioni che 
invece potrebbero darti segFault.
Non vedo perché invece non fai:
	int n=0;
	if (p)
		n=p->getVal();
che è molto più chiaro e anche più efficiente.
E.

 se lo standard lo garantisce o meno.
Posso immaginare perché "funzioni" nel caso in questione. Posso anche
prevedere che cesserà di "funzionare" se la funzione viene
dichiarata 'virtual'.
In ogni caso, secondo lo Standard, non è lecito dereferenziare un puntatore
nullo.
Quindi, per rispondere alla domanda dell'OP, il comportamento descritto è un
comportamento possibile. Se sia "normale" non si può dire, visto che il
comportamento previsto dallo Standard è indefinito.
Max
 

 sulla validità del puntatore!?!
Io direi che è undefined behaviour bella e buona. 
I punti chiave dello standard sono questi:
1) Se E1 ha tipo "puntatore a classe X" allora l'espressione E1-E2 è
convertito alla forma equivalente  (*(E1)).E2.
2) The postfix expression before the dot or arrow is evaluated,  con la
nota:
 This evaluation happens even if the result is unnecessary to determine
the value of the entire postfix expression, for example if the 
id-expressiondenotes a static member. 
Dal che segue che tu stai dereferenziando un NULL pointer, che è appunto
undefined behaviour. Non dovresti farlo. 
Nota che se la funzione non dipende dal contenuto dell'oggetto (e questo
darebbe errore in ogni caso) puoi metterla come statica. A quel punto la
puoi chiamare come
X::foo();
e questo è invece perfettamente lecito.
-- 
blog:  
http://www.akropolix.net/rik0/blogs
 | Uccidete i filosofi,
site:  
http://www.akropolix.net/rik0/
      | tenetevi riso e
forum: 
http://www.akropolix.net/forum/
     | bacchette per voi.

 sulla validità del puntatore!?!
C'è anche un altro problema: *non sempre* un puntatore invalido è ==
0...
Se per esempio l'oggetto puntato è stato cancellato, il puntatore
conterrà ancora un indirizzo (invalido).
E con questo sistema non lo "intercetteresti" comunque...
ciao

 correggere da qualche altra parte...
il fatto che sia null è esatto, la classe rappresenta il nodo di un
albero e se questo non ha figli il puntatore è null!
con quel sistema volevo evitare di controllare in ogni funzione se il
puntatore era valido; credo che opterò per la funzione statica.
Ciao!

 quel punto la puoi chiamare come
dipende dal contenuto dell'oggetto se l'oggetto esiste, quindi glielo
passo come parametro.
Grazie
Ciao

 passo come parametro.
Mi pare corretto.
