3.16 NESTED TYPES


3.16 NESTED TYPES

If a class is expected to play a role that is completely subsidiary to another class, it is best to define the former as a member of the latter. A class is called a nested class if it is defined as a member of another class, the latter usually referred to as the enclosing class. In C++, one can also nest a typedef inside a class. In Java, one can also nest an interface inside another interface or another class, resulting in nested interfaces. It is also possible in Java to nest a class inside an interface.

3.16.1 Nested Classes in C++

A nested class can be defined in the private, public, or protected section of the enclosing class. The usual meaning ascribed to these access control modifiers applies.

In the following example, X in line (A) is the enclosing class for a nested class Y defined in line (B). The enclosing class is also provided with a data member of type Y* in line (E). Note that the definition of the nested class starting in line (B) is like any other class definition. It has a data member, a constructor, and a member function. Line (F) provides a constructor for the enclosing class and line (G) a get function for its sole data member.

 
//NestedClass.cc #include <iostream> using namespace std; class X { //(A) class Y{ //(B) int m; public: Y( int mm ) { m = mm; } //(C) void printY() cout << "m of nested class object: " << m << endl; }; //(D) }; Y* yptr; //(E) public: X() { yptr = new Y( 100 ); } //(F) Y* get_yptr() { return yptr; } //(G) }; int main() { X x; //(H) x.get_yptr()->printY(); //(I) return 0; }

When you run this program, the statement in line (H) will invoke the no-arg constructor of line (F). The invocation in line (I) will call on the print function in line (D) to output

      m of nested class: 100 

In the example above, all the member functions were defined inline for both the enclosing class and the nested class. Shown below is the same program as above, but written with all the member function definitions outside the classes. The class scope operator '::', introduced in Section 3.1, must now be used as shown to indicate the class for which a definition is being provided.

 
//NestedClassDefsNotInline.cc #include <iostream> using namespace std; class X { //(A) class Y{ //(B) int m; public: Y( int mm ); //(C) void printY(); //(D) }; Y* yptr; //(E) public: X(); //(F) Y* get_yptr(); //(G) }; //Definitions specific to the enclosing class X: X::X() { yptr = new Y( 100 ); } //(H) X::Y* X::get_yptr(){ return yptr; } //(I) //Definitions specific to the nested class Y: X::Y::Y( int mm ) { m = mm; } //(J) void X::Y::printY(){ cout << "m of nested class object: " << m << endl; } //(K) int main() { X x; x.get_yptr()->printY(); return 0; }

Note how in line (I) the return type of the function get_yptr() of class X is declared to be X::Y*. Also note how in line (J) the constructor for the nested class Y is accessed via X::Y::Y ( int mm ).[17] Even the definition of a nested class can be taken outside the outermost enclosing class. So the code in lines (A) through (G) in the previous program can be replaced by

      class X {           class Y;           Y* yptr;      public:           X();           Y* get_yptr();      };      class X::Y {      int m;      public:           Y( int mm );           void printY();      }; 

Here are some important facts to bear in mind about the relationship between an enclosing class and a nested class:

  • An enclosing class cannot directly access the private members of a nested class. The definition of the enclosing class must include a "friend" declaration for the nested class if the former is to have direct access to the private members of the latter.

  • A nested class cannot directly access the private data members of the enclosing class. The definition for the nested class must include a "friend" declaration for the enclosing class if the former is to have direct access to the private members of the latter.

  • A static member of the enclosing class can be accessed inside a nested class without the intermediary of an object of the enclosing class. (This is to be expected since a static member is global with respect to all the objects of a class.)

  • A nonstatic member of the enclosing class can be accessed inside a nested class only through the intermediary of an object of the enclosing type. (This is again to be expected, since a nonstatic class member can only exist on a per-object basis.)

