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
3 Answers 3
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.
-
1Your 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– Doc Brown2023年06月02日 08:59:49 +00:00Commented 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– Christophe2023年06月02日 16:21:13 +00:00Commented 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– Christophe2023年06月02日 21:33:05 +00:00Commented 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– Doc Brown2023年06月02日 21:50:26 +00:00Commented 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– Doc Brown2023年06月02日 21:57:37 +00:00Commented Jun 2, 2023 at 21:57
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.
-
Huh I didn't think of your second point. Being able to write
using namespace Ground
is actually really useful. Thanks!nreh– nreh2023年06月02日 20:27:10 +00:00Commented 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– Doc Brown2023年06月02日 21:52:51 +00:00Commented Jun 2, 2023 at 21:52
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.
-
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
insideGround.h
. I didn't mean forward declare.nreh– nreh2023年06月02日 20:23:26 +00:00Commented Jun 2, 2023 at 20:23
Explore related questions
See similar questions with these tags.
Ground
withGameobject
andHondaAccord
withShip
- 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.