| by Arround The Web | No comments

Scope in C++

An entity in C++ has a name, which can be declared and/or defined. A declaration is a definition, but a definition is not necessarily a declaration. A definition allocates memory for the named entity, but a declaration may or may not allocate memory for the named entity. A declarative region is the largest part of a program in which the name of an entity (variable) is valid. That region is called a scope or a potential scope. This article explains scoping in C++. Furthermore, basic knowledge of C++ is required to understand this article.

Article Content

Declarative Region and Scope

A declarative region is the largest part of a program text in which the name of an entity is valid. The region where the unqualified name can be used (seen) to refer to the same entity. Consider the following short program:

#include <iostream>

using namespace std;

void fn()

{

int var = 3;

if (1==1)

{

cout<<var<<'\n';

}

}

int main()

{

fn();

return 0;

}

 

The function fn() has two blocks: an inner block for the if-condition and an outer block for the function body. The identifier, var, is introduced and seen in the outer block. It is also seen in the inner block, with the cout statement. The outer and inner blocks are the scope for the name, var.

However, the name, var, can still be used to declare a different entity, such as a float, in the inner block. The following code illustrates this:

#include <iostream>

using namespace std;

void fn()

{

int var = 3;

if (1==1)

{

float var = 7.5;

cout<<var<<'\n';

}

}

int main()

{

fn();

return 0;

}

 

The output is

7.5

 

In this case, the name, var, can no longer be used in the inner block to refer to the integer of value 3, which was introduced (declared) in the outer block. Such inner blocks are referred to as potential scope for entities declared in the outer block.

Note: An entity of the same type, like the outer block, can still be declared in the inner block. However, in this case, the new declaration and its meaning are valid in the inner block, while the old declaration and meaning outside the inner block remain valid only in the outer block.

A declaration of the same name in an inner block normally overrides the declaration of the same name outside that inner block. Inner blocks can nest other inner blocks.

Global Scope

When a programmer starts typing a file, that is the global scope. The following short program illustrates this:

#include <iostream>

using namespace std;

float var = 9.4;

int main()

{

cout <<var<<'\n';

cout <<::var<<'\n';

return 0;

}

 

The output is:

9.4

9.4

 

In this case, the declarative region or scope for var begins from the point of declaration for var, and continues downward until the end of the file (translation unit).

The block of the main() function is a different scope; it is a nested scope for the global scope. To access an entity of the global scope from a different scope, the identifier is used directly or preceded by the scope resolution operator, :: .

Note: The entity, main(), is also declared in the global scope.

Block Scope

The if, while, do, for, or switch statements can each define a block. Such a statement is a compound statement. The name of a variable declared in a block has a block’s scope. Its scope begins at its point of declaration and ends at the end of its block. The following short program illustrates this for the variable, ident:

#include <iostream>

using namespace std;

int main()

{

if (1==1)

{

/*some statements*/

int ident = 5;

cout<<ident<<’\n’;

/*some statements*/

}

return 0;

}

 

A variable, such as ident, declared at block scope is a local variable.

A variable declared outside the block scope and above it can be seen in the block’s header (e.g., condition for if-block) and within the block. The following short program illustrates this for the variable, identif:

#include <iostream>

using namespace std;

int main()

{

int identif = 8;

if (identif == 8)

{

cout<<identif<<'\n';

}

return 0;

}

 

The output is,

8

 

There are two block scopes: the block for the main() function and the nested if-compound statement. The nested block is the potential scope of the main() function block.

A declaration introduced in a block scope cannot be seen outside the block. The following short program, which does not compile, illustrates this with the variable, variab:

#include <iostream>

using namespace std;

int main()

{

if (1 == 1)

{

int variab = 15;

}

cout<<variab<<'\n';    //error: accessed outside its scope.

return 0;

}

 

The compiler produces an error message for variab.

An entity introduced, declared in the header of a compound function, cannot be seen outside (below) the compound statement. The following for-loop code will not compile, resulting in an error message:

#include <iostream>

using namespace std;

int main()

{

for (int i=0; i<4; ++i)

{

cout<<i<<' ';

}

cout<<i<<' ';

return 0;

}

 

The iteration variable, i, is seen inside the for-loop block but not outside the for-loop block.

Function Scope

A function parameter is seen in the function block. An entity declared in a function block is seen from the point of declaration to the end of the function block. The following short program illustrates this:

#include <iostream>

#include <string>

using namespace std;

string fn(string str)

