How to successfully enforce Scott Meyers´ Effective C++

How to successfully enforce Scott Meyers Effective C++

The Origins of C++

Since its inception in 1985, the C++ programming language has an extensive history in the field of software development.

C++ was originally conceived by Bjarne Stroustrup (a Danish computer scientist) as an extension of the C programming language, which by then was already very widely used, particularly in embedded software development.  

The C programming language was favoured by many in the embedded software space due to its ‘low-level’ connection to underlying systems, its performance and efficiency, and the ability for the programmer to do almost anything that they wanted.

C++ added to and extended this programming freedom, with even greater options and ways to do things more efficiently or quickly.

However, in doing so, it also added complexity for the programmer – which ‘way’ can I best do this, or that – and complexity for those around them – in understanding what has actually been done and how does that relate to the intentions.  

It has been said that if C is the ‘sharp knife’ of programming languages – an extremely useful and powerful tool, but not without risks for the user – then C++ can be considered the sawn-off shotgun. Proceed with caution. 

A Pioneer and Master in His Field – Scott Meyers

Scott Meyers is an American author and software consultant specialising in the computer programming language C++.

He is known for his book series, “Effective C++.” 

His main principle is both simple and profound: it is not enough to be merely syntactically correct – one must also act effectively.

This difference is of paramount importance. While many resources teach the “how” of C++ coding, Scott Meyers focuses on the “why”: 

• Why should certain structures be preferred? 

• Why do specific approaches lead to better and more efficient code? 

• Why can seemingly insignificant decisions result in significant errors? 

“Effective C++” is more than just a book – it represents an entire movement.

Scott Meyers’ goal is to revolutionise the developers’ approach to C++.

His book is not just a guide but an intensive seminar on the art of programming.

The various editions of this series provide a wealth of tips and guidelines aimed at enhancing code quality, maintainability, and performance. 

The info on Scott Meyers´ effective C++ is his intellectual property and has nothing to do with Emenda. 

Scott Meyers’ First Principle 

Regardless of your individual programming experience, you must first become familiar with how C++ works. 

1. C++ is a powerful language with an impressive feature set. It should be seen as a federation of different languages.

Guidelines for successful C++ programming vary depending on which aspect of C++ you use. 

2. It is always preferable to favour the compiler over the preprocessor.

The dilemma lies in treating #define as if it were not an integral part of the language.

Therefore, for simple constants, opt for const objects and enums instead of #define. For macro-like functions, prefer inline functions over #define. 

Do define

Don´t define

3. Use const as often as possible. The keyword const stands out for its remarkable flexibility. 

• Outside of classes, it serves to define global or namespace-level constants as well as objects declared as “static” at the file, function, or block level. 

• Within classes, const can be used for both static and non-static data members. 

• For pointers, it can be determined whether the pointer itself, the data it points to, both, or neither are const

• Declaring as const helps compilers uncover errors in usage. Const can be applied to objects of any scope, function parameters and return types, as well as entire member functions. 

• While compilers enforce bitwise constness, you should develop with conceptual constness in mind. 

• If const and non-const member functions share essentially the same implementation, calling the const version in the non-const version can avoid code duplication. 

4. Make sure that objects are initialised before their use.

• Accessing uninitialised values results in unpredictable behaviour. 

• Manually initialise objects of built-in types, as C++ doesn’t always automatically initialise them. 

• Do not confuse assignment with initialisation. 

• Prefer the initialisation list for member variables in the constructor instead of assignments in the constructor body. List the data members in the initialisation list in the same order they are defined in the class. 

• To avoid issues with initialisation order across different translation units, replace non-local static objects with local static objects. 

Scott Meyers’ 2nd Principle

Nearly every class you create will have one or more constructors, a destructor and a copy assignment operator. Errors in these functions can impact your entire class. 

1. Be aware of the functions C++ performs behind the scenes.

This aspect emphasises a recurring theme in Scott Meyers’ writing: the hazards of the invisible and the unspeakable. By highlighting automatically generated constructors and assignment operators, he sensitises developers to potential pitfalls that can arise from unintended neglect. 

