237

Can someone please point me towards some nice resources for understanding and using nested classes? I have some material like Programming Principles and things like this IBM Knowledge Center - Nested Classes

But I'm still having trouble understanding their purpose. Could someone please help me?

Michael Ma
  • 872
  • 8
  • 20
bespectacled
  • 2,801
  • 4
  • 25
  • 35
  • Hard to answer when there's no questions. And you've already got a quite nice description out there- what do you specifically expect from answers here? – Kos Dec 31 '10 at 17:17
  • 20
    My advice for nested classes in C++ is simply to not use nested classes. – Billy ONeal Dec 31 '10 at 17:18
  • 12
    They're exactly like regular classes... except nested. Use them when a class's internal implementation is so complex that it can most easily be modeled by several smaller classes. – user229044 Dec 31 '10 at 17:24
  • 17
    @Billy: Why? Seems overly broad to me. – John Dibling Dec 31 '10 at 17:29
  • 1
    Maybe class composition can be an alternative to nested classes – Akhil Dec 31 '10 at 17:32
  • 1
    @Akhil: Maybe class composition as an alternative to nested classes is jamming a square peg in a round hole. – John Dibling Dec 31 '10 at 17:35
  • 1
    The only time I needed nested classes was when using a vendor's horrible API that required various runtime shenanigans to use their constructor. It was a horrible mess and is our ugliest code base. Moral of the story: @Billy is right; don't use nested classes if you don't have to. – chrisaycock Dec 31 '10 at 17:39
  • 1
    @John: Because there's never any reason to declare a nested class. It's not like, say, Java, where being a nested class confers some benefit to that class about it's parent. – Billy ONeal Dec 31 '10 at 17:45
  • 40
    I still haven't seen an argument why nested classes are bad by their nature. – John Dibling Dec 31 '10 at 17:55
  • 5
    @Billy: What about name scoping, as in my example below? Is not the expression `Field::match(...)` fairly natural and self-documenting? Of course this isn't *necesarry*, one could declare a namespace-scoped class like `match_field` just as easily, but that doesn't make the nested class **bad**, just another way to skin the cat. – John Dibling Dec 31 '10 at 17:58
  • 3
    @John: Actually I think your example is a prime example of why one should avoid nested classes. You have an `id` in both the outer and nested class, and it's confusing as to which is referred to where. As for `Field::match`, what's wrong with a non-nested class `FieldMatch`? If you want `Field::match` to work, just do `typedef FieldMatch match` inside `Field` and it'll still work, and you won't have the problem of scope conflicts between the main and nested class. – Billy ONeal Dec 31 '10 at 18:05
  • @Billy, if `id` is not a static member, why would it be confusing? For me, the question is not how to obtain `Field::match` with an externally-defined class, but what is the reason for *not making* it nested. Because of those scope conflicts you mentioned? What is so bad about them? – Roman L Dec 31 '10 at 18:42
  • 10
    @7vies: 1. because it's simply not necessary -- you can do the same with externally defined classes, which reduces the scope of any given variable, which is a good thing. 2. because you can do everything nested classes can do with `typedef`. 3. because they add an additional level of indentation in an environment where avoiding long lines is already difficult 4. because you are declaring two conceptually separate objects in a single `class` declaration, etc. – Billy ONeal Dec 31 '10 at 18:52
  • 5
    @Billy: 1,2. You can't get exactly the same effects as a private nested class with a separate class plus `typedef`. 3. No additional indentation if you define the nested class outside. `struct Outer { struct Inner; }; struct Outer::Inner {}; 4. Usually a nested class and its outer class are so closely related that it does make sense to let their declarations intertwine. – aschepler Dec 31 '10 at 19:02
  • 1
    @aschepler: RE: 1,2: How exactly does it differ then? – Billy ONeal Dec 31 '10 at 19:06
  • @Billy: Even if you have an externally defined class, they will both have an `id` member in the case of my example. Take another look. – John Dibling Dec 31 '10 at 19:40
  • 1
    @John: I know that -- my point is that it's more confusing to have two of the same name that way -- it's not wrong or ambiguous, but it certainly isn't obvious upon casual examination which `id` the author of the class was looking for. (Particularly because in your example we've seen the outer class' `id` before it gets used, but we've not see the inner class' `id` when it is used, assuming we're reading from top to bottom) – Billy ONeal Dec 31 '10 at 20:33
  • 6
    Seems worth mentioning that an inner class is implicitly a friend of the parent? – Chinasaur Mar 20 '15 at 18:45
  • A disadvantage of nested classes that I haven't seen mentioned yet is they cannot be forward declared but require the parent class definition, which requires header dependencies you might otherwise avoid. – Jake Cobb Sep 21 '16 at 18:35
  • Java used a lot nested/inner classes (I have used some), maybe we can get some hits from there. Personally, I have never used one in C++. – Kemin Zhou Dec 14 '16 at 03:20
  • In C++ structs are implemented as classes that are by default public. This allows adding some smarts to them - like a constructor or two, some aggregation methods (calculating extra values on the fly), formatted "print contents" method, and so on. Generally, still making "just a struct" but with quality-of-life improvements attached. Separately, putting enums, arrays, structs etc inside a class is nothing exotic, just a specific kind of fields of the class. And so, an embedded struct is the most natural situation where you end up with nested class. – SF. Feb 22 '23 at 13:32

