3

I have many abstract classes that describe many abstract ideas and objects. These classes have many complex relationships with each other, and I realize that while writing code with some of the derived classes can get confusing. For example, it isn't completely clear at first glance if class C is derived from class A, or if it derives from class B.

This is just an example and doesn't relate to the actual application:

These four classes describe 4 different types of vehicles,

  • Ground

  • Air

  • Water

  • Space

Now I'm hoping to declaring my derived classes inside the base class so I can specify the class by doing this:

class Ground {
    class HondaAccord;
}

// Now I know that HondaAccord is for certain a ground vehicle
Ground::HondaAccord car();

While for something as simple as this it's not really worth the increased clutter, In my opinion it makes it clearer and explicit in my specific codebase. Plus I could declare the derived classes here and defining the functions elsewhere so I wouldn't need to import a HondaAccord.h or anything when using the HondaAccord class.

It is my understanding that the nested class is effectively the same as a normal class. I was hoping to get more information from more experienced people to understand if there are potential issues down the line that I'm not seeing.

Thanks

5
  • 2
    Rather than fully defined trivial examples give us some realistic using code. Commented Jun 2, 2023 at 0:19
  • 1
    This nesting scheme encodes information about attribute, but what about others? What if a need arises to distinguish between rented, leased or owned vehicles? You can't indicate both. In any case, I suspect this is a sign of a poor use of inheritance.
    – Alexander
    Commented Jun 2, 2023 at 1:24
  • @candied_orange The realistic looking code would be to just replace Ground with Gameobject and HondaAccord with Ship - nothing really more to it. So a ship is a type of Gameobject which has a position and velocity in space. I didn't want to complicate my question if I could avoid since I didn't think it mattered what those two mean in the context of my larger project. If it's still confusing I could elaborate more in an edit, but I hope you understood the gist of what I'm trying to say.
    – nreh
    Commented Jun 2, 2023 at 1:32
  • You say '"It isn't completely clear at first glance if class C is derived from class A, or if it derives from class B." A classes inheritance is literally in the first line of its declaration, and will be displayed in a mouseover text or such by any decent IDE. Can you expand on why it doesn't appear clear in your application ?
    – nick
    Commented Aug 3, 2023 at 7:19
  • 1
    @nick So for this project, I'd probably have dozens and dozens of different subclasses each inheriting from different abstract classes. To prevent confusion (mostly for myself) I wanted it to be more visible at a glance without having to hover over it using my IDE. I guess it's mostly a personal preference thing for me since I get overwhelmed looking at dense code sometimes and wanted to try this way out to see if it could help.
    – nreh
    Commented Aug 3, 2023 at 20:48

3 Answers 3

7

This is a very bad idea, because it breaks the Open/Closed Principle:

  • Classes should be open for extension, but closed for modification. This means that it should be easy to "extend" your abstract classes (e.g. Ground) via inheritance, without modifying them.

  • Unfortunately, your nesting strategy would force you to modify your abstract class for every new specialisation that you add (e.g. HondaAccord)

If you want a namespace, simply use C++ namespace: they are designed for this purpose and provides features that make naming and nesting easy and practical.

7
  • 1
    Your answer is right, and if one needs to follow the OCP here, this may become problematic. But the OCP is not an end in itself; often it is ok not stick to it. If it is a good or bad idea is always context-dependend.
    – Doc Brown
    Commented Jun 2, 2023 at 8:59
  • 1
    @DocBrown thanks. In the end, it's about using the right tools. Of course, one can take a hammer to make a head out of a screw, but with a screwdriver the result is usually better, especially in the long run. So is it about C++ namespaces when it's about managing namespaces ;-)
    – Christophe
    Commented Jun 2, 2023 at 16:21
  • 1
    @nreh if you modify Ground.h, you'll need to recompile all the files that include this header. If you dont't modify it, you'll only need to recompile those including HondaAccord.h. The point is even worse, everytile you lodify one of the nested class, you'll have to compile all the units needing any of these classes. And recompilation is only one part of the problem. In principle you'd also need to retest as well.
    – Christophe
    Commented Jun 2, 2023 at 21:33
  • 1
    @nreh: the OCP makes most sense when you are writing reusable libs and frameworks containing a base class like Ground, and someone else who cannot change your code tries to reuse that lib and wants to add some more ground vehicles in their own code. If, however, someone tells "not following the OCP is a bad idea" without any constraints, I am always very sceptical if the cost/effort relationship is right.
    – Doc Brown
    Commented Jun 2, 2023 at 21:50
  • 1
    ... Hence I think a better first sentence for this answer would have been *"This is a very bad idea in case you are trying to follow the Open/Closed Principle" (vs. because it breaks the OCP). In the current form, however. it gives the wrong impression "the OCP is always good, not following is bad" - and that is IMHO misleading nonsense.
    – Doc Brown
    Commented Jun 2, 2023 at 21:57
2

C++ supports nested classes, so from a purely syntactically point of view, you might do this.

Not having to write #include HondaAccord.h can be achieved by bundling includes in one one header file also for non-nested classes, so this is no valid argument.

If you are fine that this forces one to write Ground::HondaAccord instead if HondaAccord, or Gameobject::Ship instead of Ship is somewhat opinionated and depends on the context. If the codebase will end up with a lot of snippets like Ground::HondaAccord or Ground::VWPassat or Ground::Fiat500, then this might become tedious. Of course, may have use some typedef to avoid frequent repetition of the base class name. If this code only appears only inside some factory, however, then it probably won't matter much if you nest the classes or not.

Imagine you note afterwards that Ground was a misleading name, and it should have been GroundVehicle instead. Are you ok to change this whereever it occurs in the code? It might also depend on your refactoring tools, the total resulting length of the real names, and the necessity to repeat the information that HondaAccord, VWPassat and Fiat500 are ground vehicles (which might also be seen as superfluous).

TLDR; it can be ok or not - it depends.

2
  • Huh I didn't think of your second point. Being able to write using namespace Ground is actually really useful. Thanks!
    – nreh
    Commented Jun 2, 2023 at 20:27
  • 1
    @nreh: I think I have to correct my answer a little bit - one can use some typedef to achieve the same for nested classes.
    – Doc Brown
    Commented Jun 2, 2023 at 21:52
1

Plus I could declare the derived classes here and defining the functions elsewhere so I wouldn't need to import a HondaAccord.h or anything when using the HondaAccord class.

The example you've shown is just a declaration of HondaAccord, if you include only that header the type HondaAccord is incomplete, so you are hiding that it is derived from Ground. Your example doesn't work, because you can't instantiate an object of incomplete type.

It is my understanding that the nested class is effectively the same as a normal class.

Whilst a class's definition is a namespace, it's more common to use an actual namespace for that purpose.

1
  • Ah yeah, I realized that my example is flawed. What I originally meant was that I would declare the class here with all things that would normally be put in HondaAccord.h inside Ground.h. I didn't mean forward declare.
    – nreh
    Commented Jun 2, 2023 at 20:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.