We're always interested in getting feedback. E-mail us if you like this guide, if you think that important material is omitted, if you encounter errors in the code examples or in the documentation, if you find any typos, or generally just if you feel like e-mailing. Send your email to Frank Brokken.Please state the document version you're referring to, as found in the title (in this document: 5.2.0a) and please state the paragraph you're referring to.
All mail received is seriously considered, and new (sub)releases of the Annotations will normally reflect your suggestions for improvements. Except for the incidental case I will not otherwise acknowledge the receipt of suggestions for improvements. Please don't misinterpret this for lack of appreciation.
Classes can be defined inside other classes. Classes that are defined inside
other classes are called
nested classes. Nested classes
are used in situations where the nested class has a close conceptual
relationship to the surrounding class. For example, with the class string
a type
string::iterator
is available which will provide all elements
(characters) that are stored in the string
. This string::iterator
type
could be defined as an object
iterator
, defined as nested class in the
class string
.
A class can be nested in every part of the surrounding class: in the
public, protected
or private
section. Such a nested class can be
considered a member
of the surrounding class. The
normal access and rules in classes apply to nested classes. If a
class is nested in the public
section of a class, it is
visible outside the surrounding class. If
it is nested in the protected
section it is visible in subclasses, derived
from the surrounding class (see chapter 13), if it is nested in
the private
section, it is only visible for the members of the surrounding
class.
The surrounding class has no privileges with respect to the nested class. So, the nested class still has full control over the accessibility of its members by the surrounding class. For example, consider the following class definition:
class Surround { public: class FirstWithin { int d_variable; public: FirstWithin(); int getVar() const { return (d_variable); } }; private: class SecondWithin { int d_variable; public: SecondWithin(); int getVar() const { return (d_variable); } }; };In this definition access to the members is defined as follows:
FirstWithin
is visible both outside and inside
Surround
. The class FirstWithin
has therefore global scope.
FirstWithin()
and the member function
getVar()
of the class FirstWithin
are also globally visible.
int d_variable
datamember is only visible for the members
of the class FirstWithin
. Neither the members of Surround
nor the
members of SecondWithin
can access the d_variable
of the class
FirstWithin
directly.
SecondWithin
is visible only inside
Surround
. The public members of the class SecondWithin
can also be
used by the members of the class FirstWithin
, as nested classes can be
considered members of their surrounding class.
SecondWithin()
and the member function
getVar()
of the class SecondWithin
can also only be reached by the
members of Surround
(and by the members of its nested classes).
int d_variable
datamember of the class SecondWithin
is
only visible for the members of the class SecondWithin
. Neither the
members of Surround
nor the members of FirstWithin
can access the
d_variable
of the class SecondWithin
directly.
friend
classes (see section 16.3).
The nested classes can be considered members of the surrounding class, but
the
members of nested classes are not members of the surrounding
class. So, a member of the class Surround
may not access
FirstWithin::getVar()
directly. This is understandable considering the
fact that a Surround
object is not also a FirstWithin
or
SecondWithin
object. The nested classes are only available as
typenames. They do not imply containment as objects by the surrounding
class. If a member of the surrounding class should use a (non-static) member
of a nested class then a pointer to a nested class object or a nested class
datamember must be defined in the surrounding class, which can thereupon be
used by the members of the surrounding class to access members of the nested
class.
For example, in the following class definition there is a surrounding
class Outer
and a nested class Inner
. The class Outer
contains a
member function caller()
which uses the inner
object that is composed
in Outer
to call the infunction()
member function of Inner
:
class Outer { public: void caller() { d_inner.infunction(); } private: class Inner { public: void infunction(); }; Inner d_inner; // class Inner must be known };Also note that the function
Inner::infunction()
can be called as part
of the inline definition of Outer::caller()
, even though the definition of
the class Inner
is yet to be seen by the compiler.
Outer::caller()
would have been defined outside of the class
Outer
, the full class definition (including the definition of the class
Inner
) would have been available to the compiler. In that situation the
function is perfectly compilable. Inline functions can be compiled
accordingly: they can be defined and use any nested class appearing later in
the class interface.
However, inline member functions can also be defined outside of their
surrounding class. Consider the constructor of the class FirstWithin
in
the example of the previous section. The constructor FirstWithin()
is
defined in the class FirstWithin
, which is, in turn, defined within the
class Surround
. Consequently, the class scopes of the two classes must be
used to define the constructor. E.g.,
Surround::FirstWithin::FirstWithin() { variable = 0; }Static (data) members can be defined accordingly. If the class
FirstWithin
would have a static
unsigned
datamember epoch
, it could be initialized as follows:
Surround::FirstWithin::epoch = 1970;Furthermore, multiple scope resolution operators are needed to refer to public static members in code outside of the surrounding class:
void showEpoch() { cout << Surround::FirstWithin::epoch = 1970; }Inside the members of the class
Surround
only the FirstWithin::
scope must be used; inside the members of the class FirstWithin
there is
no need to refer explicitly to the scope.
What about the members of the class SecondWithin
? The classes
FirstWithin
and SecondWithin
are both nested within Surround
, and
can be considered members of the surrounding class. Since members of a class
may directy refer to each other, members of the class SecondWithin
can
refer to (public) members of the class FirstWithin
. Consequently, members
of the class SecondWithin
could refer to the epoch
member of
FirstWithin
as
FirstWithin::epoch
For example, the following class Outer
contains two nested classes
Inner1
and Inner2
. The class Inner1
contains a pointer to
Inner2
objects, and Inner2
contains a pointer to Inner1
objects. Such cross references require forward declarations:
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. Consider the following
situation, in which a class Surround
has two nested classes
FirstWithin
and SecondWithin
, while each class has a
static data member int variable
:
class Surround { static int s_variable; public: class FirstWithin { static int s_variable; public: int getValue(); }; int getValue(); private: class SecondWithin { static int s_variable; public: int getValue(); }; };If the class
Surround
should be able to access the private members of
FirstWithin
and SecondWithin
, these latter two classes must declare
Surround
to be their friend. The function Surround::getValue()
can
thereupon access the private members of the nested classes. For example (note
the friend
declarations in the two nested classes):
class Surround { static int s_variable; public: class FirstWithin { friend class Surround; static int s_variable; public: int getValue(); }; int getValue() { FirstWithin::s_variable = SecondWithin::s_variable; return (s_variable); } private: class SecondWithin { friend class Surround; static int s_variable; public: int getValue(); }; };Now, in order to allow the nested classes to access the private members of the surrounding class, the class
Surround
must declare the nested classes
as friends. The friend
keyword may only be used when the class that is to
become a friend is already known as a class by the compiler, so either a
forward declaration of the nested classes is required, which is followed
by the friend declaration, or the friend declaration follows the definition of
the nested classes. The forward declaration followed by the friend declaration
looks like this:
class Surround { class FirstWithin; class SecondWithin; friend class FirstWithin; friend class SecondWithin; public: class FirstWithin; ;Alternatively, the friend declaration may follow the definition of the classes. Note that a class can be declared a friend following its definition, while the inline code in the definition already uses the fact that it will be declared a friend of the outer class. Also note that the inline code of the nested class uses members of the surrounding class which have not yet been seen by the compiler. Finally note that `
s_variable
' which is
defined in the class Surround
is
accessed in the nested classes as Surround::s_variable
:
class Surround { static int s_variable; public: class FirstWithin { friend class Surround; static int s_variable; public: int getValue() { Surround::s_variable = 4; return (s_variable); } }; friend class FirstWithin; int getValue() { FirstWithin::s_variable = SecondWithin::s_variable; return (s_variable); } private: class SecondWithin { friend class Surround; static int s_variable; public: int getValue() { Surround::s_variable = 40; return (s_variable); } }; friend class SecondWithin; };Finally, we want to allow the nested classes to access each other's private members. Again this requires some
friend
declarations. In order to
allow FirstWithin
to access SecondWithin
's private members nothing but
a friend
declaration in SecondWithin
is required. However, to allow
SecondWithin
to access the private members of FirstWithin
the
friend class SecondWithin
declaration cannot be plainly given in the class
FirstWithin
, as the definition of SecondWithin
has not yet been
given. A
forward declaration of SecondWithin
is required, and this
forward declaration must be given in the class Surround
, rather than in
the class FirstWithin
. Clearly, the forward declaration class
SecondWithin
in the class FirstWithin
itself makes no sense, as this
would refer to an external (global) class FirstWithin
. But the attempt to
provide the forward declaration of the nested class SecondWithin
inside
FirstWithin
as class Surround::SecondWithin
also fails miserably, with
the compiler issuing a message like
`Surround' does not have a nested type named `SecondWithin'
The proper procedure here is to declare the class SecondWithin
in the
class Surround
, before the class FirstWithin
is defined. Using this
procedure, the friend declaration of SecondWithin
is accepted inside the
definition of FirstWithin
. The following class definition allows full
access of the private members of all classes by all other classes:
class Surround { class SecondWithin; static int s_variable; public: class FirstWithin { friend class Surround; friend class SecondWithin; static int s_variable; public: int getValue() { Surround::s_variable = SecondWithin::s_variable; return (s_variable); } }; friend class FirstWithin; int getValue() { FirstWithin::s_variable = SecondWithin::s_variable; return (s_variable); } private: class SecondWithin { friend class Surround; friend class FirstWithin; static int s_variable; public: int getValue() { Surround::s_variable = FirstWithin::s_variable; return (s_variable); } }; friend class SecondWithin; };
ios
we've
seen values like
ios::beg
and
ios::cur
. These values are (i.e., in the
current
Gnu C++ implementation) defined as values in the
seek_dir
enumeration:
class ios : public _ios_fields { public: enum seek_dir { beg, cur, end }; };For illustration purposes, let's assume that, a class
DataStructure
may be traversed in a forward or backward direction. Such a class can define
an enumeration Traversal
having the values forward
and
backward
. Furthermore, a member function setTraversal()
can be defined
requiring either of the two enumeration values. The class can be defined as
follows:
class DataStructure { public: enum Traversal { forward, backward }; setTraversal(Traversal mode); private: Traversal d_mode; };Within the class
DataStructure
the values of the Traversal
enumeration can be used directly. For example:
void DataStructure::setTraversal(Traversal mode) { d_mode = mode; switch (d_mode) { forward: break; backward: break; } }Ouside of the class
DataStructure
the name of the enumeration type is
not used to refer to the values of the enumeration. Here the classname is
enough. Only if a variable of the enumeration type is required the name of the
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); }Again, if
DataStructure
would define a nested class Nested
in
which the enumeration Traversal
would have been defined, the two class
scopes would have been required. In that case the former example would have to
be coded as follows:
void fun() { DataStructure::Nested::Traversal localMode = DataStructure::Nested::forward; DataStructure ds; ds.setTraversal(DataStructure::Nested::backward); }
Enum
types usually have values. However, this is not required. In
section 14.5.1 the
std::bad_cast
type was introduced. A
std::bad_cast
is thrown by the
dynamic_cast<>()
operator when a
reference to a
base class object cannot be cast to a
dervied class
reference. The std::bad_cast
could be caught as type, disregarding any
value it might represent.
Actually, it is not necessary for a
type to
contain values. It is possible to define an
empty enum, an enum
without any values, whose name may thereupon be used as a legitimate type name
in, e.g. a
catch
clause defining an
exception handler.
An empty enum
is defined as follows (often, but not necessarily within
a
class
):
enum EmptyEnum {};Now an
EmptyEnum
may be thrown (and caught) as an exception:
#include <iostream> enum EmptyEnum {}; int main() { try { throw EmptyEnum(); } catch (EmptyEnum) { cout << "Caught empty enum\n"; } } /* Generated output: Caught empty enum */