Software Design (C++) : Q & A
This page contains answers to students' questions that are considered to be interesting in general. Updated every now and then :)
- Why do C++ compilers allow the use of uninitialized variables like in C? There is actually a bigger question here: why is C++ backwards compatible with C? The compatibility is not 100% but very near (see e.g. Ch. 27 in the course text book). The reason is that it has been considered very important to be able to compile legacy code that is written with C and old versions of C++ (or their mix). C++ and C are compiled languages (compiled directly to machine language of the target machine) so you in most cases need to recompile the whole code of a program when making changes or when porting to a new environment etc. Look here for more information on how to mix C and C++ code. The book The Design and Evolution of C++ also discusses C compatibility at length.
-
Can I somehow make a std::vector that could hold a collection of heterogeneous objects (= that have different types)? Well, there is a way to do this. However, it is against good progamming style with C++ and strongly discouraged. But if you absolutely must (like if you are dealing with some ugly old legacy code), here is how (but please think twice):
- Define a vector that holds pointers to objects. If the objects you need to store have a common base class, you can use pointer to base class as the type of the elements. Then, you can do a run-time checked safe (may throw bad_cast) downcast (dynamic_cast) to convert (pointers to) objects to their true type when accessing them.
- If the above is not possible, you can use pointer-to-void (void *) as the type of the elements. However, you can't use dynamic_cast on void* (not a complete type), so you must use some other cast without any run time checks when accessing the objects through the void pointers.
- Here is example code as HTML (and same as a c++ source file) and the ouput it produces. Examine the code and the comments, and the output. Note that the code in main() does not destroy the objects allocated from the free store, but the memory is freed when the program ends (can be considered bad style!).
- What is this std::cerr? It is the second default output stream attached to a running program by the run time environment (operating system). You can use it exactly like std::cout (except that cerr does not buffer the output but writes it out immediately). You should use cerr for error messages. When executing a program from a console (command line, or, terminal window), the ouput form cout and cerr are directed to the same screen output. You can redirect the output of cerr to a file by executing the program from the command line (or in a script) as: "a.exe 2> errors.txt". Also cout can be redirected to a file by: "a.exe > output.txt 2> errors.txt" (works the same way in Windows and in Linux).
- What if I forget the '[]' from the call to delete an array allocated by 'new T[n]'? See these items in the FAQ at isocpp.org:
-
Sometimes in the code examples the member functions are defned (the function body is shown) directly in the class declaration, sometimes they are put in a separate source code file (a .cpp file) that includes the class declaration (a .h file). When should I use which member function definition style? Both ways are correct from the syntactic point of view but they have different consequences. Stroustrup [Stroustrup 2014, Ch. 9.4.4] gives the following advice: "Writing the definition of a member function within the class definition has three effects:
- The function will be inline; that is, the compiler will try to generate code for the function at each point of call rather than using function-call instructions to use common code. This can be a significant performance advantage for functions that hardly do anything but are used a lot.
- All uses of the class will have to be recompiled whenever we make a change to the body of an inlined function. If the function body is out of the class declaration, recompilation of users is needed only when the class declaration is itself changed. Not recompiling when the body is changed can be a huge advantage in large programs.
- The class definition gets larger. Consequently, it can be harder to find the members among the member function definitions.
The obvious rule of thumb is: Don’t put member function bodies in the class declaration unless you know that you need the performance boost from inlining tiny functions. Large functions, say five or more lines of code, don’t benefit from inlining and make a class declaration harder to read. We rarely inline a function that consists of more than one or two expressions."
See also what ISO C++ FAQ has to say about inlining. - What is the '&&' reference syntax? See the explanation in cppreference: reference declaration. Basically, it is a way to extend the lifetime of a modfiable temporary object. It is also used to make objects 'movable' (a compilation time type trick to select a move constructor or move assigment function).
-
What do the couble colons mean in '::operator delete (ptr);'? The '::<name>' without a namespace name refers to the global namespace. A local definition (e.g. in a class) of 'operator delete()' may hide the global implementation and that's why we make sure we call the globally defined 'operator delete' to free the memory that 'ptr' is pointing to. Classes are also namespaces. Sample code from Stroustrup:
int f(); // global function int g() { int f; // local var iable; hides the global function f(); // error : we can’t call an int ::f(); // OK: call the global function }