12.3. Class ScopeEvery class defines its own new scope and a unique type. The declarations of the class members within the class body introduce the member names into the scope of their class. Two different classes have two different class scopes.
class First {
public:
int memi;
double memd;
};
class Second {
public:
int memi;
double memd;
};
First obj1;
Second obj2 = obj1; // error: obj1 and obj2 have different types
Using a Class MemberOutside the class scope, members may be accessed only through an object or a pointer using member access operators dot or arrow, respectively. The left-hand operand to these operators is a class object or a pointer to a class object, respectively. The member name that follows the operator must be declared in the scope of the associated class: Class obj; // Class is some class type Class *ptr = &obj; // member is a data member of that class ptr->member; // fetches member from the object to which ptr points obj.member; // fetches member from the object named obj // memfcn is a function member of that class ptr->memfcn(); // runs memfcn on the object to which ptr points obj.memfcn(); // runs memfcn on the object named obj Some members are accessed using the member access operators; others are accessed directly from the class using the scope operator, (::). Ordinary data or function members must be accessed through an object. Members that define types, such as Screen::index, are accessed using the scope operator. Scope and Member DefinitionsMember definitions behave as if they are in the scope of the class, even if the member is defined outside the class body. Recall that member definitions that appear outside the class body must indicate the class in which the member appears: double Sales_item::avg_price() const { if (units_sold) return revenue/units_sold; else return 0; } Here we use the fully qualified name Sales_item::avg_price to indicate that the definition is for the avg_price member in the scope of the Sales_item class. Once the fully qualified name of the member is seen, the definition is known to be in class scope. Because the definition is in class scope, we can refer to revenue and units_sold without having to write this->revenue or this->units_sold. Parameter Lists and Function Bodies Are in Class ScopeIn a member function defined outside the class, the parameter list and member-function body both appear after the member name. These are defined inside the class scope and so may refer to other class members without qualificationfor example, the definition of the two-parameter version of get in class Screen: char Screen::get(index r, index c) const { index row = r * width; // compute the row location return contents[row + c]; // offset by c to fetch specified character } This function uses the type name index defined inside Screen to name the types of its parameters. Because the parameter list is inside the scope of class Screen, there is no need to specify that we want Screen::index. It is implicit that the one we want is the one defined in the current class scope. Similarly, the uses of index, width, and contents all refer to names declared inside class Screen. Function Return Types Aren't Always in Class ScopeIn contrast to the parameter types, the return type appears before the member name. If the function is defined outside the class body, then the name used for the return type is outside the class scope. If the return type uses a type defined by the class, it must use the fully qualified name. For example, consider the get_cursor function: class Screen { public: typedef std::string::size_type index; index get_cursor() const; }; inline Screen::index Screen::get_cursor() const { return cursor; } The return type of this function is index, which is a type name defined inside the Screen class. If we define get_cursor outside the class body, the code is not in the class scope until the function name has been processed. When the return type is seen, its name is used outside of the class scope. We must use the fully qualified type name, Screen::index to specify that we want the name index that is defined inside class Screen.
12.3.1. Name Lookup in Class ScopeIn the programs we've written so far, name lookup (the process of finding which declaration is matched to a given use of a name) has been relatively straightforward:
If no declaration is found, then the program is in error. In C++ programs, all names must be declared before they are used. Class scopes may seem to behave a bit differently, but in reality they obey this same rule. Confusion can arise due to the way names are resolved inside a function defined within the class body itself.
Of course, the names used in class scope do not always have to be class member names. Name lookup in class scope finds names declared in other scopes as well. During name lookup, if a name used in class scope does not resolve to a class member name, the scopes surrounding the class or member definition are searched to find a declaration for the name. Name Lookup for Class Member DeclarationsNames used in the declarations of a class member are resolved as follows:
typedef double Money; class Account { public: Money balance() { return bal; } private: Money bal; // ... }; When processing the declaration of the balance function, the compiler first looks for a declaration of Money in the scope of the class Account. The compiler considers only declarations that appear before the use of Money. Because no member declaration is found, the compiler then looks for a declaration of Money in global scope. Only the declarations located before the definition of the class Account are considered. The declaration for the global typedef Money is found and is used for the return type of the function balance and the data member bal.
The compiler handles member declarations in the order in which they appear in the class. As usual, a name must be defined before it can be used. Moreover, once a name has been used as the name of a type, that name may not be redefined: typedef double Money; class Account { public: Money balance() { return bal; } // uses global definition of Money private: // error: cannot change meaning of Money typedef long double Money; Money bal; // ... }; Name Lookup in Class Member DefinitionsA name used in the body of a member function is resolved as follows:
Class Members Follow Normal Block-Scope Name Lookup
The following function uses the same name for a parameter and a member, which normally should be avoided. We do so here to show how names are resolved: // Note: This code is for illustration purposes only and reflects bad practice // It is a bad idea to use the same name for a parameter and a member int height; class Screen { public: void dummy_fcn(index height) { cursor = width * height; // which height? The parameter } private: index cursor; index height, width; }; When looking for a declaration for the name height used in the definition of dummy_fcn, the compiler first looks in the local scope of that function. A function parameter is declared in the local scope of its function. The name height used in the body of dummy_fcn refers to this parameter declaration. In this case, the height parameter hides the member named height.
If we wanted to override the normal lookup rules, we could do so: // bad practice: Names local to member functions shouldn't hide member names void dummy_fcn(index height) { cursor = width * this->height; // member height // alternative way to indicate the member cursor = width * Screen::height; // member height } After Function Scope, Look in Class ScopeIf we wanted to use the member named height, a much better way to do so would be to give the parameter a different name: // good practice: Don't use member name for a parameter or other local variable void dummy_fcn(index ht) { cursor = width * height; // member height } Now when the compiler looks for the name height, it will not find that name in the function. The compiler next looks in the Screen class. Because height is used inside a member function, the compiler looks at all the member declarations. Even though the declaration of height appears after its use inside dummy_fcn, the compiler resolves this use to the data member named height. After Class Scope, Look in the Surrounding ScopeIf the compiler doesn't find the name in function or class scope, it looks for the name in the surrounding scope. In our example, declarations in global scope that appear before the definition of the Screen include a global object named height. However, that object is hidden.
// bad practice: Don't hide names that are needed from surrounding scopes void dummy_fcn(index height) { cursor = width * ::height;// which height? The global one } Names Are Resolved Where They Appear within the FileWhen a member is defined outside the class definition, the third step of name lookup not only considers the declarations in global scope that appear before the definition of class Screen, but also considers the global scope declarations that appear before the member function definitionfor example: class Screen { public: // ... void setHeight(index); private: index height; }; Screen::index verify(Screen::index); void Screen::setHeight(index var) { // var: refers to the parameter // height: refers to the class member // verify: refers to the global function height = verify(var); } Notice that the declaration of the global function verify is not visible before the definition of the class Screen. However, the third step of name lookup considers the surrounding scope declarations that appear before the member definition, and the declaration for the global function verify is found. ![]() |