Last week I conducted a series of 5 online lectures about C++ for embedded applications. [You can still access the materials and recordings from this site.] As with any such activity, although I hope that I am imparting information and advice, I also expect to get questions and ideas back. I was not disappointed. There were many smart questions that made me think [= good!]. There were no dumb questions that I know of, as the only dumb question is the one that you fail to ask.
It got particularly interesting when C++ constructors and destructors get mixed up with pointers …
First off, there is a little confusion about the difference between the scope of a variable/object and its actual existence. For “existence”, read “validity” because the memory occupied by a variable never goes away; the time at which the memory is actually allocated to the variable varies. I will illustrate this point with some C code.
In this code, the array arr has a scope which is limited to inside the function fun(). The memory it uses is also allocated at the start of this function and deallocated when the code returns. However, this code, though syntactically correct, has a glaring error: it returns a pointer to arr so that the code in function f2() can access the array after the return from fun(). But, since this memory has now been deallocated, the assignment to p is not valid and could produce an “interesting” error. Remember that pointers, though a powerful language feature in C/C++, are the most common source of errors.
A small change to the code rectifies this fault.
By declaring arr to be static the time frame of its existence is changed. The memory is now allocated at build time so it is in existence before, during and, most importantly, after the execution of fun() so the assignment to p is now valid. The scope of arr is unchanged: it is only accessible inside fun().
All of this would be equally true in C++, but we have the additional issue of constructor and destructor functions, which are automatically executed when an object is created and destroyed. Here is some similar code in C++.
This is much the same as the first example, except that we have an object of type ccc instead of an int. Some key things do not change: the object z still has a scope and existence that is confined to the function fun(). However, there is now the question of the constructor and destructor functions for z. These are run when the object is created and when it is destroyed, which occurs on return from fun(). So, any subsequence access to the object, via the returned pointer, is a problem because the destructor will have deallocated all the object’s resources.
In the same way as with the C example, the object could be made static and all would be well.
There is another option: use dynamic memory allocation to create the variable/object; the deallocation of resources is then under the programmer’s control. In C, this would require the use of malloc(), but in C++ it may be done more neatly using new.
The constructor for the created ccc object is executed when the new is invoked. The destructor is executed at some later time when the corresponding delete is applied. This approach has the advantage of giving control over the running of constructor/destructor, but their automatic execution on scope helps to avoid memory leak style errors.