REVIEW - C++ Crash Course - A Fast-paced Introduction

Title:

C++ Crash Course - A Fast-paced Introduction

Author:

Josh Lospinoso

ISBN:

9781593278885

Publisher:

No Starch Press (2019)

Pages:

733pp

Reviewer:

Ian Bruntlett

Reviewed:

September 2020

Rating:

5 out of 5

Highly recommended

This book consists of ‘An Overture to C Programmers’ followed by two main parts, ‘The Core Language’ and ‘C++ Libraries and Frameworks’. At first glance, the order in which the language is introduced is somewhat unusual (but good) – it is intended for intermediate to seasoned programmers, so time will tell. Helpfully, most chapters end with a ‘Further Reading’ section. I used this book as a kind of C++17 Primer with examples. In addition, the text often explains example programs in great detail. I am not a C++ expert but I have done my best to verify my findings. No Starch Press kindly provided a physical and electronic copy of this work.

‘An Overture to C Programmers’ tries to sell this book – and C++ in general – to experienced C programmers as a kind of ‘Super C’. It does give an assembler listing to show an optimisation of constexpr but only refers to an x86 book – there are plenty of x64 resources out there and most programmers using Intel/AMD hardware are most likely to be using x64 cores. I think that the examples should have been introduced with the proviso ‘You are not intended to understand this… yet’.

Part 1 covers the core language in 9 chapters and just over 270 pages. It brings in the language concepts at the beginning. For simplicity and familiarity, it uses printf in its examples, rather than iostreams, which I feel is unfortunate.

The first chapter, ‘Up and Running’, explains an example C program, shows how to set up a C++ development environment (Visual Studio, Xcode, gdb). It can get quite involved and, ideally, you’ll either know how to do it already or know someone else that does. It has a section on Bootstrapping C++ that introduces the basic concepts of C++. It finishes with a section on debugging with an example being debugged in the previously mentioned tools.

The next chapter, ‘Types’, is ambitious. It starts with fundamental types, and progresses through arrays and user defined types all the way to fully featured C++ classes – methods, access control, constructors and initialisation, and destructors. It is particularly detailed in its coverage of the different ways to initialise objects, concluding that in general always use {} except for when using certain stdlib classes. Strikingly, it overlooks the protected keyword.

The next chapter, ‘Reference Types’, concentrates on pointers and references and their differences. As is customary in some C++ texts, it recommends declaring a pointer variable as int * my_ptr. This can mislead the reader into thinking that you can declare two pointer variables with int * my_ptr, another_ptr, which is wrong. It illustrates the hazards of sloppy pointer arithmetic. It does cover address space layout randomisation – but surprisingly it doesn’t illustrate physical memory as a sequence of bytes, the machine stack or the free store. The const keyword (arguments, methods, member variables) are dealt with as is the auto keyword. The author recommends “As a general rule, use auto always” – which I believe is a bit extreme. Some examples are highly contrived and, at first glance, got me thinking ‘What are they trying to achieve here?’ but the abundant examples have greatly assisted my learning of C++. One thing bugged me – it kept on referring to C++ idioms as Design Patterns.

The ‘Object Life Cycle’ chapter discusses local, static, thread local and free store variables. It illustrates their behaviour. It gets one thing wrong – in its thread_local example, the relevant variable is never referenced so it got optimised out of existence. Some help from accu-general got it working properly. It also covers how to use and react to exceptions and provides a SimpleString class to show how constructors use exceptions and how destructors are used to release resources. It shows how naive copy semantics cause problems and how move semantics can add a welcome boost in performance. It does mention value categories, something I’ll have to learn more about.

The ‘Runtime Polymorphism’ chapter washes its hands of implementation inheritance. It recommends the use of pure-virtual classes to implement interfaces. It discusses the use of constructor injection and property injection as well.

‘Compile-time Polymorphism’ is all about templates. It covers a lot of ground so some material is a bit thin because of that. I have some experience of putting templates into practice and even so, I found it necessary to re-read this chapter. It covers const_cast<> et al, a function template example, and a smart pointer template example. It also covers the Concepts TS and illustrates how to define your own concepts and the use of type traits as a compromise for situations where Concepts are not available. It recommends other books to build on this foundation.