{

char stri[] = "bananas";

/*other statements*/

string totalStr = str + stri;

return totalStr;

}

int main()

{

string totStr = fn("eating ");

cout<<totStr<<'\n';

return 0;

}

 

The output is:

eating bananas

 

Note: An entity declared outside the function (above it) can be seen in the function parameter list and the function block.

Label

The scope of a label is the function in which it appears. The following code illustrates this:

#include <iostream>

using namespace std;

void fn()

{

goto labl;

/*other statements*/

labl: int inte = 2;

cout<<inte<<'\n';

}

int main()

{

fn();

return 0;

}

 

The output is

2

 

Class Scope

With normal scoping, the declarative region begins from one point in the program, continues, and stops at a different point. The scope exists in one continuous region. With the class, the scope of an entity can be in different regions that are not joined together. The rules for nested blocks still apply. The following program illustrates this:

#include <iostream>

using namespace std;

//Base class

class Cla

{

private:

int memP = 5;

protected:

int memPro = 9;

public:

void fn()

{

cout<<memP<<'\n';

}

};

//Derived Class

class DerCla: public Cla

{

public:

int derMem = memPro;

};

int main()

{

Cla obj;

obj.fn();

DerCla derObj;

cout<<derObj.derMem<<'\n';

return 0;

}

 

The output is:

5

9

 

In the class Cla, the variable memP, is seen at the point of declaration. After that, the short portion of “protected” is skipped and seen again in the class member function block. The derived class is skipped and seen again at the main() function scope (block).

In the class Cla, the variable, memPro, is seen at the point of declaration. The portion of the public function fn() is skipped, and then seen in the derived class description block. It is seen again down in the main() function.

Scope Resolution Operator

The scope resolution operator in C++ is :: . It is used to access a static member of the class. The following program illustrates this:

#include <iostream>

using namespace std;

class Cla

{

public:

static int const mem = 5;

public:

static void fn()

{

cout<<mem<<'\n';

}

};

int main()

{

cout<<Cla::mem<<'\n';

Cla::fn();

return 0;

}

 

The output is:

5

5

 

The static members are seen in the main() function block, accessed using the scope resolution operator.

Enumeration Scope

Unscoped Enumeration

Consider the following if-block:

if (1==1)

{

enum {a, b, c=b+2};

cout<<a<<' '<<b<<' '<<c<<'\n';

}

 

The output is:

0 1 3

 

The first line in the block is an enumeration; a, b, and c are its enumerators. The scope of an enumerator begins from the point of its declaration, to the end of the enclosing block of the enumeration.

The following statement will not compile because the point of declaration of c is after that of a:

enum {a=c+2, b, c};

 

The following code segment will not compile because the enumerators are accessed after the enclosing block of the enumeration:

if (1==1)

{

enum {a, b, c=b+2};

}

cout<<a<<' '<<b<<' '<<c<<'\n'//error: out of scope

 

The previous enumeration is described as an unscoped enumeration, and its enumerators are defined as unscoped enumerators. This is because it begins only with the reserved word, enum. Enumerations that begin with enum class or enum struct are described as scoped enumerations. Their enumerators are defined as scoped enumerators.

Scoped Enumeration

The following statement is OK:

enum class nam {a, b, c=b+2};

 

This is an example of a scoped enumeration. The name of the class is nam. Here, the scope of the enumerator begins from the point of declaration to the end of the enumeration definition and not the end of the enclosing block for the enumeration. The following code will not compile:

if (1==1)

{

enum class nam {a, b, c=b+2};

cout<<a<<' '<<b<<' '<<c<<'\n';   //error: out of scope for enum class or enum struct

}

 

Simple Usage of Scoped Enumeration

The following code shows a simple way of using scoped enumeration:

if (1==1)

{

enum nam {a, b, c=b+2};

cout << c << endl;   //simple usage

}

 

The output is 3, since b is 1 (‘a’ is 0).

Global Variable

A global variable is a variable declared in the global scope.  When a programmer starts typing a file, that is the global scope. Consider the following program:

#include <iostream>

using namespace std;

char va = ‘A’;

int main()

{

cout <<va<<'\n';

cout <<::va<<'\n';

return 0;

}

 

The output is:

A

A

 

In this program, the portion or scope for va starts from the point of declaration of va and continues downward until the end of the translation unit (file).

The main() function’s body is a different scope in its own right; the global scope nests the main() function scope.

The variable va is a global scope variable or simply global variable because it can be seen everywhere in the file, beginning from where it has been declared. It can be seen in the main() function scope.