6 Answers6

282

Nested classes are cool for hiding implementation details.

List:

class List
{
    public:
        List(): head(nullptr), tail(nullptr) {}
    private:
        class Node
        {
              public:
                  int   data;
                  Node* next;
                  Node* prev;
        };
    private:
        Node*     head;
        Node*     tail;
};

Here I don't want to expose Node as other people may decide to use the class and that would hinder me from updating my class as anything exposed is part of the public API and must be maintained forever. By making the class private, I not only hide the implementation I am also saying this is mine and I may change it at any time so you can not use it.

Look at std::list or std::map they all contain hidden classes (or do they?). The point is they may or may not, but because the implementation is private and hidden the builders of the STL were able to update the code without affecting how you used the code, or leaving a lot of old baggage laying around the STL because they need to maintain backwards compatibility with some fool who decided they wanted to use the Node class that was hidden inside list.

peterh
  • 11,875
  • 18
  • 85
  • 108
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • 12
    If you're doing this then `Node` shouldn't be exposed in the header file at all. – Billy ONeal Dec 31 '10 at 19:08
  • 10
    @Billy ONeal: What if I am doing a header file implementation like the STL or boost. – Martin York Dec 31 '10 at 19:29
  • @Martin: Apparently header file implementations are also "bad" :/ – John Dibling Dec 31 '10 at 20:21
  • @Martin: Then I still wouldn't define it as a nested class (It'd go into a `namespace detail`), but in that case I think it's just a matter of opinion. – Billy ONeal Dec 31 '10 at 20:31
  • 8
    @Billy ONeal: No. Its a matter of good design not opinion. Putting it in a namespace does not protect it from use. It is now part of the public API that needs to be maintained for perpetuity. – Martin York Dec 31 '10 at 21:39
  • @Martin: Putting it in a nested class doesn't "protect" it any more than putting it in a namespace does. – Billy ONeal Dec 31 '10 at 22:08
  • 29
    @Billy ONeal: It protects it from accidental use. It also documents the fact that it is private and should not be used (can not be used unless you do something stupid). Thus you do not need to support it. Putting it in a namespace makes it part of the public API (something you keep missing in this conversation. Public API means you need to support it). – Martin York Dec 31 '10 at 22:36
  • 1
    @Martin York: Err... convention is that anything in a namespace `detail` is not public for clients to use. – Billy ONeal Jan 01 '11 at 04:21
  • 1
    @Billy ONeal: I like that convention and will start using it. But its the first time I have heard it. Is it a boost convention that is documented in their site. – Martin York Jan 01 '11 at 19:30
  • @John Dibling: Sure they are. But how to avoid them when one uses templates? – SasQ Mar 31 '14 at 09:18
  • 11
    @Billy ONeal: Nested class has some advantage over nested namespace: You cannot create instances of a namespace, but you can create instances of a class. As to the `detail` convention: Instead depending on such conventions one needs to keep in mind oneself, it's better to depend on the compiler which keeps track of them for you. – SasQ Mar 31 '14 at 09:21
  • Is possible to put implementation class out of distributed include path? This way client side cant use this classes never. I like this approach or use of forward declared nested classes. – Isaac Pascual Apr 11 '17 at 08:39
  • 1
    @IsaacPascual: You can do that. But you will need to implement the PIMPL pattern to use it. – Martin York Apr 11 '17 at 14:15
  • @Martin : What is the difference between _creating class P with all members as private and befreinding class B which is derived from P_ **AND** _creating a class within a class so that nobody can use the hidden class and easy maintanence without having to worry about breaking someone elses dependent code_ – anurag86 Jun 28 '18 at 07:11
  • @anurag86 Good question. Why not create a question. – Martin York Jun 28 '18 at 16:29
  • @Martin : Done https://stackoverflow.com/questions/51095742/difference-between-nested-class-and-befriending-a-derived-class-in-base-class – anurag86 Jun 29 '18 at 07:18
