stringa typestring::iterator is available which provides all charactersthat are stored in thestring. Thisstring::iterator type could bedefined as an objectiterator, defined as nested class in the classstring.Since nested classes are defined inside other classes their members, whenprovided with references or pointers to objects of their surrounding classes,may access all members of those objects, even their private members.
A class can be nested in every part of the surrounding class: in thepublic, protected orprivate section. If a class is nested in thepublic section of a class, it is visible outside the surrounding class. Ifit is nested in theprotected section it is visible in subclasses, derivedfrom the surrounding class, if it is nested in theprivate section, it isonly visible for the members of the surrounding class.
The surrounding class has no special privileges with respect to the nestedclass. For example, consider the following class definition:
class Surround { public: class FirstWithin { int d_variable; public: FirstWithin(); int var() const; }; private: class SecondWithin { int d_variable; public: SecondWithin(); int var() const; }; }; inline int Surround::FirstWithin::var() const { return d_variable; } inline int Surround::SecondWithin::var() const { return d_variable; }In the Annotations(), in order to save space, nested class interfaces areusually declared inside their surrounding class, as shown above. Often thiscan be avoided, which is desirable as it more clearly separates the outerclass's interface and the nested class's interface. Likewise, in-class memberimplementations should be avoided. Here is an illustration of how outer- andnested class interfaces can be separated:
class Surround { class SecondWithin; public: class FirstWithin; }; class Surround::FirstWithin { int d_variable; public: FirstWithin(); int var() const; }; class Surround::SecondWithin { int d_variable; public: SecondWithin(); int var() const; };For these three classes access to members is defined as follows:
Surround::FirstWithin is visible outside and insideSurround. The classSurround::FirstWithin thus has global visibility.FirstWithin's constructor and its member functionvar are also globally visible.d_variable is only visible to the members ofthe classSurround::FirstWithin. Neither the members ofSurround northe members ofSurround::SecondWithin can directly accessSurround::FirstWithin::d_variable.Surround::SecondWithin is only visible insideSurround. The public members of the classSurround::SecondWithin canalso be used by the members of the classSurround::FirstWithin, as nestedclasses can be considered members of their surrounding class.Surround::SecondWithin's constructor and its member functionvar also can only be reached by the members ofSurround (and by themembers of its nested classes).Surround::SecondWithin::d_variable is only visible toSurround::SecondWithin's members. Neither the members ofSurround northe members ofSurround::FirstWithin can accessd_variable of theclassSurround::SecondWithin directly.Although nested classes can be considered members of the surroundingclass, members of nested classes arenot members of the surroundingclass: members of the classSurround may not directly callFirstWithin::var. This is understandable considering that aSurround object is not also aFirstWithin orSecondWithinobject. In fact, nested classes are just typenames. It is not implied thatobjects of such classes automatically exist in the surrounding class. If amember of the surrounding class should use a (non-static) member of a nestedclass then the surrounding class must define a nested class object, which canthereupon be used by the members of the surrounding class to use members ofthe nested class.
For example, in the following class definition there is a surroundingclassOuter and a nested classInner. The classOuter contains amember functioncaller. The member functioncaller uses thed_inner object that is composed withinOuter to callInner::infunction:
class Outer { public: void caller(); private: class Inner { public: void infunction(); }; Inner d_inner; // class Inner must be known }; void Outer::caller() { d_inner.infunction(); }Outer::caller outside of the classOuter, the function's fully qualified name (startingfrom the outermost class scope (Outer)) must be provided to thecompiler. Inline and in-class functions can be defined accordingly. They canbe defined and they can use any nested class. Even if the nested class'sdefinition appears later in the outer class's interface.When (nested) member functions are defined inline, their definitions should beput below their class interface. Static nested data members are also usuallydefined outside of their classes. If the classFirstWithin would have had astatic size_tdatamemberepoch, it could have been initialized as follows:
size_t Surround::FirstWithin::epoch = 1970;
Furthermore, multiple scope resolution operators are needed to refer topublic static members in code outside of the surrounding class:
void showEpoch() { cout << Surround::FirstWithin::epoch; }Within the classSurround only theFirstWithin::scope must be used; within the classFirstWithin there isno need to refer explicitly to the scope.
What about the members of the classSecondWithin? The classesFirstWithin andSecondWithin are both nested withinSurround, andcan be considered members of the surrounding class. Since members of a classmay directly refer to each other, members of the classSecondWithin canrefer to (public) members of the classFirstWithin. Consequently, membersof the classSecondWithin could refer to theepoch member ofFirstWithin asFirstWithin::epoch.
For example, the following classOuter contains two nested classesInner1 andInner2. The classInner1 contains a pointer toInner2 objects, andInner2 contains a pointer toInner1objects. Cross references require forward declarations. Forward declarationsmust be given an access specification that is identical to the accessspecification of their definitions. In the following example theInner2forward declaration must be given in aprivate section, as its definitionis also part of the classOuter's private interface:
class Outer { private: class Inner2; // forward declaration class Inner1 { Inner2 *pi2; // points to Inner2 objects }; class Inner2 { Inner1 *pi1; // points to Inner1 objects }; };friend keyword must be used.No friend declaration is required to grant a nested class access to theprivate members of its surrounding class. Static members of the surroundingclass can directly be accessed, other members can be accessed if a surroundingclass object is defined by or passed to members of the nested class. Afterall, a nested class is a type defined by its surrounding class and as suchobjects of the nested class are members of the outer class and thus can accessall the outer class's members. Here is an example showing this principle. Theexample won't compile as members of the classExtern are denied access toOuter's private members, butOuter::Inner's memberscan accessOuter's private members:
class Outer { int d_value; static int s_value; public: Outer() : d_value(12) {} class Inner { public: Inner() { cout << "Outer's static value: " << s_value << '\n'; } Inner(Outer &outer) { cout << "Outer's value: " << outer.d_value << '\n'; } }; }; class Extern // won't compile! { public: Extern(Outer &outer) { cout << "Outer's value: " << outer.d_value << '\n'; } Extern() { cout << "Outer's static value: " << Outer::s_value << '\n'; } }; int Outer::s_value = 123; int main() { Outer outer; Outer::Inner in1; Outer::Inner in2{ outer }; }Now consider the situation where a classSurround has two nestedclassesFirstWithin andSecondWithin. Each of the three classes has astatic data memberint s_variable:
class Surround { static int s_variable; public: class FirstWithin { static int s_variable; public: int value(); }; int value(); private: class SecondWithin { static int s_variable; public: int value(); }; };If the classSurround should be able to accessFirstWithin andSecondWithin's private members, these latter two classes must declareSurround to be their friend. The functionSurround::value canthereupon access the private members of its nested classes. For example (notethefriend declarations in the two nested classes):
class Surround { static int s_variable; public: class FirstWithin { friend class Surround; static int s_variable; public: int value(); }; int value(); private: class SecondWithin { friend class Surround; static int s_variable; public: int value(); }; }; inline int Surround::FirstWithin::value() { FirstWithin::s_variable = SecondWithin::s_variable; return (s_variable); }Friend declarations may be providedbeyond the definition of theentity that is to be considered a friend. So a class can be declared a friendbeyond its definition. In that situation in-class code may already use thefact that it is going to be declared a friend by the upcoming class. As anexample, consider an in-class implementation of the functionSurround::FirstWithin::value. The requiredfriend declaration can alsobe insertedafter the implementation of the functionvalue:
class Surround { public: class FirstWithin { static int s_variable; public: int value(); { FirstWithin::s_variable = SecondWithin::s_variable; return s_variable; } friend class Surround; }; private: class SecondWithin { friend class Surround; static int s_variable; }; };Note that members named identically in outer and inner classes(e.g., `s_variable') may be accessed using the proper scope resolution expressions, asillustrated below:
class Surround { static int s_variable; public: class FirstWithin { friend class Surround; static int s_variable; // identically named public: int value(); }; int value(); private: class SecondWithin { friend class Surround; static int s_variable; // identically named public: int value(); }; static void classMember(); }; inline int Surround::value() { // scope resolution expression FirstWithin::s_variable = SecondWithin::s_variable; return s_variable; } inline int Surround::FirstWithin::value() { Surround::s_variable = 4; // scope resolution expressions Surround::classMember(); return s_variable; } inline int Surround::SecondWithin::value() { Surround::s_variable = 40; // scope resolution expression return s_variable; }Nested classes aren't automatically each other's friends. Herefrienddeclarations must be provided to grant one nested classes access to anothernested class's private members.
To grantFirstWithin access toSecondWithin's private members,SecondWithin must contain afriend declaration.
Likewise, the classFirstWithin simply usesfriend classSecondWithin to grantSecondWithin access toFirstWithin's privatemembers. Even though the compiler hasn't seenSecondWithin yet, a frienddeclaration is also considered a forward declaration.
Note thatSecondWithin's forward declaration cannot be specified insideFirstWithin by using `class Surround::SecondWithin;', as this wouldgenerate an error message like:
`Surround' does not have a nested type named `SecondWithin'
Now assume that in addition to the nested classSecondWithin there alsoexists an outer-level classSecondWithin. To declare that class a friendofFirstWithin's declarefriend ::SecondWithin insideclassFirstWithin. In that case, an outer level class declaration ofFirstWithin must be provided before the compiler encounters thefriend::SecondWithin declaration.
Here is an example in which all classes have full access to all privatemembers of all involved classes:, and aouter levelFirstWithin has also been declared:
class SecondWithin; class Surround { // class SecondWithin; not required (but no error either): // friend declarations (see below) // are also forward declarations static int s_variable; public: class FirstWithin { friend class Surround; friend class SecondWithin; friend class ::SecondWithin; static int s_variable; public: int value(); }; int value(); // implementation given above private: class SecondWithin { friend class Surround; friend class FirstWithin; static int s_variable; public: int value(); }; }; inline int Surround::FirstWithin::value() { Surround::s_variable = SecondWithin::s_variable; return s_variable; } inline int Surround::SecondWithin::value() { Surround::s_variable = FirstWithin::s_variable; return s_variable; }ios we've seen values likeios::beg andios::cur. In the currentGNUC++ implementation these values aredefined as values of theseek_dir enumeration: class ios: public _ios_fields { public: enum seek_dir { beg, cur, end }; };As an illustration assume that a classDataStructure represents a datastructure thatmay be traversed in a forward or backward direction. Such a class can definean enumerationTraversal having the valuesFORWARD andBACKWARD. Furthermore, a member functionsetTraversal can be definedrequiring aTraversal type of argument. The class can be defined asfollows:
class DataStructure { public: enum Traversal { FORWARD, BACKWARD }; setTraversal(Traversal mode); private: Traversal d_mode; };Within the classDataStructure the values of theTraversalenumeration can be used directly. For example:
void DataStructure::setTraversal(Traversal mode) { d_mode = mode; switch (d_mode) { FORWARD: // ... do something break; BACKWARD: // ... do something else break; } }Outside of the classDataStructure the name of the enumeration type isnot used to refer to the values of the enumeration. Here the classname issufficient. Only if a variable of the enumeration type is required the name ofthe enumeration type is needed, as illustrated by the following piece of code:
void fun() { DataStructure::Traversal // enum typename required localMode = DataStructure::FORWARD; // enum typename not required DataStructure ds; // enum typename not required ds.setTraversal(DataStructure::BACKWARD); }In the above example the constantDataStructure;:FORWARD was used tospecify a value of an enum defined in the classDataStructure. Instead ofDataStructure::FORWARD the constructionds.FORWARD is alsoaccepted. In my opinion this syntactic liberty is ugly:FORWARD is asymbolic value that is defined at the class level; it's not a member ofds,which is suggested by the use of the member selector operator.
Only ifDataStructure defines a nested classNested, inturn defining the enumerationTraversal, the two class scopes arerequired. In that case the latter example should have been coded as follows:
void fun() { DataStructure::Nested::Traversal localMode = DataStructure::Nested::FORWARD; DataStructure ds; ds.setTraversal(DataStructure::Nested::BACKWARD); }Here a construction likeDataStructure::Nested::Traversal localMode =ds.Nested::FORWARD could also have been used, although I personally wouldavoid it, asFORWARD is not a member ofds but rather a symbol that isdefined inDataStructure.
Enum types usually define symbolic values. However, this is notrequired. In section14.6.1 thestd::bad_cast type wasintroduced. Abad_cast is thrown by thedynamic_cast<> operatorwhen a reference to a base class object cannot be cast to a derivedclass reference. Thebad_cast could be caught as type, irrespectiveof any value it might represent.Types may be defined without any associatedvalues. Anempty enum can be defined which is anenum not definingany values. The empty enum's type name may thereupon be used as a legitimatetype in, e.g. acatch clause.
The example shows how an empty enum is defined (often, but not necessarilywithin aclass) and how it may be thrown (and caught) as exceptions:
#include <iostream> enum EmptyEnum {}; int main() try { throw EmptyEnum(); } catch (EmptyEnum) { std::cout << "Caught empty enum\n"; }Base was defined as an abstract base class. A classClonable was defined to manageBase class pointers in containers likevectors.As the classBase is a minute class, hardly requiring any implementation,it can very well be defined as a nested class inClonable. Thisemphasizes the close relationship betweenClonable andBase. NestingBase underClonable changes
class Derived: public Base
into:
class Derived: public Clonable::Base
Apart from definingBase as a nested class and deriving fromClonable::Base rather than fromBase (and providingBase memberswith the properClonable:: prefix to complete their fully qualifiednames), no further modifications are required. Here are the modified parts ofthe program shown earlier (cf. section14.12), now usingBasenested underClonable:
// Clonable and nested Base, including their inline members: class Clonable { public: class Base; private: Base *d_bp; public: class Base { public: virtual ~Base(); Base *clone() const; private: virtual Base *newCopy() const = 0; }; Clonable(); explicit Clonable(Base *base); ~Clonable(); Clonable(Clonable const &other); Clonable(Clonable &&tmp); Clonable &operator=(Clonable const &other); Clonable &operator=(Clonable &&tmp); Base &base() const; }; inline Clonable::Base *Clonable::Base::clone() const { return newCopy(); } inline Clonable::Base &Clonable::base() const { return *d_bp; }// Derived and its inline member: class Derived1: public Clonable::Base { public: ~Derived1(); private: virtual Clonable::Base *newCopy() const; }; inline Clonable::Base *Derived1::newCopy() const { return new Derived1(*this); }// Members not implemented inline: Clonable::Base::~Base() {}