The last two points made above are illustrated by the following program in which the enclosing class X has two data members, one nonstatic and one static, in lines (A) and (B), respectively. The nested class accesses these in lines (C) and (D). The nonstatic data member of the enclosing class is accessed through a pointer to an object of the enclosing class type in line (C). On the other hand, the static member of the enclosing class, initialized in line (F), is accessed directly in line (D).

 
//EnclosingClassAccess.cc #include <iostream> class X { public: int regularIntEnclosing; //(A) static int staticIntEnclosing; //(B) class Y{ public: int m; int n; Y( X* xptr ) { m = xptr->regularIntEnclosing; //(C) n = staticIntEnclosing; //(D) } }; X( int n ) { regularIntEnclosing = n; } //(E) }; int X::staticIntEnclosing = 300; //(F) int main() { X* xptr = new X( 100 ); X::Y y( xptr ); return 0; }

For the case of C++, it is also possible (and common) to nest type definitions created by typedef and enumerations inside an enclosing class, as in lines (A) and (B) in the example below:

 
//NestedTypes.cc class X {}; class Y { public: typedef X Z; //(A) enum Weight { light, medium, heavy }; //(B) private: Z zobj; Weight weight; }; int main() { // Z zobj; // error Y:: Z zobj; // ok // Weight w = medium; // error // Y::Weight w = medium; // error Y::Weight w = Y::medium; // ok return 0; }

Note the fact that the type Z and the enum Weight are not directly available in the global namespace in main. However, if needed, Z is available in main through the class Y via the scope operator. The same is true for the enumeration.

3.16.2 Nested Classes in Java

What a nested class does for C++ is achieved by a static nested class in Java.[18] Here is the Java equivalent of the C++ nested classes program NestedClass.cc shown in the preceding subsection:

 
//NestedClass.java class X { //(A) static class Y{ //(B) private int m; public Y( int mm ) { m = mm; } public void printY(){ System. out.println( "m of nested class object: " + m ); } } private Y yref; public X() { yref = new Y( 100 ); } Y get_yref(){ return yref; } } class Test { public static void main( String[] args ) { X x = new X(); x.get_yref().printY(); // m of nested class object: 100 } }

The class X contains a nested type defined by the class Y in line (B). The definition of class Y is like that of any other class in Java; it is allowed to have its own constructors, data members and methods, with different access privileges, if so desired. Since a static nested class in Java creates a type with respect to the enclosing class, it can only be accessed via the enclosing class. To illustrate this fact, in the following version of the above program, the nested class Y is in the public section of the enclosing class X. We can use it like any other class to declare variables provided we access it via the X. Y notation, as shown below:

 
//NestedClassAsType.java class X { public static class Y{ //(A) private int m; public Y( int mm ) { m = mm; } public void printY(){ System.out.println( "m of nested class obj: " + m ); } } private Y yref; public X() { yref = new Y( 100 ); } Y get_yref(){ return yref; } } class Test { public static void main( String[] args ) { X x = new X(); x.get_yref().printY(); // m of nested class obj: 100 X.Y y = new X.Y( 200 ); //(B) y.printY(); // m of nested class obj: 200 } }

Note how in line (B) the variable y is declared to be of type X. Y and how the constructor for Y is invoked through the full name of the nested class, X.Y.

The following example parallels (and also illustrates an important difference between C++ and Java) the C++ code of the program EnclosingClassAccess.cc. As with the earlier C++ program EnclosingClassAccess.cc, our enclosing class has a nonstatic data member in line (A) and a static data member in line (B). To access the nonstatic data member in line (C), we must do so through the intermediary of an object of the enclosing type. But, as we show in line (D), the static data member of the enclosing class can be accessed directly.

 
//EnclosingClassAccess.java class X { private int regularIntEnclosing; //(A) private static int staticIntEnclosing = 300; //(B) public static class Y{ private int m; private int n; Y( X xref ) { m = xref.regularIntEnclosing; //(C) n = staticIntEnclosing; //(D) } } public X( int n ) { regularIntEnclosing = n; } //(E) } class Test { public static void main( String[] args ) { X x = new X( 100 ); X.Y y = new X.Y( x ); //(F) } }

With regard to restrictions on the access to the members of the enclosing class, the main difference between the Java code shown above and the C++ code in the equivalent EnclosingClassAccess.cc is that, for the Java case, we kept the data members of the enclosing class private and were still able to access them inside the nested class. If we had made the data members private for the C++ example, we'd need to also make the nested class a friend of the enclosing class for the C++ and the Java programs to remain logically identical. The important point to note about the Java case is that a nested class is like any other member for the enclosing class; the private members of the enclosing class are visible everywhere, even inside nested classes.