160

Nested classes are just like regular classes, but:

  • they have additional access restriction (as all definitions inside a class definition do),
  • they don't pollute the given namespace, e.g. global namespace. If you feel that class B is so deeply connected to class A, but the objects of A and B are not necessarily related, then you might want the class B to be only accessible via scoping the A class (it would be referred to as A::Class).

Some examples:

Publicly nesting class to put it in a scope of relevant class


Assume you want to have a class SomeSpecificCollection which would aggregate objects of class Element. You can then either:

  1. declare two classes: SomeSpecificCollection and Element - bad, because the name "Element" is general enough in order to cause a possible name clash

  2. introduce a namespace someSpecificCollection and declare classes someSpecificCollection::Collection and someSpecificCollection::Element. No risk of name clash, but can it get any more verbose?

  3. declare two global classes SomeSpecificCollection and SomeSpecificCollectionElement - which has minor drawbacks, but is probably OK.

  4. declare global class SomeSpecificCollection and class Element as its nested class. Then:

    • you don't risk any name clashes as Element is not in the global namespace,
    • in implementation of SomeSpecificCollection you refer to just Element, and everywhere else as SomeSpecificCollection::Element - which looks +- the same as 3., but more clear
    • it gets plain simple that it's "an element of a specific collection", not "a specific element of a collection"
    • it is visible that SomeSpecificCollection is also a class.

In my opinion, the last variant is definitely the most intuitive and hence best design.

Let me stress - It's not a big difference from making two global classes with more verbose names. It just a tiny little detail, but imho it makes the code more clear.

Introducing another scope inside a class scope


This is especially useful for introducing typedefs or enums. I'll just post a code example here:

class Product {
public:
    enum ProductType {
        FANCY, AWESOME, USEFUL
    };
    enum ProductBoxType {
        BOX, BAG, CRATE
    };
    Product(ProductType t, ProductBoxType b, String name);

    // the rest of the class: fields, methods
};

One then will call:

Product p(Product::FANCY, Product::BOX);

But when looking at code completion proposals for Product::, one will often get all the possible enum values (BOX, FANCY, CRATE) listed and it's easy to make a mistake here (C++0x's strongly typed enums kind of solve that, but never mind).

But if you introduce additional scope for those enums using nested classes, things could look like:

class Product {
public:
    struct ProductType {
        enum Enum { FANCY, AWESOME, USEFUL };
    };
    struct ProductBoxType {
        enum Enum { BOX, BAG, CRATE };
    };
    Product(ProductType::Enum t, ProductBoxType::Enum b, String name);

    // the rest of the class: fields, methods
};

Then the call looks like:

Product p(Product::ProductType::FANCY, Product::ProductBoxType::BOX);

Then by typing Product::ProductType:: in an IDE, one will get only the enums from the desired scope suggested. This also reduces the risk of making a mistake.

Of course this may not be needed for small classes, but if one has a lot of enums, then it makes things easier for the client programmers.

In the same way, you could "organise" a big bunch of typedefs in a template, if you ever had the need to. It's a useful pattern sometimes.

The PIMPL idiom


The PIMPL (short for Pointer to IMPLementation) is an idiom useful to remove the implementation details of a class from the header. This reduces the need of recompiling classes depending on the class' header whenever the "implementation" part of the header changes.

It's usually implemented using a nested class:

X.h:

class X {
public:
    X();
    virtual ~X();
    void publicInterface();
    void publicInterface2();
private:
    struct Impl;
    std::unique_ptr<Impl> impl;
}

X.cpp:

#include "X.h"
#include <windows.h>

struct X::Impl {
    HWND hWnd; // this field is a part of the class, but no need to include windows.h in header
    // all private fields, methods go here

    void privateMethod(HWND wnd);
    void privateMethod();
};

X::X() : impl(new Impl()) {
    // ...
}

// and the rest of definitions go here

This is particularly useful if the full class definition needs the definition of types from some external library which has a heavy or just ugly header file (take WinAPI). If you use PIMPL, then you can enclose any WinAPI-specific functionality only in .cpp and never include it in .h.

dennisfen
  • 15
  • 5
