2009-06-06 11 views
6

Ce code illustre quelque chose que je pense, devrait être traitée comme une mauvaise pratique, et obtenir des mises en garde d'un compilateur de redéfinir ou de masquer une variable:En C++, quand deux variables du même nom peuvent-elles être visibles dans la même portée?

#include <iostream> 

int *a; 

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

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

Sa sortie (compilé avec g ++):

0 
0x602010 

J'ai regardé quelques références (Stroustrup et The Complete C++ Reference) et je ne trouve rien à propos de quand et pourquoi cela est autorisé. Je sais que ce n'est pas dans une portée locale unique, cependant.

Quand et pourquoi est-ce autorisé? Y a-t-il un bon usage pour cette construction? Comment puis-je obtenir g ++ pour m'en prévenir? Est-ce que d'autres compilateurs crient à ce sujet?

Répondre

6

Vous pouvez ignorer la substitution d'identificateurs globaux en toute sécurité. Essentiellement, vous devez seulement vous préoccuper des noms globaux que vous utilisez réellement. Supposons que, dans votre exemple, f() ait été défini en premier. Ensuite, un autre développeur a ajouté la déclaration globale. En ajoutant un nom, f() qui fonctionnait, fonctionne toujours. Si overriding était une erreur, alors la fonction cesserait soudainement de fonctionner, même si elle ne fait rien du tout avec la variable globale nouvellement ajoutée.

15

Pourquoi cela est-il autorisé: cela est parfaitement valable.

Lorsque vous êtes dans votre fonction f(), vous définissez une portée locale. Les étendues locales remplacent la portée globale, de sorte que la définition de votre variable "a" masque "le global" int *a;

4

De nombreuses langues permettent ce genre de choses.
Habituellement (par rapport à toutes les langues), la variable la plus définie localement est celle que vous mentionnez également. Parmi les 20 + langues que j'ai utilisé c'est très commun.

La plupart des langages vous permettent également de vous référer explicitement à celui de la portée externe.
Par exemple C++ vous permet de spécifier la variable dans la portée globale avec l'opérateur ::.

#include <iostream> 


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

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

"Toutes les langues permettent ce genre de chose". Le langage de programmation de ma calculatrice TI-85 ne le fait pas. Toutes les variables sont globales et aucun nom ne se cache. –

+0

.NET (VB, C#) ne permet pas cela aussi! – Dario

+1

L'interdiction de l'ombrage de C# m'exaspère à n'en plus finir lorsque j'utilise des fonctions d'ordre supérieur. –

9

Ceci est parfaitement valable, mais je pense qu'avec -Wall vous obtenez seulement un avertissement lorsque vous l'ombre d'un paramètre.

Si vous voulez des avertissements lorsque vous ombre tout type de variable, vous pouvez utiliser, à partir de la page de manuel g++:

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

Notez que -Wshadow ne sont pas inclus dans -Wall par défaut.

0

Comme d'autres l'ont mentionné, ceci est parfaitement légal, et est sans ambiguïté pour le compilateur. Cependant, c'est l'une des nombreuses fonctionnalités dans les langages de programmation qui peut causer de la confusion ou des bogues difficiles à trouver. Puisqu'il serait trivial de donner des noms différents à chacune de ces variables, par souci de clarté, je suggérerais toujours de le faire.

1

Pour répondre lorsque cela est autorisé: essentiellement dans deux étendues imbriquées.

Par exemple:

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. 

Maintenant, la réponse doit être clairement que d'avoir deux « choses » avec un seul nom dans le même champ d'application est généralement admise. Ceci est possible car au plus l'un des noms est en réalité défini dans cette portée; d'autres seraient simplement visibles dans cette portée. Les règles de résolution de nom déterminent le nom choisi, s'il y a plusieurs candidats.

Vous ne voulez vraiment pas donner d'avertissement pour chaque cas où le compilateur choisit entre des alternatives. Cela vous donnera des tonnes de warnigns, sur des choses aussi innocentes que la surcharge et un certain code modèle intelligent.

Questions connexes