I think that the debate "C vs C++" will end when the two langages died, and each one have its advantages and inconvenients, the choice of one instead of another depend on the application context.
Recently i read a famous linus torvalds opinion about C++, where he wrote:
"C++ is a horrible language. It's made more horrible by the fact that a lot of substandard programmers use it, to the point where it's much much easier to generate total and utter crap with it. Quite frankly, even if the choice of C were to do *nothing* but keep the C++ programmers out,that in itself would be a huge reason to use C."
I decide to do a quick analysis of Linux using CppDepend and try to understand the Linus opinion.
CppDepend can analyse Visual Studio projects without any configuration, but if the build process is described by another build processing system like Makefile, SCONS, CMake or others, the solution is to use the ProjectMaker to describe the project to be analyzed.
For Linux you use ProjectMaker to specify files to analyze and also include path and defines.
Linux code source contains many directories, and to have a clear vision of code source we will associate each directory to a project.
And for each project we can specify other informations:
C vs C++ comparison
Modularity:Physical vs Logical
Modularity is a software design technique that increases the extent to which software is composed from separate parts , you can manage and maintain modular code easily.
We can modularize a project with two approachs:
- physicaly: by using directory and files, this modularity is provided by the OS and can be applied to any langage
- logicaly: by using namespaces,component,classes,structs and functions, this technique depends on the langage capabilties.
when we develop with C, and to package our code we use essentialy physical modularity, for example for Linux each module is isolated in directory and each file contains structs,variables and methods relative to a specific functionality.
This dependency graph shows relation between some linux directories:
C++ instead of C can use namespaces and classes to modularize the code, thoses types are provided by the langage, and for the previous graph we can use namespaces to modularize our code instead of directories.
C++ provide an efficient way to modularize the code base, but why namespaces artifacts are not enough used? for example only some C++ open source projects use it, maybe it's due to historical reason or maybe we use "C with Classes" where the C habits are all the time present instead of modern C++.
What the difference between the two approachs?
Lisibility : the logical approch is better, because we can understand and use easily the code, and we dont need to know the physical emplacement of a code element.
Managing changes: a good design need in general many iterations, and for the physical approch the impact of design changements can be very limited than logical one, indeed we need only to move function or variable from a file to another, or move file from directory to another.
However for C++ it can impact a lot of code because the logical modularity is implemented by the langage artifacts and a code modification is needed.
Encapsulation:Class vs File
For C++ the encapsulation is defined as the process of combining data and functions into a single unit called class. Using the method of encapsulation, the programmer cannot directly access the data. Data is only accessible through the functions present inside the class.
For C we can have an encapsulation but using also a physical approach like described in the modularity section, and a class can be a file containing functions and data used by them, and we can limit the accesibility of functions and variables by using "static" keyword.
Linux use this technique to hide functions and variables, to discover that let's search for static function:
SELECT METHODS WHERE IsStatic
and also we can search for static varibles:
SELECT FIELDS WHERE IsStatic
Lisibility:using C++ encapsulation mecanism improve the understanding and lisibility of code, C is low level and use physical approch rather than logical.
Managing changes:if we have to change the place where varibale or function are encapsulated, it can very easy for C, but for C++ it can impact a lot of code.
Polymorphism vs Selection idiom
Polymorphism means that some code or operations or objects behave differently in different contexts.
This technique is very used in C++ projects, but what about C?
for procedural langages the selection techniques by using the keywords "switch", "if" or maybe "goto" can simulate the polymorphism behavior, but this technique tend to increase cyclomatic complexity of code.
Let's search for complex function and see if the polymorphism can minimize the complexity.
the more complex function w9968cf_v41_ioctl use a big switch to differentiate a behavior that depends on a specific command, with C++ we can use polymorphism and command pattern to minimize the complexity of this code.
Lisibility: using Polymorphism permit the isolation of a specific behavior to a class, it improve the visibility of the code.
Managing changes: adding another behavior with polymorphism can implies the adding of another class, however with selection idiomyou can add only another case under the switch statement.
Inheritance vs Agregation
Linux use essentially structs to define data manipulated by functions.
Let's search for all structs used:
SELECT TYPES WHERE IsStructure
but what happened when struct has a common data as another, for exemple many struct can be considered as "inode" with adding some other fields, in this case the agregation is used instead of inheritance, let's search for structs using "inode".
SELECT TYPES WHERE IsDirectlyUsing "inode"
Some structs using inode can be inherited from it, but the agregation technique is used instead.
Lisibility: using inheritance can improve the understanding of data, but we have to be carefull whene using it, its used only for the "Is" relation.
Managing changes: Inheritance implies a high coupling so any changes can impact a lot of code.
C++ provide a better lisibility but any changes or refactoring can be difficult, and Linus talk about this idea of design changement in the same post:
"inefficient abstracted programming models where two years down the road you notice that some abstraction wasn't very efficient, but now all your code depends on all the nice object models around it, and you cannot fix it without rewriting your app."
But doing refactoring need to understand the existing code and design before making changes, and C program is very difficult to understand but easy to change, however C++ can be more lisible than C but need some effort when making changes.
How we can limit the impact of changes for C++?
The good solution to limit the impact of changements is to use patterns, specially low coupling concept to isolate changements only in a specific place, Irrlicht as explained in the previous post is a good example of using low coupling.