To access a global variable (variable of the global scope) from a different scope, the variable name is used directly or preceded by the scope resolution operator,:: as shown in the above program.

The if, while, do, for, or switch statement can each define a block. Such a statement is a compound statement. The name of a variable declared in a block has a block’s scope. Its scope begins at its point of declaration and ends at the end of its block. The following short program illustrates this for the variable, ident:

#include <iostream>

using namespace std;

int main()

{

if (1==1)

{

/*some statements*/

int ident = 5;

cout<<ident<<'\n';

/*some statements*/

}

return 0;

}

 

A variable, such as ident, declared at block scope is a local variable.

A variable declared outside the block scope and above it can be seen in the block’s header (e.g., condition for if-block) and within the block. The following short program illustrates this for the variable, identif:

#include <iostream>

using namespace std;

int main()

{

int identif = 8;

if (identif == 8)

{

cout<<identif<<'\n';

}

return 0;

}

 

The output is,

8

 

There are two block scopes here: the block for the main() function and the nested if-compound statement. The nested block is the potential scope of the main() function block.

A declaration introduced in a block scope cannot be seen outside the block. The following short program, which does not compile, illustrates this with the variable, variab:

#include <iostream>

using namespace std;

int main()

{

if (1 == 1)

{

int variab = 15;

}

cout<<variab<<'\n';    //error: accessed outside its scope.

return 0;

}

 

The compiler produces an error message for variab.

An entity introduced, declared in the header of a compound function, cannot be seen outside (below) the compound statement. The following for-loop code will not compile, resulting in an error message:

#include <iostream>

using namespace std;

int main()

{

for (int i=0; i<4; ++i)

{

cout<<i<<' ';

}

cout<<i<<' ';

return 0;

}

 

The iteration variable, i, is seen inside the for-loop block but not outside the for-loop block.

A variable declared in the global scope is a global variable. It can be seen inside a block scope. The following program illustrates this:

#include <iostream>

using namespace std;

int glb = 5;

int main()

{

cout << glb << endl;

return 0;

}

 

The output is 5.

Reassignment Not Permitted in Global Scope

In the previous program, the statement:

int glb = 5;

 

It is a declaration with initialization (first assignment of value) simultaneously. In the following program, the statement “int glb;” has zero assigned to it by default. The line after (glb = 5;) tries reassignment of the value, 5, in the global scope. This creates an error, and the program does not compile. The program is:

#include <iostream>

using namespace std;

int glb;

glb = 5;

int main()

{

cout << glb << endl;

return 0;

}

 

However, reassignment will take place in a local (block) scope, as shown in the following program:

#include <iostream>

using namespace std;

int glb;

int main()

{

if (1 == 1) {

glb = 5;

cout << glb << endl;

}

return 0;

}

 

The output is 5.

Global Pointer

When a pointer is declared in the global scope, it can be seen in a nested scope. A pointer declared in a nested scope cannot be seen outside the nested scope. There are two ways of declaring a pointer. A pointer can be declared with a practical assignment (of an address). A pointer can be declared without a practical assignment. If the pointer is declared without practical assignment in the global scope, then it cannot be reassigned in the global scope. However, in this case, it can be reassigned in a nested scope.

Global Array

When an array is declared in the global scope, it can be seen in a nested scope. An array declared in a nested scope, cannot be seen outside the nested scope. There are two ways of declaring an array. An array can be declared with a practical assignment (of an address). An array can be declared without practical assignment. If the array is declared without practical assignment in the global scope, then it cannot be reassigned in the global scope. However, it can be reassigned in a nested scope (element by element).

Template Parameter Scope

The normal scope of a template parameter name begins from the point of declaration to the end of its block, as in the following example:

template<typename T, typename U>  struct Ages

{

T John = 11;

U Peter  = 12.3;

T Mary  = 13;

U Joy   = 14.6;

};

 

U and T are seen within the block.

For a template function prototype, the scope begins from the point of declaration to the end of the function parameter list, as in the following statement:

template<typename T, typename U> void func (T no, U cha, const char *str );

 

However, when it comes to the class description (definition), the scope can also be of different portions, as in the following example program:

#include <iostream>

using namespace std;

template<class T, class U> class TheCla

{

public:

T num;

static U ch;

void func (U cha, const char *str)

{

cout << "There are " << num << " books worth " << cha << str << " in the store." << '\n';

}

static void fun (U ch)

{

if (ch == 'a')

cout << "Official static member function" << '\n';

}

};

