📜 ⬆️ ⬇️

Without knowing the ford, do not go into the water. Part one

Without knowing the ford, do not go into the water
I wanted to write a few small notes about how C / C ++ programmers play with fire, unaware of it. The first note will be about trying to explicitly call the constructor.

Programmers are lazy creatures. Therefore, they strive to solve the problem with a minimum amount of code. This is a commendable and good pursuit. The main thing is not to get carried away by the process and stop in time.

For example, programmers may be too lazy to create a single initialization function in a class in order to then call it from different constructors. The programmer thinks: “Why do I need an extra function? I'd rather call one constructor from another. ” Unfortunately, the programmer doesn’t always solve this simple problem. To detect such unsuccessful attempts, I am implementing a new rule in PVS-Studio right now. Here is an example of the code I found in the eMule project:
  class CSlideBarGroup
 {
 public:
   CSlideBarGroup (CString strName,
     INT iIconIndex, CListBoxST * (pListBox);
   CSlideBarGroup (CSlideBarGroup & Group);
   ...
 }

 CSlideBarGroup :: CSlideBarGroup (CSlideBarGroup & Group)
 {
   CSlideBarGroup (
     Group.GetName (), Group.GetIconIndex (), Group.GetListBox ());
 } 

Consider carefully the implementation of the last constructor. The programmer decided that the code
  CSlideBarGroup (
   Group.GetName (), Group.GetIconIndex (), Group.GetListBox ()); 

just calls another constructor. Nothing like this. Here a new unnamed object of the CSlideBarGroup type is created and immediately destroyed.

It turns out that the programmer really called up another constructor. That's just what he did is not what he intended. Class fields will remain uninitialized.
')
Such mistakes are only half the trouble. Some people know how to really call another constructor. And cause. It would be better if they did not know how to do it. :)

For example, the above code could be rewritten as:
  CSlideBarGroup :: CSlideBarGroup (CSlideBarGroup & Group)
 {
   this-> CSlideBarGroup :: CSlideBarGroup (
     Group.GetName (), Group.GetIconIndex (), Group.GetListBox ());
 } 

or so:
  CSlideBarGroup :: CSlideBarGroup (CSlideBarGroup & Group)
 {
   new (this) CSlideBarGroup (
     Group.GetName (), Group.GetIconIndex (),
     Group.GetListBox ());
 } 

Now, really, one constructor to initialize data calls another constructor.

If you see a programmer who does this, hang him one shelchan from his head and one from me personally.

The above examples are very dangerous code, and you need to understand how they work!

Due to the petty optimization (laziness to write a separate function), this code can do more harm than good. Let us consider in more detail why such structures sometimes work, but more often not.
  class SomeClass
 {
   int x, y;
 public:
   SomeClass () {new (this) SomeClass (0,0);  }
   SomeClass (int xx, int yy): x (xx), y (yy) {}
 }; 

This code will work correctly. The code is safe and works because the class contains simple data types and is not inherited from other classes. In this case, a double constructor call does not threaten anything.

Consider another code where an explicit constructor call results in an error (an example is taken from the discussion on the StackOverflow site):
  class Base 
 { 
 public: 
  char * ptr; 
  std :: vector vect; 
  Base () {ptr = new char [1000];  } 
  ~ Base () {delete [] ptr;  } 
 }; 
 
 class Derived: Base 
 { 
   Derived (Foo foo) {} 
   Derived (Bar bar) { 
      new (this) Derived (bar.foo); 
   } 
 } 

When we call the constructor “new (this) Derived (bar.foo);”, the Base object is already created and the fields are initialized. Recalling the constructor will result in double initialization. In 'ptr' write the pointer to the newly allocated memory. The result is a memory leak. What the double initialization of an object of type std :: vector will lead to is generally difficult to predict. One thing is clear. This code is invalid.

Conclusion


An explicit constructor call is required only in extremely rare cases. In normal programming, an explicit constructor call usually occurs due to the desire to reduce the size of the code. Do not do this! Create an ordinary initialization function.
  Here is what the correct code should look like:
 class CSlideBarGroup
 {
   void Init (CString strName, INT iIconIndex,
             CListBoxST * (pListBox);
 public:
   CSlideBarGroup (CString strName, INT iIconIndex,
                  CListBoxST * pListBox)
   {
     Init (strName, iIconIndex, pListBox);
   }
   CSlideBarGroup (CSlideBarGroup & Group)
   {
     Init (Group.GetName (), Group.GetIconIndex (),
          Group.GetListBox ());
   }
   ...
 }; 

Source: https://habr.com/ru/post/137039/


All Articles