Java also permits you define a nonstatic nested class, called an inner class. An inner class is defined on a per object basis with respect to the instances of the enclosing class. In other words, the type corresponding to the inner class will be defined separately for each instance of the enclosing class. For that reason, an inner class can directly access all members of the enclosing class-static as well as nonstatic.

This is illustrated by the following example in which, as before, the enclosing class has a nonstatic and a static data member in lines (A) and (B). Note the nested class is nonstatic, meaning that it is now an inner class. Lines (C) and (D) demonstrate that both the static and the nonstatic members of the enclosing class are directly accessible inside the inner class-without the intermediary of an object of the enclosing type. Since the inner class is public, we should be able to use it outside the enclosing class as a data type and create object of that type. Line (G) demonstrates that to construct an object of the inner type, you have to invoke the operator new on the enclosing object.

 
//InnerClass.java class X { private int regularIntEnclosing; //(A) private static int staticIntEnclosing = 300; //(B) public class Y{ private int m; private int n; public Y() { m = regularIntEnclosing; //(C) n = staticIntEnclosing; //(D) } } public X( int n ) { regularIntEnclosing = n; } //(E) } class Test { public static void main( String[] args ) { X x = new X( 100 ); //(F) // X.Y y = new X.Y(); // error X.Y y = x.new Y(); // ok //(G) } }

If X is the enclosing class for Y, the members of X can also be accessed inside Y by using the X. this prefix. This is illustrated by the following example that is identical to the above program, except that we have used the same names, m and n, for the data members of both the enclosing and the inner classes. Now how the name conflict is avoided in lines (E) and (F) by using appropriate prefixes.

 
//InnerClassThisPrefix.java class X { private int m; //(A) private static int n = 300; //(B) public class Y{ private int m; //(C) private int n; //(D) public Y() { this.m = X.this.m; //(E) this.n = X.this.n; //(F) } public String toString() { return "inner's state: " + this.m + " " + this.n; } } public X( int mm ) { m = mm; } //(G) } class Test { public static void main( String[] args ) { X x = new X( 100 ); X.Y y = x.new Y(); System.out.println( y ); // 100 300 } }

As we mentioned at the beginning of this section, a Java interface can also contain nested interfaces and classes. The following example illustrates this idea. The interface Drawable contains a nested class Color. Any class implementing the interface would have available to it the data type Color.

 
//NestedInterface.java interface Drawable { class Color { private int red; private int green; private int blue; public Color( int r, int g, int b ) { red = r; green = g; blue = b; } } void setColor( Color c ); void draw(); } class Rectangle implements Drawable { private Color color; // Color made available by the interface private int width; private int height; public Rectangle( int w, int h ) { width = w; height = h; } public void setColor( Color c ) { color = c; } public void draw() { System.out.println( "Invoke code for drawing a rectangle" ); } public static void main( String [] args ) { Color col = new Color( 120, 134, 200 ); Rectangle rect = new Rectangle( 23, 34 ); rect.setColor( col ); rect.draw(); } }

When an interface encloses a class, that class is implicitly public and static.[19]

A nested class can have its own nested class and interfaces. If class X encloses class Y and class Y enclosed class Z, the members of X can be accessed inside Z by using the X.this prefix. Finally, an inner class is not allowed to have static members.

[17]When the implementation code for a nested class member in C++ is not provided inside the class definition, it can only be provided outside the outermost enclosing class using the class scope operator in the manner shown.

[18]A nested interface in Java is implicitly a static member of the enclosing type.

[19]Here is a summary of the implicit properties of the various things you can place inside a Java interface: (a) All methods declared inside an interface are implicitly public and nonstatic. (b) All constants in an interface are implicitly public, final, and static. (c) All classes nested inside an interface are implicitly public and static.




Programming With Objects[c] A Comparative Presentation of Object-Oriented Programming With C++ and Java
Programming with Objects: A Comparative Presentation of Object Oriented Programming with C++ and Java
ISBN: 0471268526
EAN: 2147483647
Year: 2005
Pages: 273
Authors: Avinash Kak

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net