2010-08-17 34 views
11
class Foo { 
public: 
static const int kType = 42; 
}; 

void Func() { 
Foo *bar = NULL; 
int x = bar->kType; 
putc(x, stderr); 
} 

¿Es este comportamiento definido? Leí el estándar de C++ pero no pude encontrar nada sobre el acceso a un valor estático de const como este ... He examinado el ensamblado producido por GCC 4.2, Clang ++ y Visual Studio 2010 y ninguno de ellos realiza una desreferencia del NULL puntero, pero me gustaría estar seguro.C++ acceso const estático a través de un puntero NULL

+1

La mayoría de los compiladores deberían advertirle sobre el acceso a los miembros estáticos/const a través de un puntero de instancia. – cHao

Respuesta

10

Puede usar un puntero (u otra expresión) para acceder a un miembro estático; sin embargo, hacerlo a través de un puntero NULL desafortunadamente es un comportamiento oficialmente indefinido. De 9,4/2 "miembros estáticos":

Un miembro estático s de la clase X puede ser se refiere al uso de la calificado-id expresión X :: s; no es necesario utilizar la sintaxis de acceso de miembro de clase (5.2.5) para hacer referencia a un miembro estático. Se puede hacer referencia a un miembro estático utilizando la sintaxis de acceso de miembro de clase, en , en cuyo caso se evalúa la expresión de objeto .

Basado en el ejemplo que sigue:

class process { 
public: 
    static void reschedule(); 
}; 

process& g(); 

void f() 
{ 
    process::reschedule(); // OK: no object necessary 
    g().reschedule();  // g() is called 
} 

La intención es permitir que se asegure de que las funciones serán llamados en este escenario.

+4

Iba a dividir un cabello y señalar que "evaluar la expresión del objeto" no implica necesariamente * desreferenciar el puntero * - podría significar simplemente encontrar el valor del puntero - pero mirando §5.2.5, aparece that it * does *, porque la "expresión de objeto" en este caso se define como "' * bar' ", no solo" 'bar'". Así que tienes razón. – zwol

+0

¿No ha sido esto un tema de debate? La noción de evaluar un objeto, como simplemente nominarlo, es más bien zen. Por otro lado, el ejemplo bajo ese texto muestra claramente una llamada a función. Claramente, quieren decir que la expresión antes del "." o "->" es evaluado. – Potatoswatter

+2

@Potatoswatter: Creo que esta es mi interpretación correcta, pero ciertamente no me considero una autoridad. Si bien creo que la probabilidad de llamar a un miembro estático funciona a través de un puntero NULL apropiadamente escrito es probable que funcione, como es de esperar, es bastante fácil hacer la llamada usando el nombre de tipo en lugar de un puntero (aunque creo que para plantillas eso puede no ser siempre cierto), así que es mejor que simplemente no uses punteros para esto. –

3

creo que el valor real del tipo no se utiliza en absoluto al llamar

bar->kType 

desde kType es estático, y la barra es de tipo Foo es lo mismo que llamar

Foo::kType 

que de todos modos deberías estar haciendo para mayor claridad.

Llamando bar->kType da una advertencia de compilador en la mayoría de las plataformas por este motivo.

+0

+1 "que en realidad debería estar haciendo para mayor claridad" – leedm777

+0

Los miembros correctos y estáticos se almacenan independientemente de las instancias de los objetos, por lo que el objeto nunca se desmarca. – casablanca

+0

@casablanca: es un detalle de implementación. Si tiene pruebas documentadas de que este es el comportamiento de su versión de compilación en particular, ¡es genial! De lo contrario, no lo hagas. Ciertamente, en términos de una pregunta sobre la máquina abstracta llamada C++, me temo que esta no es una respuesta "correcta". –

1

Aparte de la cuestión sobre cómo acceder a través del puntero NULL, hay otro tema sutil en el código

$ 9.4.2/2 - "La declaración de un miembro de datos estáticos en su definición de clase no es una definición y puede ser de un tipo incompleto que no sea un vacío calificado por cv. La definición de un miembro de datos estáticos debe aparecer en un ámbito de espacio de nombres que incluya la definición de clase del miembro. "

$ 9.4.2/4- "Si un miembro de datos estático es de tipo const integral o const enumeration, su declaración en la definición de clase puede especificar un constante-inicializador que será una expresión de constante integral (5.19). caso, el miembro puede aparecer en expresiones constantes integrales. El miembro aún se definirá en un ámbito de espacio de nombres si se usa en el programa y la definición del alcance del espacio de nombres no contendrá un inicializador ".

class Foo { 
public: 
static const int kType = 42; 
}; 

int const Foo::kType; 

void Func() { 
Foo *bar = NULL; 
int x = bar->kType; 
putc(x, stderr); 
} 

Entonces, una razón más para UB en el código OP.

1

Incluso si funcionó, es un código horrible.

En la programación seria, usted codifica no solo para usted, sino también para otros que mantendrán su código. Debes evitar hacer trucos como este, porque respetas a tus colegas.

Una consecuencia de este código: si el puntero es NULL o no, incluso no está en cuestión, pero implica que este miembro kType puede no ser un miembro simple no estático de la clase. A veces las clases son grandes (esto también es malo) y uno no siempre puede volver a verificar la definición de cada variable.

Sea riguroso. Y llamar a todos sus miembros estáticos Sólo de esta manera:

Foo::kType 

Otra posibilidad es seguir una convención de codificación que hizo saber que el miembro es estático, por ejemplo, un prefijo s_ para todas las clases de miembros estáticos:

Foo::s_kType 
-1

Existe una regla más alta, por así decirlo, que básicamente dice: ni siquiera pienses en compilar cosas que probablemente no se utilicen. La programación avanzada de plantillas depende de esto mucho, por lo que incluso si es un poco gris-zonish cuando un compilador ve claramente que el resultado de una construcción no se utiliza, simplemente lo eliminará. Especialmente cuando es probablemente seguro como en este caso.

Puede querer probar algunas variantes si lo desea, como hacer un puntero a una función, resultado de una función, dejar un puntero sin inicializar (la mejor oportunidad para desencadenar la queja del compilador), haciendo un molde directo de 0 (mejor oportunidad de ser libre de plagas).

+0

Eso es tonto. Todo tipo de optimizaciones podrían hacer que su llamada a la función no suceda, u otras cosas locas (http://blogs.msdn.com/b/oldnewthing/archive/2014/06/27/10537746.aspx). Lo digo en serio. Esto puede suceder en la práctica. –

Cuestiones relacionadas