• Compilers can generate the default constructor, copy constructor, copy assignment operator and destructor of a class without explicit instructions. 

• C++ does not allow redirecting a reference to another object. 

2. Explicitly prohibit the use of compiler-generated functions that you reject. 

If you do not want compilers to allow functions provided by default, declare these member functions as private and do not provide implementations for them. One approach to achieve this is to use a base class like “uncopyable.”

3. In polymorphic base classes, destructors should be declared as virtual.

• A function that returns a base class pointer pointing to a newly generated object of a derived class can be used to deliver a pointer to a time object. 

• Polymorphic base classes must declare virtual destructors. If a class has virtual functions, it should also possess a virtual destructor. 

• Classes not intended as base classes or not meant for polymorphic use should not declare virtual destructors. 

4. Avoid throwing exceptions from destructors.

• Although C++ does not prohibit throwing exceptions from destructors, it is discouraged. 

• If functions called in the destructor can potentially throw exceptions, these exceptions should be caught within the destructor and either suppressed or the program terminated. 

• If it’s necessary for users of the class to respond to exceptions thrown during an operation, the class should provide a separate (non-destructive) function to perform that operation. 

5. Avoid calling virtual functions during construction or destruction.

• An object becomes an object of the derived class only once the constructor of that derived class is executed. 

• Avoid calling virtual functions during construction or destruction, as such calls will never be forwarded to a class beyond the currently executed constructor or destructor. 

6. Ensure that assignment operators return a reference to *this.

7. Handle self-assignment in operator=.

• Ensure that operator= works correctly when an object is self-assigned. This includes methods like comparing addresses of the source and target objects, a thoughtful sequence of instructions, and copying and swapping operations. 

• Ensure that functions affecting multiple objects work correctly even when two or more of these objects are identical. 

8. Complete copying of an object.

• When implementing copy functions, ensure that all data components of an object and all aspects of its base class are considered. 

• It should be avoided to realise one copy function through the other. Instead, it’s better to consolidate the shared functionality in a separate function called by both. 

Scott Meyers’ 3rd Principle

Resource Management 

A resource refers to something that should be returned to the system after use. In C++, the most commonly used resource type is dynamically allocated memory (where improper allocation, initialisation or deallocation – memory leaks – can lead to program crashes, unexpected behaviour, or security vulnerabilities).

Other common resources include file descriptors, mutex locks in concurrency, fonts, and drawing tools in graphical user interfaces (GUIs), database connections, network connections and even hardware devices.

Regardless of the type of resource, it is essential to release it after use. 

1. Use objects for resource management. 

• To minimise the risk of resource leaks, rely on the principle of Resource Acquisition Is Initialisation (RAII) – this involves objects that request resources during construction in their constructors and release them in their destructors. 

• Two common RAII classes are shared_ptr and auto_ptr

• In most cases, shared_ptr is the preferred option as its copying behaviour is more predictable. Copying an auto_ptr will result in it being reset. 

2. Carefully consider how resource management classes should be copied.

• When copying an RAII object, the managed resource is also copied, implying that the copying behaviour of the resource affects the copying behaviour of the RAII object. 

• Typically, RAII classes prevent copying and perform reference counting, but there are other approaches. 

3. Provide access to raw resources in resource management classes. 

This access can be enabled either through explicit or implicit conversion. While explicit conversion is generally safer, implicit conversion provides the users with more convenience. 

4. Use the same syntax for new and delete in appropriate cases.

If you use [] in a new command, you must also use [] in the corresponding delete command. If you do not use [] in a new command, you should also avoid it in the corresponding delete command.

5. Preserve objects created with new in smart pointers in separate statements. 

Place newly created objects in smart pointers in separate statements. Failing to do so can result in hidden resource leaks when exceptions are thrown.

Scott Meyers’ Fourth Principle

Design and Definition

Software design – concepts to guide software to achieve desired behaviour – often starts as rough sketches. However, over time, they must become precise enough to enable the creation of concrete interfaces. These interfaces are then translated into C++ definitions. 

The following aspects emphasise key considerations, caution against common mistakes, and offer solutions to challenges that designers of classes, functions, and templates often face in interface design. 

