Chapter 16: Nested Classes

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: 6.5.0) 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 (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 special 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 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 this definition access to the members is defined as follows: If the surrounding class should have access rights to the private members of its nested classes or if nested classes should have access rights to the private members of the surrounding class, the classes can be defined as 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::var() directly. This is understandable considering the fact 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() which uses the inner object that is composed in Outer to call the infunction() member function of Inner:

    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();
    }
The mentioned 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. 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.

16.1: Defining nested class members

Member functions of nested classes may be defined as inline functions. Inline member functions can be defined as if they were functions defined outside of the class definition: if the function 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 they can use any nested class. Even if it appears later in the class interface.

As shown, when (nested) member functions are defined inline, their definition should be put below their class interface. Static nested data members are also normally defined outside of their classes. If the class FirstWithin would have a static size_t datamember epoch, it could be 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 = 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 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

16.2: Declaring nested classes

Nested classes may be declared before they are actually defined in a surrounding class. Such forward declarations are required if a class contains multiple nested classes, and the nested classes contain pointers, references, parameters or return values to objects of the other nested classes.

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. These forward declarations must be specified in the same access-category as their actual 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
            };
    };

16.3: Accessing private members in nested classes

To allow nested classes to access the private members of their surrounding class; to access the private members of other nested classes; or to allow the surrounding class to access the private members of its nested classes, the 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 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);
    }
Now, to allow the nested classes access to the private members of their surrounding class, the class Surround must declare its 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. When defining members within the class interface implementations of nested class members may use members of the surrounding class that have not yet been seen by the compiler. Finally note that q`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 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, we want to allow the nested classes access to 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 plainly be given in the class FirstWithin, as the definition of SecondWithin 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.

Clearly, the forward declaration class SecondWithin in the class FirstWithin itself makes no sense, as this would refer to an external (global) class SecondWithin. Likewise, it is impossible to provide the forward declaration of the nested class SecondWithin inside FirstWithin as class Surround::SecondWithin, 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 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;
    }

16.4: Nesting enumerations

Enumerations too may be nested in classes. Nesting enumerations is a good way to show the close connection between the enumeration and its class. In the class ios we've seen values like ios::beg and ios::cur. In the current Gnu C++ implementation these values are 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 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);
    }
Again, 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);
    }

16.4.1: Empty enumerations

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 derived class reference. The std::bad_cast could be caught as type, irrespective of any value it might represent.

Actually, it is not even 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
    {};

    using namespace std;

    int main()
    try
    {
        throw EmptyEnum();
    }
    catch (EmptyEnum)
    {
        cout << "Caught empty enum\n";
    }
    /*
        Generated output:

        Caught empty enum
    */

16.5: Revisiting virtual constructors

In section 14.10 the notion of virtual constructors was introduced. In that section a class Base was used as an abstract base class. A class Clonable was thereupon defined to manage Base class pointers in containers like vectors.

As the class Base is a very small class, hardly requiring any implementation, it can well be defined as a nested class in Clonable. This will emphasize the close relationship that exists between Clonable and Base, as shown by the way classes are derived from Base. One no longer writes:

    class Derived: public Base
but rather:
    class Derived: public Clonable::Base
Other than defining Base as a nested class, and deriving from Clonable::Base rather than from Base, nothing needs to be modified. Here is the program shown earlier in section 14.10, but now using nested classes:
    #include <iostream>
    #include <vector>
    #include <typeinfo>

    class Clonable
    {
        public:
            class Base
            {
                public:
                    virtual ~Base();
                    virtual Base *clone() const = 0;
            };

        private:
            Base *d_bp;

        public:
            Clonable();
            ~Clonable();
            Clonable(Clonable const &other);
            Clonable &operator=(Clonable const &other);

            // New for virtual constructions:
            Clonable(Base const &bp);
            Base &get() const;

        private:
            void copy(Clonable const &other);
    };


    inline Clonable::Base::~Base()
    {}

    inline Clonable::Clonable()
    :
        d_bp(0)
    {}
    inline Clonable::~Clonable()
    {
        delete d_bp;
    }
    inline Clonable::Clonable(Clonable const &other)
    {
        copy(other);
    }
    inline Clonable &Clonable::operator=(Clonable const &other)
    {
        if (this != &other)
        {
            delete d_bp;
            copy(other);
        }
        return *this;
    }

    inline Clonable::Clonable(Base const &bp)
    {
        d_bp = bp.clone();      // allows initialization from
    }                           // Base and derived objects

    inline Clonable::Base &Clonable::get() const
    {
        return *d_bp;
    }

    inline void Clonable::copy(Clonable const &other)
    {
        if ((d_bp = other.d_bp))
            d_bp = d_bp->clone();
    }

    class Derived1: public Clonable::Base
    {
        public:
            ~Derived1();
            virtual Clonable::Base *clone() const;
    };

    inline Derived1::~Derived1()
    {
        std::cout << "~Derived1() called\n";
    }
    inline Clonable::Base *Derived1::clone() const
    {
        return new Derived1(*this);
    }

    using namespace std;

    int main()
    {
        vector<Clonable> bv;

        bv.push_back(Derived1());
        cout << "==\n";

        cout << typeid(bv[0].get()).name() << endl;
        cout << "==\n";

        vector<Clonable> v2(bv);
        cout << typeid(v2[0].get()).name() << endl;
        cout << "==\n";
    }