Kos
  • 70,399
  • 25
  • 169
  • 233
  • 3
    `struct Impl; std::auto_ptr impl;` This error was popularized by Herb Sutter. Don't use auto_ptr on incomplete types, or at least take precautions to avoid wrong code being generated. – Gene Bushuyev Dec 31 '10 at 20:00
  • @Gene: What do you mean "wrong code"? I was not aware `auto_ptr` didn't work on incomplete types... – Billy ONeal Dec 31 '10 at 20:39
  • 2
    @Billy ONeal: As far as I am aware you can declare an `auto_ptr` of incomplete type in most implementations but technically it is UB unlike some of the templates in C++0x (e.g. `unique_ptr`) where it has been made explicit that the template parameter may be an incomplete type and where exactly the type must be complete. (e.g. use of `~unique_ptr`) – CB Bailey Jan 01 '11 at 11:29
  • 2
    @Billy ONeal: In C++03 17.4.6.3 [lib.res.on.functions] says "In particular, the effects are undefined in the following cases: [...] if an incomplete type is used as a template argument when instantiating a template component." whereas in C++0x it says "if an incomplete type is used as a template argument when instantiating a template component, unless specifically allowed for that component." and later (e.g.): "The template parameter `T` of `unique_ptr` may be an incomplete type." – CB Bailey Jan 01 '11 at 11:33
  • Just for the avoidance of confusion these are the practical issues that I know about. The common practical error with `auto_ptr` pimpl is to fail to provide a user-declared destructor meaning that where the compiler generates one the type will almost always be incomplete. Other errors include not preventing copying or assignment (or providing user-defined copy constructor and copy assignment operator to do it properly). This can be achieve by using `const auto_ptr`. Also the destructor almost certainly shouldn't be virtual. – CB Bailey Jan 01 '11 at 12:57
  • @Charles: So you're saying it's possible that the compiler won't generate a correct call to the destructor? – Billy ONeal Jan 01 '11 at 15:58
  • @Billy ONeal: I'm just saying it's possible that the implementation of `auto_ptr` relies on the template argument being a complete type at the point at which you declare an `auto_ptr` object. TBH, I find it easier to think of a situation where the declaration might cause a compiler error rather than just the destructor call being wrong at a later point. As it's possible to implement `auto_ptr` without this dependency I don't see a reason not to; it's just not required by the standard. – CB Bailey Jan 01 '11 at 16:22
  • The destructor almost certainly should be virtual. Destructors should always be virtual iirc. – Miles Rout Mar 16 '13 at 03:17
  • 1
    @MilesRout That's way too general. Depends on whether client code is allowed to inherit. Rule: If you are certain that you won't delete through a base class pointer, then the virtual dtor is completely redundant. – Kos Mar 16 '13 at 11:04
  • 1
    You can't force others not to inherit anyway, and you should never try to enforce a "no inheriting" rule. – Miles Rout Mar 16 '13 at 23:50
  • 1
    Java has `final`, C# has `sealed` for exactly that purpose; in C++ you can [do hacks](http://stackoverflow.com/questions/2184133/prevent-class-inheritance-in-c) or simply write a doc comment that says so. If you're doing API design (esp. with forward compatibility in mind), you should only allow inheritance where there's a good reason to do so. – Kos Mar 18 '13 at 09:14
  • Rule of thumb is "design and document for inheritance or disallow it", please refer to the google tech talk "How to Design a Good API & Why it Matters" by Joshua Bloch (part 3, classes) – Kos Mar 18 '13 at 09:20
  • OK so how to do it correctly if the `auto_ptr` trick is wrong? – SasQ Mar 31 '14 at 09:36
  • @SasQ nowadays you'd use `unique_ptr` instead and [Charles mentioned](http://stackoverflow.com/questions/4571355/nested-classes-c/4571878#comment5019805_4571878) that it's safe to use with undefined types. – Kos Apr 02 '14 at 11:49
  • @Kos Incomplete, not "undefined". Also, about your earlier comment, now C++ has `final` for class declarations too. And I agree that the statement "Destructors should always be virtual" is absurdly wrong. But the same user has insisted elsewhere that C++ is not an OOP language, _so_. – underscore_d Sep 11 '16 at 10:06
  • 1
    In "Introducing another scope inside a class scope" example is better to make enums like `Type` and `BoxType`. One advantage of using nested classes is that not necessary to repeat the scope like `Product::ProductType` is less verbose and more readable to use `Product::Type` – Isaac Pascual Apr 11 '17 at 08:45
  • 2
    @IsaacPascual aww, I should update that now that we have `enum class`. – Kos Apr 11 '17 at 09:00