Design interfaces to be intuitively correct and difficult to misuse. This forms the foundation for more specific recommendations covering various aspects such as precision, performance, encapsulation, maintainability, scalability, and adherence to standards. 

Treat the design of classes as type design. 

Prefer passing by reference-to-const over passing by value

Avoid returning a reference when an object should be returned. 

Define data members as private. 

Favour non-member non-friend functions over member functions. Here, Scott Meyers emphasises encapsulation and the goal of reducing exposure risk. The more details hidden within a class, the more stable it is. With his recommendation for non-member functions, he advocates for minimalism and strict encapsulation. 

Define non-member functions when type conversions are needed for all arguments. 

Consider implementing a non-throwing swap mechanism. 

Explore Boost. Beyond the C++ Standard Library, Scott Meyers recognises the relevance of third-party libraries, specifically Boost. This mention of a broader development ecosystem reflects his pragmatic approach and encourages developers to leverage existing solutions rather than reinventing everything from scratch. 

Scott Meyers’ Effective C++ Code Check in Understand

SciTools Understand offers the capability to analyse your code against published coding standards or your own standards. These checks can be used to verify naming guidelines, metric requirements, published best practices, or any other rules or conventions important to your team. 

The following coding standards are examined in Understand: 

• Effective C++ (3rd Edition) by Scott Meyers 

• AUTOSAR C++14 

• CERT C 

• CERT C++ 

• MISRA-C 2004 

• MISRA-C++ 2008 

• MISRA-C 2012/2023 

A full, up-to-date list of the Understand coverage for all coding standards can be found here.

In addition to covering many of Scott Meyers “Effective C++” guidelines, developers can use Understand in conjunction with principles from Scott Meyers’ books to enhance code quality, maintainability and developer understanding! 

SciTools Understand offers:

1. Complexity Metrics:

One of the principles of “Effective C++” is to write simple and straightforward C++ code. Understand offers various metrics (such as cyclomatic complexity, lines of code, etc.) that can help developers identify complex or dense parts of the codebase. By reviewing and potentially refactoring these sections, developers can create code that aligns with Scott Meyers’ guidelines. 

2. Dependency Analysis:

Scott Meyers emphasises the importance of understanding dependencies and managing object relationships effectively. With SciTools Understand, developers can visualise dependency graphs that highlight tight coupling between components. This visualisation can guide refactoring efforts to achieve a cleaner and more modular architecture. 

3. Code Documentation:

Adequate documentation ensures that code is readable and maintainable. Understand provides tools for reviewing and generating documentation, allowing teams to ensure that the codebase is well-documented in accordance with Scott Meyers’ suggestions for code clarity.

The Convergence of SciTools Understand and Scott Meyers’ Effective C++

Scott Meyers’ principles in “Effective C++” provide a solid foundation for high-quality C++ programming. His recommendations and guidelines are aimed at writing code that is both efficient and maintainable.

In this context, SciTools Understand is a tool that aligns perfectly with Meyers’ approaches.

It offers comprehensive code analysis and facilitates developers in implementing Scott Meyers’ best practices in their projects. 

By utilising SciTools Understand, developers can ensure that they not only follow Meyers’ advice but also create code that is optimised, understandable, and free from hidden errors.

The tool enables visualising the complexity of the code, understanding dependencies, and identifying potential problem areas that might have been overlooked. 

For programmers who are constantly evolving and striving to perfect their craft, SciTools Understand provides invaluable added value.

It not only enhances the understanding of one’s own code but also ensures that the applications they create adhere to the highest industry standards.

Try SciTools Understand and integrate it into your development process! 

Source Texts:

https://en.wikipedia.org/wiki/Scott_Meyers

https://github.com/gavincyi/Effective-Modern-Cpp

https://github.com/SahibYar/Effective-Cpp-Summary#effective-c-by-scott-meyers-short-summary – and all other related pages of this blog 

https://support.scitools.com/support/solutions/articles/70000583268-what-standards-does-codecheck-test

https://support.scitools.com/support/solutions/articles/70000583282-codecheck-overview