int main()

{

TheCla<int, char> obj;

obj.num = 12;

obj.func('$', "500");

 

return 0;

}

 

Name Hiding

An example of name hiding occurs when the name of the same object type is re-declared in a nested block. The following program illustrates this:

#include <iostream>

using namespace std;

void fn()

{

int var = 3;

if (1==1)

{

int var = 4;

cout<<var<<'\n';

}

cout<<var<<'\n';

}

int main()

{

fn();

return 0;

}

 

The output is:

4

3

 

It’s because var in the nested block hid var in the outer block.

Possibility for Repeating Declaration in the Same Scope

The point of the declaration is where the name is introduced (for the first time) in its scope.

Function Prototype

Different entities, even of different types, cannot normally be declared in the same scope. However, a function prototype can be declared more than once in the same scope. The following program with two function prototypes and corresponding function definition illustrates this:

#include <iostream>

using namespace std;

void fn(int num);

void fn(int num);

void fn(int num)

{

cout<<num<<'\n';

}

int main()

{

fn(5);

return 0;

}

 

The program works.

Overloaded Functions

Overloaded functions are functions with the same name but with different function signatures. As another exception, overloaded functions with the same name can be defined in the same scope. The following program illustrates this:

#include <iostream>

using namespace std;

void fn(int num)

{

cout<<num<<'\n';

}

void fn(float no)

{

cout<<no<<'\n';

}

int main()

{

fn(5);

float flt = 8.7;

fn(flt);

return 0;

}

 

The output is:

5

8.7

 

The overloaded functions have been defined in the global scope.

Namespace Scope

Since two different variables can have the same name and to differentiate them, the variables have to be declared in two different namespaces. A namespace is a block with a name, having its own named entities. The name of a namespace is the name of the block. Two different namespaces can have entities with the same names. The following program illustrates this:

#include <iostream>

using namespace std;

namespace NA

{

char ch1;

char ch2 = 'Q';

}

namespace NB

{

char ch1;

char ch2 = 'Y';

}

int main()

{

NA::ch1 = 'P';

cout << NA::ch1 << endl;

NB::ch1 = 'X';

cout << NB::ch1 << endl;

return 0;

}

 

The output is:

P

X

 

There are two namespaces: NA and NB. The predefined space for NA is its block. The predefined space for NB is also its block. Note that the entities in the two different blocks have the same names. To declare a namespace, begin with the reserved word, namespace; a space follows, then the block with its entities.

To access an entity outside the namespace block, begin with the name of the namespace. This is followed by the scope resolution operator and then the name of the entity.

The scope of the variable, NA::ch1, begins from the point of declaration of ch1 in the NA block. It ends at the end of the NA block and then appears in any line outside (and below) the block, where “NA::” is used. The scope of the variable, NB::ch1, begins from the point of declaration of ch1 in the NB block. It ends at the end of the NB block and appears in any line outside (and below) the block, where “NB::” is used.

A namespace scope refers to an entity declared in the namespace block. The scope of the namespace entity is the namespace block and any line outside the block that uses the namespace name.

Scope in Different Portions

The previous class and namespace scopes have illustrated how a scope can be in different portions of the program text. Another good example is the friend specifier.

Friend Specifier

If class C is declared as a friend to class A, in class A definition, then all the public member functions of class C can access the private data members (and private member functions) of class A. The following program illustrates this:

#include <iostream>

using namespace std;

class A {

private: int d = 5;

friend class C;

};

class B {

/*

public: int fB() {

A a;

return a.d;

}

*/

};

class C {

public: int fC() {

A a;

return a.d;

}

};

int main()

{

A a;

B b;

C c;

//int ret1 = b.fB();

int ret2 = c.fC();

cout << ret2 << endl;

return 0;

}

 

The output is 5. In the definition of class A, class C is declared as a friend to class A. So, fC() of class C definition, can see d of class A definition. The program compiles. If the comment symbols are removed, then the program will not compile because class B has not been declared a friend to class A.

The scope of the private variable b begins from its point of declaration and ends at the end of the class A block. It also appears in the fC() function in class C, which is a friend to class A. The two portions for the scope are the class A block and the fC() function block.

Conclusion

A scope is a declarative region. A declarative region is the largest part of a program text in which the name of an entity is valid. It can be divided into more than one portion with certain programming schemes, such as nested blocks. The portions that do not have the declaration point form the potential scope. The potential scope may or may not have the declaration.

Share Button

Source: linuxhint.com

Leave a Reply