26

I don't use nested classes much, but I do use them now and then. Especially when I define some kind of data type, and I then want to define a STL functor designed for that data type.

For example, consider a generic Field class that has an ID number, a type code and a field name. If I want to search a vector of these Fields by either ID number or name, I might construct a functor to do so:

class Field
{
public:
  unsigned id_;
  string name_;
  unsigned type_;

  class match : public std::unary_function<bool, Field>
  {
  public:
    match(const string& name) : name_(name), has_name_(true) {};
    match(unsigned id) : id_(id), has_id_(true) {};
    bool operator()(const Field& rhs) const
    {
      bool ret = true;
      if( ret && has_id_ ) ret = id_ == rhs.id_;
      if( ret && has_name_ ) ret = name_ == rhs.name_;
      return ret;
    };
    private:
      unsigned id_;
      bool has_id_;
      string name_;
      bool has_name_;
  };
};

Then code that needs to search for these Fields can use the match scoped within the Field class itself:

vector<Field>::const_iterator it = find_if(fields.begin(), fields.end(), Field::match("FieldName"));
JTIM
  • 2,774
  • 1
  • 34
  • 74
John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • Thank You for the great example and comments though I am not quite aware with STL functions. I notice that the constructors in match() are public. I assume constructors need not always be public in which case it can't be instantiated outside the class. – bespectacled Dec 31 '10 at 19:22
  • 1
    @user: In the case of an STL functor, the constructor does need to be public. – John Dibling Dec 31 '10 at 19:41
  • 1
    @Billy: I *still* have yet to see any concrete reasoning why nested classes are bad. – John Dibling Dec 31 '10 at 19:55
  • @John: All coding style guidelines come down to a matter of opinion. I listed several reasons in several comments around here, all of which (in my opinion) are reasonable. There's no "factual" argument that can be made so long as the code is valid and invokes no undefined behavior. However, I think the code example you put here points out a big reason why I avoid nested classes -- namely the name clashes. – Billy ONeal Dec 31 '10 at 20:37
  • @user552127: If the constructor isn't public, then only `friend` s can instantiate the class. – Billy ONeal Dec 31 '10 at 20:38
  • @Billy: The name clash "problem" you speak of is a red herring. The exact same issue would arise with an externally-defined class. – John Dibling Dec 31 '10 at 20:42
  • @Billy: If its just your opinion that nested classes are bad, and you have no technical foundation for that opinion, then that's you perrogative. I might argue that without technical reasoning that your opinion is more a whim than anything else, but you're entitled to it. But your opinion should have been posted as an answer on its own rather than commenting on other people's answers so that if someone thinks you're wrong, they can engage you on it. – John Dibling Dec 31 '10 at 20:47
  • @John: All coding standards are effectively opinion. There's no "technical" reason to prefer inlines to macros, nor is there a "technical" reason to indent code consistently. But we do those things because they make code easier to understand. I did not put my opinion in an answer because the OP asked for how nested classes operate, not whether or not they should be used. Therefore what I said does not belong in an answer. – Billy ONeal Dec 31 '10 at 22:07
  • 1
    Of course there are technical reasons to prefer inlines to macros!! – Miles Rout Mar 16 '13 at 03:18
  • @Billy ONeal: Not only `friend`s. Also member functions of the same class, e.g. a static factory method. – SasQ Mar 31 '14 at 09:42
16

One can implement a Builder pattern with nested class. Especially in C++, personally I find it semantically cleaner. For example:

class Product{
    public:
        class Builder;
}
class Product::Builder {
    // Builder Implementation
}

Rather than:

class Product {}
class ProductBuilder {}
Yeo
  • 11,416
  • 6
  • 63
  • 90
  • Sure, it will work if there is only one build but will get nasty if there is a need to have multiple concrete builders. One should be carefully make design decisions :) – irsis Jun 26 '18 at 08:26
1

I think the main purpose of making a class to be nested instead of being just a friend class is the ability to inherit nested class within derived one. Friendship is not inherited in C++.

-3

You also can think about first class ass type of main function, where You initiate all needed classes to work togheter. Like for example class Game, initiate all other classes like windows, heroes, enemy's, levels and so on. This way You can get rid all that stuff from main function it self. Where You can create obiect of Game, and maybe do some extra external call not related to Gemente it self.

maxim
  • 31
  • 2