2009-06-06 12 views
6

Este código ilustra algo que creo que debe ser tratada como una mala práctica, y obtener advertencias de un compilador de redefinir o enmascarar una variable:En C++, ¿cuándo pueden verse dos variables del mismo nombre en el mismo ámbito?

#include <iostream> 

int *a; 

int* f() 
{ 
    int *a = new int; 
    return a; 
} 

int main() 
{ 
    std::cout << a << std::endl << f() << std::endl; 
    return 0; 
} 

Su salida (compilados con g ++):

0 
0x602010 

He visto un par de referencias (Stroustrup y The Complete C++ Reference) y no puedo encontrar nada sobre cuándo y por qué está permitido. Sin embargo, sé que no está dentro de un ámbito local único.

¿Cuándo y por qué se permite esto? ¿Hay un buen uso para esta construcción? ¿Cómo puedo obtener g ++ para advertirme al respecto? ¿Los demás compiladores graznan al respecto?

Respuesta

6

Está permitido para que pueda ignorar de forma segura el reemplazo de identificador global. Esencialmente, solo debe preocuparse por los nombres globales que realmente usa.

Supongamos, en su ejemplo, que se había definido primero f(). Luego, otro desarrollador agregó la declaración global. Al agregar un nombre, f() que solía funcionar, todavía funciona. Si la anulación fue un error, la función dejará de funcionar repentinamente, aunque no haga nada con la variable global recién agregada.

15

En cuanto a por qué esto está permitido: esto es perfectamente válido.

Cuando se encuentra dentro de su función f(), está definiendo un ámbito local. Los ámbitos locales prevalecen sobre el ámbito global, por lo que la definición de su variable "a" allí "oculta" el mundo int *a;

4

Muchos idiomas permiten este tipo de cosas.
Por lo general (en relación con todos los idiomas) la variable más localmente definida es a la que se refiere también. De los más de 20 idiomas que he usado, esto es muy común.

También la mayoría de los idiomas le permiten consultar explícitamente el que está en el alcance exterior.
Por ejemplo C++ le permite especificar la variable en alcance global con el operador ::.

#include <iostream> 


int a = 5; 
int main() 
{ 
    int a = 6; 

    std::cout << a << "\n" << ::a << "\n"; 
      // Local 
          // global 
} 
+1

"Todos los idiomas permiten este tipo de cosas". El lenguaje de programación en mi calculadora TI-85 no. Todas las variables son globales y no hay ningún nombre escondido. –

+0

.NET (VB, C#) no permite esto también! – Dario

+1

La prohibición de C# de sombrearme me exaspera infinitamente cuando estoy usando funciones de orden superior. –

9

Esto es perfectamente válido, pero creo que con -Wall que sólo recibe una advertencia cuando remedar un parámetro.

Si desea advertencias Al remedar cualquier tipo de variable, puede utilizar este, desde la página de manual de g++:

-Wshadow 
     Warn whenever a local variable shadows another local variable, 
     parameter or global variable or whenever a built-in function is 
     shadowed. 

Tenga en cuenta que -Wshadow no está incluido en -Wall por defecto.

0

Como han mencionado otros, esto es perfectamente legal y no es ambiguo para el compilador.

Sin embargo, es una de las muchas características de los lenguajes de programación que pueden causar confusión o errores difíciles de encontrar. Dado que sería trivial dar diferentes nombres a cada una de estas variables, en aras de la claridad, siempre sugeriría hacerlo.

1

Para responder cuando esto está permitido: básicamente en dos ámbitos anidados.

Por ejemplo:

void foo() { 
    int a; 
    { 
     int a; 
    } 
} 

class Base { 
    int a; 
}; 
class Derived: public Base { 
    int a; // Yes, the name Base::a is visible in the scope of Derived, even if private 
}; 

class Foo() { 
    int a; 
    Foo(int a) : a(a) { } // Works OK 
}; 

using std::swap; 
void swap(MyClass& lhs, MyClass& rhs); 
// Not strictly a variable, but name lookup in C++ happens before determining 
// what the name means. 

Ahora, la respuesta debe ser claramente que tener dos 'cosas' con un único nombre en el mismo ámbito es generalmente permitido. Esto es posible porque como máximo uno de los nombres es realmente definido en ese alcance; otros serían meramente visibles en ese ámbito. Las reglas de resolución de nombres determinan qué nombre se elige, si hay múltiples candidatos.

Realmente no desea dar una advertencia para cada caso donde el compilador elige entre las alternativas. Eso le dará toneladas de advertencias, sobre cosas tan inocentes como la sobrecarga y algún código de plantilla inteligente.

Cuestiones relacionadas