‘Expressions’. Everyone knows all about expressions, don’t they? This chapter might change your mind. There is the usual operator precedence and associativity with a useful table. Unfortunately, when it comes to order of evaluation advice (on page 196) it is still based on C++11. See [1] for current wisdom. Because, to quote Bjarne Stroustrup’s A Tour of C++ 2nd Edition section 1.4, “The order of evaluation of expressions is left to right, except for assignments, which are right to left. The order of evaluation of function arguments is unfortunately unspecified”. It shows how to overload new and delete and how to implement your own free store management. User defined literals are mentioned briefly. Type conversions are discussed in depth. There is an interesting constexpr example – rgb_to_hsv.

The ‘Statements’ chapter covers basics – the usual stuff plus namespaces, type aliasing, structured bindings, attributes, constexpr if. However, one example shows a range class for Fibonacci numbers, which I thought was particularly welcome.

The ‘Functions’ chapter finishes off Part 1. It seems fairly comprehensive, ranging from simple stuff through to mind-bending stuff (lambdas, templates). Again, the examples were really helpful.

Part 2 of this book (430+ pages) covers many things with chapters on testing, smart pointers, utilities, containers, iterators, strings, streams, filesystems, (stdlib) algorithms, concurrency and parallelism and Boost ASIO (network programming) and odds and ends for writing applications. For a lot of examples, the Catch2 TDD framework is used, to illustrate the behaviour being taught. A mixture of standard library and Boost’s library facilities are covered here.

The ‘Testing’ chapter explores TDD using an extended example of a braking system for an autonomous vehicle. It starts off with simple assertions and then moves onto more ambitious facilities (Catch v2, Google Test, Boost Test). It covers Mocking Frameworks (Google Mock, Hippo Mock).

The ‘Smart Pointers’ chapter covers families of smart pointers (scoped, unique, shared, weak, intrusive). It finishes off with an example of a toy allocator.

The ‘Utilties’ chapter covers “a motley collection of tools”. There are Data Structures (tribool, optional, pair, tuple, any, variant). There is quite a section on Dates and Times and finally Numerics (functions, complex numbers, constants, random numbers, numeric limits and compile time rational arithmetic).

The ‘Containers’ chapter covers a variety of containers from stdlib and Boost. It acts both as a tutorial and as an abbreviated reference. Some parts needed to cover broad topics, so further reading is required. Again, there are useful examples using Catch v2 to illustrate container behaviour.

The ‘Iterators’ chapter covers iterators with some very useful reference tables that lists the operations that different iterator types support.

The ‘Strings’ chapter is over 40 pages long, covering the varieties of std::string, a multitude of methods – including std::string_view and regular expressions from both stdlib and Boost.

The ‘Streams’ chapter is interesting but I feel that the later examples would be better understood after reading other C++ books.

The ‘Filesystems’ chapter covers stdlib and Boost facilities that provide platform agnostic facilities for interacting with the kinds of hierarchical provided by Linux and Windows and other operating systems. It covers a forest of facilities, not always supported by every platform – it can be difficult to separate the wood from the trees.

The ‘Algorithms’ chapter covers a big topic, is a large chapter with Catch v2 examples of algorithms in action, divided into the usual categories – non-modifying sequence operations etc.

The ‘Concurrency and Parallelism’ chapter is a necessarily short coverage of the topic. The examples require an up-to-date toolchain. There is quite a selection of examples to experiment with but I am not familiar enough with this topic to comment further.

The ‘Networking with Boost ASIO library’ chapter is present because the stdlib does not currently support networking. It is interesting but I am out of my depth with this chapter.

The final chapter, ‘Writing Applications’, is an assortment of useful things to know including std::atexit, std::abort, std::system, std::getenv, std::signal, and Boost ProgramOptions for dealing with command line parameters.

Conclusion: This is a comprehensive book, both in size (over 700 pages, taking 5 months to review) and breadth of coverage. It provides suggested reading at the end of most chapters and has a decent index. The examples are built using the CMake cross-platform system which helps a lot.

All in all, highly recommended.

References

[1] ‘Refining Expression Evaluation Order for Idiomatic C++’:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0145r3.pdf