For ten years I have been writing in C ++. The first five years my task was to write effective code for computer games, then the main requirement was stability, since it was about automation of industrial facilities. I would like to share my personal experience and practical work that will help create effective and at the same time stable programs.

The material is divided into parts, from the most common practical rules, interesting for beginners, to specific issues relevant to experienced programmers.
In the first part I will give in practice my answer to the most basic questions. How to write a program in general, especially a complex program? What should be done at the very beginning, so as not to redo it from scratch? How to create an optimal program structure?
So, colleagues, we write in C ++. As a rule, this means that our programs are complex. Some are very complex. For the program to work as I need it, its creator, you need to keep in mind how it works, down to the smallest details. This is a necessary condition, since all that could not be fully thought out is guaranteed bugs.
It’s impossible to keep the whole picture in my head at all levels of abstraction. Therefore, I divide the complex system into simpler pieces. Each of them is well understood and obvious, like a nail. Everything that is more complicated is assembled from more simple elements. Obviously? Of course! But you must strictly follow this rule.
')
If I write a program that uses an unprocessed technology or element, I first do a sketch. A small console program that contains the desired piece. I torture this study until blue in the face, until I clearly understand how and what it works, what are the restrictions on the input data, what are the features and pitfalls. Yes, I have a whole directory of several hundred sketches. Everyone is eventually brought to an independent piece of code (most often, a class or a bunch of classes), which I insert into the program as a battery: I pulled out the old version - stuck a new one.
“Gluing together” elements is sometimes a rather complicated mechanism in itself, and therefore requires a separate etude. But this is not enough when it comes to complex mechanisms. All the complex algorithms I write down on paper. It is impossible to write in C ++ what you cannot describe in simple Russian. When you describe something with words, the brain stumbles upon pitfalls and inconsistencies. They become visible at the very beginning, and not at the end, when the code is already written. The text itself must be polished, thin places - mentioned. Another plus: you can return to the project in a year and don’t spend weeks on remembering why it was done this way.
For each customer, I started on the notebook. They contain all non-trivial moments, from creating realistic foam around a sailing frigate to calculating the optimal trajectories of cranes on a narrow patch around hot metals and acids. If I meet a similar problem, I just look in the notebook. There, among the crossed-out texts and shemeks, in 10-15 minutes I reproduce in my memory the entire course of creating an algorithm with all its features. As a rule, the tasks are not repeated completely, but some idea turns out to be necessary after three to four years.
Obviously? Fine! Let's slightly complicate and look inside the elements that I mentioned above ...
C ++ is very simple in the sense that C ++ classes often repeat the description of objects in the real world (not in the sense of direct copying, but in the sense of names, partially in functionality and relationships, moreover, in C ++ there are synthetic auxiliary classes) . When a real-world object is or contains another real-world object (only one), class inheritance is used in C ++. If a child object can contain several of the same type, then we use the inclusion of objects in the child class. We distinguish the same type of classes in a separate class (which I will call the mixing class). All this is perfectly painted by Alain Golub [2], so I will not repeat the masterfully written text. I can only say that this simple mnemonic rule should be remembered and applied on the machine.
A much less canonical question is the distribution of access rights to members and functions of a member of a class. Everyone knows about encapsulation, everyone reads books. What to do with all this in practice?
In practice, the following happens. I make a class Bunker that describes the real bunker to which the sensors are connected. Bunkers are combined in the shop, all this is displayed in a beautiful interface, somehow interacting with each other and with the person.
The question is, how much inside information about the bunker have I discovered for other classes? The answer is: nothing at all. Do you understand? All class members are private. No one outside should know what is inside the class. The reason is clear: I changed something in the classroom - and the chain of changes stretched all over the code, which was clean and debugged like a Swiss watch a second before.
So, the Bunker does not give out the details of its implementation. At the same time, he himself displays himself into the interface, he himself handles clicks on his controls, he himself does all the work that requires knowledge of how it works. Outside, there are member functions that ask the class to do some work: put yourself in the interface, process the mouse click, save yourself in XML, etc.
You might ask: it might not be so difficult, why not make the classes more open and friendly? “In no case” - I will answer, - and this is why.
It takes two months for the program to work, and the customer asks me to do remote control of the bunker. Then remote control is required via different communication channels - from the Internet to the cellular network and the serial port, and the communication channels should be duplicated. Time passes, the program works, and the need for remote synchronization of operator action logs is gradually becoming clear. And after a couple of months, I find out that it is necessary to do an incremental synchronization of the state of the bins and the log when there is a bad connection (for example, noisy RS-485 or Wi-Fi over long distances).
What I've done? I have complicated the Bunker class, which now works either locally or remotely. Added another code that picks up the GSM / Ethernet / RS-232 settings. And most importantly: everything else remains as it was. All the months of work on fine-tuning control algorithms remained with me! The same applied to the magazine, and to everything else.
If I brought out at least something from the Bunker’s internal kitchen, I’m even afraid to imagine how much I’d ​​have to rewrite from the 250 KB of control code.
I repeat once again an important point: the structure of a program in C ++ differs from C in exactly the opposite. There we had structures and common variables. Here we have objects that are black boxes. Each object is itself a reaper, a Swede, a igrets on a dude.
On the other hand, the class code itself should be simple. If complex functionality is required, I implement it by including it in the class of internal helper objects, or I use mixing classes (but more cautiously, since inheritance is a stronger form of linking than inclusion).
I remind you that get / set access is a slightly more veiled version of an open class member. With this approach, we are not on the way.
The right way to interact with an object is to ask it to do an action.
Instead of requesting data, we ask the object to do the necessary action on its own. This is a necessary condition for the stability of large C ++ projects. In some cases, the implementation of an action is simply to return the value of a class member (the exception that confirms the rule).
And the last. Any structure has limited growth potential. Perhaps the customer will request such global changes that it will be necessary to change the interaction interface between the classes. Moreover, it may be necessary to change the structure of the classes. But this is not all: there is an objective time frame for running the program, during which it becomes obvious the need to create a new class structure.
I want to reassure: everything is perfectly normal. Use the program - it means the program lives, and therefore its structure is changing and developing. Before the program becomes really good, it will almost certainly have to be rewritten several times (my most serious programs, as a rule, are rewritten twice or three times). Such a program has excellent growth potential, it no longer suffers from "childhood diseases" and, as a rule, works like that same Swiss watch. And all the achievements from the previous version are transferred the simpler, the more isolated the original classes were.
Literature:
- Bjarne Stroustrup. C ++ programming language.
- Alain I. Golub "A rope of sufficient length to ... shoot yourself in the foot."
In the next part: I say goodbye to the problems of new / delete, get rid of the need for smart pointers and garbage collectors, replacing them with a simpler and more natural way of working with resources.