Friday, August 19, 2011

Scope of Identifiers in C++

Every identifier has a scope that is determined by where it is declared. Identifier Scope in a program is the region(s) of the program within which the identifier can be used. Using a name outside of its scope is an error. The same name may declared/used in different scopes.Ambiguities are resolved as follows:
  1. The name from the most local scope is used.
  2. If the name is not defined in the most local scope, the same name defined in the nearest enclosing scope will be used.
  3. If the name is not defined in any enclosing scope, then the compiler will report an error.

There are five possible scopes in C++.
  1. Block scope (local to a block of statements)
  2. Function scope (the entire extent of a function)1
  3. Class scope (the entire extent of a class, including its member functions)
  4. File scope (the entire extent of a source code file)
  5. Global scope (the entire extent of a program)

Because the compiler only deals with one source file at a time, only the Linker can tell the difference between global and file scope, as Example below shows.

/*
 * In File 1:
 */
int g1;         // global
int g2;         // global
static int g3;  // keyword static limits g3 to file scope (etc.)

/*
 * In File 2:
 */
int g1;        // linker error!
extern int g2; // okay, share variable space
static int g3; // okay: 2 different variable spaces (etc.)

Default Scope of Identifiers: A Summary

Let’s look at the five scopes in a bit more detail.
  1. Block scope: An identifier declared inside curly braces (excluding namespace blocks) { . . . } or in a function parameter list has block scope. Block scope extends from the declaration to the enclosing right brace.
  2. Function scope: Labels in C/C++ have their own scope. They are accessible before and after their declaration, for the whole function. C supports a very rarely used and often shunned goto statement that requires a label. The thing that makes its scope unique is that the label (the declaration) can appear after the first goto that uses it. Example below shows an example of its use. goto is seldom used by serious programmers but labels, because they exist, sometimes pop up in code and are used to solve various compatibility problems. NOTE: Avoid goto and labels in your code.
  3. Class scope: An identifier that is declared inside a class definition has class scope. Class scope is anywhere in the class definition3 or in the bodies of member functions.
  4. File scope: This is basically the same as global scope, except that file scope variables are not exported to the linker. The keyword static hides an identifier from other source files and gives it file scope. File scope variables cannot be declared extern and accessed from another file. File scope variables, because they are not exported, do not pollute the global namespace. They are often used in C programs because C has no concept of private class members. NOTE:File scope is available in C++ for backward compatibility with C, but we prefer to use namespaces or static class members instead.
  5. Global scope: An identifier whose declaration is not in between braces (round or curly) has global scope. The scope of such an identifier begins at the declaration and extends from there to the bottom of the source code file. extern declarations may be used to access global definitions in other source files. An identifier in a namespace is available from other scopes through the using keyword. It is also available globally through the use of the scope resolution :: operator.Namespace variables and static class members are accessible globally and have static storage, like global variables, except that they do not enlarge the global namespace. NOTE: Use of global scope for variables is unnecessary in C++. In general, only classes and namespaces should be defined in global scope. If you need a “global” variable, you can achieve something similar through the use of public static class member or a namespace member.
Example: goto.cpp
[ . . . . ]
int look() {
   int i=0;
   for (i=0; i<10; ++i) {
      if (i == rand() % 20) {
         goto found; // It would be better to use break or continue.
      }
   }
   return -1;
   found: // goto serves as a forward declaration to a label.
      return i;
}
[ . . . . ]

File Scope versus Block Scope and operator::

We have seen and used the scope resolution operator to extend the scope of a class or access its members with ClassName::. A similar syntax is used to access the individual symbols in a namespace with NamespaceName::. C++ also has a (unary) file scope resolution operator, ::, that provides access to global, namespace, or file scope objects from inside an enclosed scope. The following exercise deals with the use of this operator with various scopes.

A simple exercise for the readers:

Determine the scope of each of the variables in Example below. Then be the computer and predict the output of the program.
scopex.cpp:

#include <iostream>
using namespace std;

long x = 17;
float y = 7.3;       // Scope: ________________ ?
static int z = 11;   // Scope: ________________ ?

class Thing {
   private:
      int m_Num;           // Scope: ________________ ?
   public:
      static int sm_Count; // Scope: ________________ ?
      Thing(int n = 0) : m_Num(n) {
         ++sm_Count;
      }
      ~Thing() {
         --sm_Count;
      }
      int getNum() {
         return m_Num;
      }
};
int Thing :: sm_Count = 0; 
Thing t (11);


int fn(char c, int x) {    // Scope: ________________ ?
   int z = 5;              // Scope: ________________ ?
   double y = 6.933;
   {
      char y;                 // Scope: ________________ ?
      Thing z(4);             // Scope: ________________ ?
      y = c + 3;
      ::y += 0.3;             // Scope: ________________ ?
      cout << y << endl;      // Scope: ________________ ?
   }
   cout << Thing::sm_Count
   << endl;                // Scope: ________________ ?
   y /= 3.0;               // Scope: ________________ ?
   ::z++;                  // Scope: ________________ ?
   cout << y << endl;
   return (x + z);
}
int main() {
   int x, y = 10;
   char ch = 'B';          // Scope: ________________ ?
   x = fn(ch, y);
   cout << x << endl;
   cout << ::y << endl;    // Scope: ________________ ?
   cout << ::x / 2 << endl;
   cout << ::z << endl;
   return 0;
}

1 comment:

  1. This is such a great resource that you are providing and you give it away for free. I love seeing blog that understand the value of providing a quality resource for free. scope management

    ReplyDelete