Don't hesitate to send in feedback: send an e-mail if you like the C++ Annotations; if you think that important material was omitted; if you find errors or typos in the text or the code examples; or if you just feel like e-mailing. Send your e-mail to Frank B. Brokken.Please state the document version you're referring to, as found in the title (in this document: 8.3.1) and please state chapter and paragraph name or number you're referring to.
All received mail is processed conscientiously, and received suggestions for improvements will usually have been processed by the time a new version of the Annotations is released. Except for the incidental case I will normally not acknowledge the receipt of suggestions for improvements. Please don't interpret this as me not appreciating your efforts.
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 its surrounding class. For example, with the class string
a type
string::iterator is available which will provide all 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, if it is nested in the private section, it is
only visible for the members of the surrounding class.
The surrounding class has no special privileges towards the nested class. The nested class 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 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;
}
Here access to the members is defined as follows:
FirstWithin is visible outside and inside
Surround. The class FirstWithin thus has global visibility.
FirstWithin's constructor and its member function
var are also globally visible.
d_variable is only visible to the members of
the class FirstWithin. Neither the members of Surround nor the members
of SecondWithin can directly access FirstWithin::d_variable.
SecondWithin is only visible 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's constructor and its member function var
also can only be reached by the members of Surround (and by the members of
its nested classes).
SecondWithin::d_variable is only visible to
SecondWithin's members. Neither the members of Surround nor the
members of FirstWithin can access d_variable of the class
SecondWithin directly.
friend classes (see section 17.3).
Nested classes can be considered members of the surrounding class, but
members of nested classes are not members of the surrounding class. So, a
member of the class Surround may not access FirstWithin::var
directly. This is understandable considering that a Surround object is not
also a FirstWithin or SecondWithin object. In fact, nested classes are
just typenames. It is not implied that objects of such classes automatically
exist in the surrounding class. If a member of the surrounding class should
use a (non-static) member of a nested class then the surrounding class must
define a nested class object, which can thereupon be used by the members of
the surrounding class to use 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. The member function caller uses the
d_inner object that is composed within Outer to call
Inner::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();
}
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. On the other hand, the compiler must have seen the
definition of the class Inner before a data member of that class can be
defined.
Outer::caller outside of the class Outer, the function's
fully qualified name (starting
from the outermost class scope (Outer)) must be provided to the
compiler. Inline and in-class functions can be defined accordingly. They can
be defined and they can use any nested class. Even if the nested class's
definition appears later in the outer class's interface.
When (nested) member functions are defined inline, their definitions should be
put below their class interface. Static nested data members are also usually
defined outside of their classes.
If the class FirstWithin would have had a static size_t
datamember epoch, it could have been initialized as follows:
size_t 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;
}
Within the class Surround only the FirstWithin::
scope must be used; within 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 directly 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. Cross references require forward declarations. Forward declarations
must be given an access specification that is identical to the access
specification of their definitions. In the following example the Inner2
forward declaration must be given in a private section, as its definition
is also part of the class Outer'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.
Consider the following situation where a class Surround has two nested
classes FirstWithin and SecondWithin. Each of the three classes has a
static data member int 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 class Surround should be able to access FirstWithin and
SecondWithin's private members, these latter two classes must declare
Surround to be their friend. The function Surround::value can
thereupon access the private members of its 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 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);
}
To allow the nested classes access to the private members of their
surrounding class, the class Surround must declare its nested classes as
friends. As 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, either a
forward declaration of the nested classes is required (to be 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
{
public:
class FirstWithin;
class SecondWithin;
friend class FirstWithin;
friend class SecondWithin;
class FirstWithin
{ ... };
...
};
Alternatively, the friend declaration may follow the definition of the
classes. A class can be declared a friend following its definition, with
in-class code already using the fact that it will be declared a friend of the
outer class. Furthermore, in-class defined nested class members may already
use members of the surrounding class that have not yet been seen by the
compiler. Finally note that `s_variable' which is
defined in the class Surround may be
accessed by 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 value();
};
friend class FirstWithin;
int value();
private:
class SecondWithin
{
friend class Surround;
static int s_variable;
public:
int value();
};
static void classMember();
friend class SecondWithin;
};
inline int Surround::value()
{
FirstWithin::s_variable = SecondWithin::s_variable;
return s_variable;
}
inline int Surround::FirstWithin::value()
{
Surround::s_variable = 4;
Surround::classMember();
return s_variable;
}
inline int Surround::SecondWithin::value()
{
Surround::s_variable = 40;
return s_variable;
}
Finally, friend declarations must be applied to grant the nested
classes access to each other's private members. To grant FirstWithin
access to SecondWithin's private members nothing but a friend
declaration in SecondWithin is required. To grant
SecondWithin access to FirstWithin's private members the class
FirstWithin cannot simply use friend class SecondWithin, as
SecondWithin's definition is as yet unknown. A
forward declaration of
SecondWithin is required and this forward declaration must be provided by
the class Surround, rather than by the class FirstWithin.
Considering the above, a forward declaration like `class SecondWithin'
makes no sense in the class FirstWithin itself, as this would refer to an
external (global) class SecondWithin. Likewise, the forward declaration of
the nested class SecondWithin cannot be specified inside FirstWithin
as class Surround::SecondWithin. Doing so would generate the following
error message:
`Surround' does not have a nested type named `SecondWithin'
Instead of providing a forward declaration for SecondWithin inside the
nested classes the class SecondWithin must be declared by the class
Surround, before the class FirstWithin has been defined. This way
SecondWithin's friend declaration is accepted inside FirstWithin.
The following example shows the classes having full access to all private members of all classes:
class Surround
{
class SecondWithin;
static int s_variable;
public:
class FirstWithin
{
friend class Surround;
friend class SecondWithin;
static int s_variable;
public:
int value();
};
friend class FirstWithin;
int value();
private:
class SecondWithin
{
friend class Surround;
friend class FirstWithin;
static int s_variable;
public:
int value();
};
friend class SecondWithin;
};
inline int Surround::value()
{
FirstWithin::s_variable = SecondWithin::s_variable;
return s_variable;
}
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 like ios::beg and
ios::cur. In the current
Gnu C++ implementation these values are
defined as values of the
seek_dir enumeration:
class ios: public _ios_fields
{
public:
enum seek_dir
{
beg,
cur,
end
};
};
As an illustration assume that a class DataStructure represents a data
structure that
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 a Traversal type of argument. 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:
// ... do something
break;
BACKWARD:
// ... do something else
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
sufficient. 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);
}
Only if DataStructure defines a nested class Nested, in
turn defining the enumeration Traversal, the two class scopes are
required. 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);
}
Enum types usually define symbolic values. However, this is not
required. In section 14.5.1 the std::bad_cast type was
introduced. A bad_cast is thrown by the dynamic_cast<> operator
when a reference to a base class object cannot be cast to a derived
class reference. The bad_cast could be caught as type, irrespective
of any value it might represent.
Types may be defined
without any associated
values. An
empty enum can be defined which is an enum not defining
any values. The empty enum's type name may thereupon be used as a legitimate
type in, e.g. a
catch clause.
The example shows how an empty enum is defined (often, but not necessarily
within a
class) 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 class
Clonable was defined to manage Base class pointers in containers like
vectors.
As the class Base is a minute class, hardly requiring any implementation,
it can very well be defined as a nested class in Clonable. This
emphasizes the close relationship between Clonable and
Base. Nesting Base under Clonable changes
class Derived: public Base
into:
class Derived: public Clonable::Base
Apart from defining Base as a nested class and deriving from
Clonable::Base rather than from Base (and providing Base members
with the proper Clonable:: prefix to complete their fully qualified
names), no further modifications are required. Here are the modified parts of
the program shown earlier (cf. section 14.11), now using Base
nested under Clonable:
// 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 const &&tmp);
Clonable &operator=(Clonable const &other);
Clonable &operator=(Clonable const &&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()
{}