Refactoring with C++ - Dmitry Danilov - E-Book

Refactoring with C++ E-Book

Dmitry Danilov

0,0
28,79 €

-100%
Sammeln Sie Punkte in unserem Gutscheinprogramm und kaufen Sie E-Books und Hörbücher mit bis zu 100% Rabatt.

Mehr erfahren.
Beschreibung

Despite the prevalence of higher-level languages, C++ is still running the world, from bare-metal embedded systems to distributed cloud-native systems. C++ is on the frontline whenever there is a need for a performance-sensitive tool supporting complex data structures. The language has been actively evolving for the last two decades.
This book is a comprehensive guide that shows you how to implement SOLID principles and refactor legacy code using the modern features and approaches of C++, the standard library, Boost library collection, and Guidelines Support Library by Microsoft. The book begins by describing the essential elements of writing clean code and discussing object-oriented programming in C++. You’ll explore the design principles of software testing with examples of using popular unit testing frameworks such as Google Test. The book also guides you through applying automated tools for static and dynamic code analysis using Clang Tools.
By the end of this book, you’ll be proficient in applying industry-approved coding practices to design clean, sustainable, and readable real-world C++ code.

Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:

EPUB
MOBI

Seitenzahl: 480

Veröffentlichungsjahr: 2024

Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Refactoring with C++

Explore modern ways of developing maintainable and efficient applications

Dmitry Danilov

Refactoring with C++

Copyright © 2024 Packt Publishing

All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.

Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing or its dealers and distributors, will be held liable for any damages caused or alleged to have been caused directly or indirectly by this book.

Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.

Group Product Manager: Kunal Sawant

Book Project Manager: Prajakta Naik

Senior Editor: Kinnari Chohan

Technical Editor: Rajdeep Chakraborthy

Copy Editor: Safis Editing

Proofreader: Kinnari Chohan

Indexer: Hemangini Bari

Production Designer: Alishon Mendonca

DevRel Marketing Coordinator: Sonia Chauhan

First published: July 2024

Production reference: 2010724

Published by Packt Publishing Ltd.

Grosvenor House

11 St Paul’s Square

Birmingham

B3 1RB, UK

ISBN 978-1-83763-377-7

www.packtpub.com

This book has been a long journey, and I would like to thank my loving partner, Rina, for her unwavering support and encouragement throughout this endeavor.

I am also deeply grateful to my parents, Marina and Borys, for their continual support, inspiration, and for always encouraging me to pursue my dreams with determination.

– Dmitry Danilov

Contributors

About the author

Dmitry Danilov is an engineer and team manager with over 15 years of experience in C++. Throughout his career, he has developed network sniffers and analyzers, audio/video streaming solutions, low-level embedded applications in telecommunications, and distributed systems.

Originally from Odesa, Ukraine, Dmitry graduated with a Master’s degree in Computer Engineering from Odesa National Polytechnic University. He currently resides in Tel Aviv, Israel, where he continues to advance his career in technology.

In addition to his professional career, Dmitry demonstrates his passion for knowledge sharing and engaging with the tech community through his blog (https://ddanilov.me) and by actively speaking at various events, including Core C++ Conference and Core C++ Group Meetup Israel.

About the reviewer

Anil Achary is a Lead Software Engineer at FactSet FinTech company with over 14 years of experience in the software industry. With expertise in C++ and real-time data processing, Anil has worked extensively in both FinTech and Telecom domains. Leading multiple engineering teams, Anil has developed high-throughput, scalable applications, optimized real-time market data systems, and integrated innovative search solutions using ChatGPT GenAI. Anil has also provided technical reviews for several books, bringing a wealth of practical knowledge to this publication.

Table of Contents

Preface

1

Coding Standards in C++

The difference between good code and bad code

Why coding standards are important

Code convention

Language features limitations

General guidelines

Readability, efficiency, maintainability, and usability

Readability

Efficiency

Maintainability

Usability

Summary

2

Main Software Development Principles

SOLID

The Single Responsibility Principle

The Open-Closed Principle

The Liskov Substitution Principle

The Dependency inversion principle

The KISS principle

The KISS and SOLID Principles together

Side effects and immutability

Con.1 – by default, make objects immutable

Con.2 – by default, make member functions const

Con.3 – by default, pass pointers and references to const

Con.4 – use const to define objects with values that do not change after construction

Con.5 – use constexpr for values that can be computed at compile time

Constness and data races

Summary

3

Causes of Bad Code

The need to deliver the product

The developer’s personal taste

Multiple ways of solving the same problem in C++

Revisiting Bob and Alice’s example

Raw pointers and C functions versus Standard Library functions

Inheritance versus templates

Example – handling errors

Projects using different approaches

Lack of knowledge in C++

Using raw pointers and manual memory management

Incorrect use of smart pointers

Efficient use of move semantics

Misusing const correctness

Inefficient string handling

Undefined behavior with lambdas

Misunderstanding undefined behavior

Misuse of C-style arrays

Insufficient pointer usage

Building std::shared_ptr

Copying std::shared_ptr by value

Cyclic dependencies with std::shared_ptr

Checking the std::weak_ptr status

Summary

4

Identifying Ideal Candidates for Rewriting – Patterns and Anti-Patterns

What kind of code is worth rewriting?

Smelly code and its basic characteristics

Anti-patterns

The pitfalls of magic numbers – a case study on data chunking

Legacy code

Summary

5

The Significance of Naming

General naming principles

Descriptiveness

Consistency

Unambiguity

Pronounceability

Scope and lifetimes

Avoid encoding type or scope information

Class and method naming

Naming variables

Utilize namespaces

The use of domain-specific language

Balancing long names and comments in code

Exploring popular C++ coding conventions – Google, LLVM, and Mozilla

Summary

6

Utilizing a Rich Static Type System in C++

Utilizing Chrono for time duration

Improving Pointer Safety with not_null and std::optional

The pitfalls of raw pointers

Using not_null from the Guidelines Support Library

Utilizing std::optional for optional values

A comparison between raw pointers and nullptr

Leveraging std::expected for expected results and errors

Strong typing with enum class and scoped enumerations

A review of enum class

The benefits over traditional enums

Real-world scenarios

Leveraging the standard library’s type utilities

std::variant – a type-safe union

std::any – type-safe containers for any type

Advanced type techniques

Avoiding common pitfalls in advanced type usage

Writing robust code with type checks

Implicit conversions and type coercion

Summary

7

Classes, Objects, and OOP in C++

Good candidates for classes

Cohesion

Encapsulation

Reusability

Abstraction

Real-world entities

Manage complexity

Minimizing class responsibilities through encapsulation

Usage of structs and classes in C++

Common method types in classes – getters and setters

Inheritance in C++

Evolution of inheritance in C++

Implementation of inheritance at the binary level

Pros and cons of inheritance

Base class – Discount

Derived class – SeasonalDiscount

Derived class – ClearanceDiscount

Tight coupling problems

Solution – decouple with the strategy pattern

Templates and generic programming

What are templates good for?

Generic algorithms

Container classes

How templates work

How templates are instantiated

A real-world example of template usage in C++

Defining currencies

Defining assets

Using the financial system

Disadvantages of using templates in system design

Summary

8

Designing and Developing APIs in C++

Principles of minimalistic API design

Techniques for achieving minimalism

Real-world examples of minimalistic API design

Common pitfalls and how to avoid them

Important caveats of developing shared libraries in C++

Shared libraries within a single project

Shared libraries for wider distribution

Example – MessageSender class

Summary

9

Code Formatting and Naming Conventions

Why is code formatting important?

Overview of existing tools that facilitate compliance with coding conventions

cpplint

Artistic Style

Uncrustify

Editor plugins

Clang-Format

Clang-Format configuration – a deep dive into customizing your formatting rules

Leveraging existing presets

Extending and overriding presets

Ignoring specific lines with Clang-Format

Endless options for configuration

Version control and sharing

Integrating Clang-Format into the build system

Clang-Format report examples

Extending for code format checks for CI

Clang-Format support across various editors

Checking name styling

Integrating Clang-Tidy into the build system

Checking source code name styling with Clang-Tidy

Fixing naming issues automatically

Important caveats

Example project

Clang-Tidy support across various editors

Summary

10

Introduction to Static Analysis in C++

The essence of static analysis

Leveraging newer compiler versions for enhanced static analysis

Compiler settings to harden C++ code

GCC

Clang

MSVC

Static analysis via multiple compilers

Highlighting compiler differences – unused private members in GCC versus Clang

Highlighting compiler differences – compiler checks for uninitialized variables

Exploring static analysis with Clang-Tidy

Categories of checks in Clang-Tidy

Expanding Clang-Tidy’s capabilities with custom checks

Fine-tuning Clang-Tidy for customized static analysis

Overview of static analysis tools – comparing PVS-Studio, SonarQube, and others to Clang-Tidy

PVS-Studio

SonarQube

Other notable tools

Comparison with Clang-Tidy

Summary

11

Dynamic Analysis

Compiler-based dynamic code analysis

ASan

LeakSanitizer (LSan)

MemorySanitizer (MSan)

TSan

UBSan

Dynamic code analysis with Valgrind

Setting up Valgrind

Memcheck – the comprehensive memory debugger

Helgrind – threading error detector

Performance impact, fine-tuning, and limitations

Other notable tools in the Valgrind suite

Data Race Detector (DRD) – a thread error detector

Cachegrind

Callgrind

Massif

Dynamic heap analysis tool (DHAT)

Summary

12

Testing

Test-driven development

Unit testing in C++

C++ unit testing frameworks

Google Test and Google Mock

Integrating Google Test into a C++ project

Usage of Google Test in C++ projects

Writing a simple test

Using a test fixture

The main function

Running Google Test tests

Advanced features of Google Test

Using gMock in C++ projects

Example of using gMock

Mocking non-virtual methods via dependency injection

Mocking with templates

The Nice, the Strict, and the Naggy

Other notable C++ unit testing frameworks

Catch2

Boost.Test

Doctest

Google Test versus Catch2 versus Boost.Test versus Doctest

Good candidates for unit tests

E2E testing in software development

E2E testing frameworks

When to use E2E testing

Situations favoring E2E testing

Complex interactions

Real-world environment testing

Automatic test coverage tracking tools

Automatic test coverage tracking tools with examples

Utilizing hit maps for enhanced test coverage analysis

Recommendations for code coverage

Summary

13

Modern Approach to Managing Third Parties

Overview of linking and shared V threads::ThreadsS static libraries

Managing third-party libraries in C++

Installing libraries with OS package managers

Using Git as a third-party manager via submodules

Using CMake FetchContent to download libraries

Conan – advanced dependency management

Conan configuration and features

Library locations and Conan Center

Configuring static or dynamic linking

Extending Conan with custom packages

CMake integration

Other build system integration

Custom integration

Conclusion

vcpkg

Key differences from Conan

Operating system support

Example of configuring a project with vcpkg

Utilizing Docker for C++ builds

Summary

14

Version Control

What is a good commit?

The principle of singular focus

The art of communication

The art of refinement

Conventional Commits specification

Linking code to context

Overview and intent

Options and usage

Origins and adoption

Advantages of Conventional Commits

Commitlint – enforcing commit message standards

Installation

Configuration

Local usage

Customizing rules

Basic configuration

Custom rule configuration

Scope and subject configuration

Customizing and sharing configurations

Integration with CI

Generating changelogs

Installation

GitCliff usage

Utilizing git-bisect in bug hunting

Summary

15

Code Review

What is a code review and why is it needed?

Benefits of code reviews

Preparing for code reviews

Clear guidelines

Self-review

How to pass a code review

Discuss big features with reviewers and code owners before writing code

Go over your code before publishing it

Make sure the code is compliant with the code convention

Code review is a conversation, not an order

Remember – your code is not you

How to efficiently dispute during a code review

Clear justification for changes

Reciprocal explanation from reviewees

Direct communication

Involving additional perspectives

How to be a good reviewer

Initiate the conversation

Maintain politeness and respect

Review manageable chunks

Avoid personal bias

Focus on understandability

Summary

Index

Other Books You May Enjoy

Preface

In an era where higher-level languages dominate the technological landscape, C++ remains a cornerstone, driving a vast array of systems from bare-metal embedded platforms to distributed, cloud-native infrastructures. Its prowess lies in its ability to deliver performance-sensitive solutions while adeptly handling complex data structures. Over the past two decades, C++ has undergone significant evolution, continually adapting to meet the demands of modern computing.

This book serves as a comprehensive guide for those seeking to master the art of writing clean, efficient C++ code. It delves into the implementation of SOLID principles and the refactoring of legacy code using the latest features and methodologies of C++. Readers will gain a deep understanding of the language, the standard library, the extensive Boost library collection, and Microsoft’s Guidelines Support Library.

Starting with the fundamentals, the book covers the core elements essential for writing clean code, with a strong emphasis on object-oriented programming in C++. It provides insights into the design principles of software testing, illustrated with examples utilizing popular unit testing frameworks like Google Test. Furthermore, the book explores the application of automated tools for both static and dynamic code analysis, featuring the powerful capabilities of Clang Tools.

By journey’s end, readers will be equipped with the knowledge and skills to apply industry-approved coding practices, enabling them to craft clean, sustainable, and readable C++ code for real-world applications.

Who this book is for

This book is designed for a wide range of professionals in the C++ community. If you are a C++ engineer looking to refine your skills and write more elegant, efficient code, this book will provide you with the insights and techniques needed to elevate your programming practice. It is also an excellent resource for those tasked with refactoring and improving existing codebases, offering practical advice and strategies to make this process more manageable and effective.

Additionally, this book is an invaluable guide for tech and team leaders who aim to enhance their software development processes. Whether you are leading a small team or managing a larger development project, you will find useful tips and methodologies to make your workflows smoother and more efficient. By implementing the best practices outlined in this book, you can foster a more productive and harmonious development environment, ultimately leading to higher-quality software and more successful projects.

What this book covers

Chapter 1, Coding Standards in C++, explores the world of clean code and its crucial role in successful software projects. We discuss technical debt and how poor-quality code contributes to its accumulation. The chapter also covers the importance of code formatting and documentation, emphasizing their role in maintaining a manageable and effective codebase. We introduce common conventions and best practices used in the C++ community, highlighting the necessity of clean code and proper documentation for any project.

Chapter 2, Main Software Development Principles, covers key software design principles for creating well-structured and maintainable code. We discuss the SOLID principles—Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion—which help developers write code that is easy to understand, test, and modify. We also highlight the importance of levels of abstraction, the concepts of side effects and mutability, and their impact on software quality. By applying these principles, developers can create more robust, reliable, and scalable software.

Chapter 3, Causes of Bad Code, identifies the key factors leading to subpar code in C++. These include the pressure to deliver quickly, the flexibility of C++ allowing multiple solutions to the same problem, personal coding styles, and a lack of knowledge of modern C++ features. Understanding these causes helps developers avoid common pitfalls and improve existing codebases effectively.

Chapter 4, Identifying Ideal Candidates for Rewriting - Patterns and Anti-Patterns, focuses on identifying ideal candidates for refactoring in C++ projects. We will explore factors that make code segments suitable for refactoring, such as technical debt, complexity, and poor readability. We will also discuss common patterns and anti-patterns, providing guidelines and techniques for improving code structure, readability, and maintainability without altering its behavior. This chapter aims to equip developers with the knowledge to enhance the quality and robustness of their C++ codebases effectively.

Chapter 5, The Significance of Naming, highlights the crucial role of naming conventions in C++ programming. Proper names for variables, functions, and classes enhance code readability and maintainability. We discuss best practices for naming, the impact of poor naming on code efficiency, and the importance of consistent coding conventions. By understanding and applying these principles, you will write clearer and more effective code.

Chapter 6, Utilizing a Rich Static Type System in C++, explores the powerful static type system in C++, emphasizing its role in writing robust, efficient, and maintainable code. We discuss advanced techniques like using the <chrono> library for time durations, not_null wrappers, and std::optional for safer pointer handling. Additionally, we look at external libraries like Boost for enhancing type safety. Through real-world examples, we demonstrate how to leverage these tools to harness the full potential of C++’s type system, resulting in more expressive and error-resistant code.

Chapter 7, Classes, Objects, and OOP in C++, focuses on advanced topics in classes, objects, and object-oriented programming (OOP) in C++. We cover class design, method implementation, inheritance, and template usage. Key topics include optimizing class encapsulation, advanced method practices, evaluating inheritance versus composition, and sophisticated template techniques. Practical examples illustrate these concepts, helping you create robust and scalable software architectures.

Chapter 8, Designing and Developing APIs in C++, explores the principles and practices for designing maintainable APIs in C++. We discuss the importance of clarity, consistency, and extensibility in API design. Through concrete examples, we illustrate best practices that help create intuitive, easy-to-use, and robust APIs. By applying these principles, you will develop APIs that meet user needs and remain adaptable over time, ensuring the longevity and success of your software libraries.

Chapter 9, Code Formatting and Naming Conventions, explore the critical role of code formatting and naming conventions in creating robust and maintainable software. While these topics may seem minor, they greatly enhance code readability, simplify maintenance, and foster effective team collaboration, especially in complex languages like C++. We delve into the importance of code formatting and provide practical knowledge on using tools like Clang-Format and editor-specific plugins to implement consistent formatting. By the end of this chapter, you’ll understand the significance of these practices and how to apply them effectively in your C++ projects.

Chapter 10, Introduction to Static Analysis in C++, discusses the crucial role of static analysis in ensuring code quality and reliability in C++ development. We discuss how static analysis identifies bugs quickly and cost-effectively, making it a key component of software quality assurance. We delve into popular tools like Clang-Tidy, PVS-Studio, and SonarQube, and guide integrating static analysis into your development workflow.

Chapter 11, Dynamic Analysis, explores dynamic code analysis in C++, focusing on tools that scrutinize program behavior during execution to detect issues like memory leaks, race conditions, and runtime errors. We cover compiler-based sanitizers such as Address Sanitizer (ASan), Thread Sanitizer (TSan), and Undefined Behavior Sanitizer (UBSan), along with Valgrind for thorough memory debugging. By understanding and integrating these tools into your development workflow, you can ensure cleaner, more efficient, and reliable C++ code.

Chapter 12, Testing, emphasizes the crucial role of software testing in ensuring quality, reliability, and maintainability. We cover various testing methodologies, starting with unit testing to validate individual components, followed by integration testing to examine the interaction between integrated units. We then move to system testing for a comprehensive assessment of the entire software system and conclude with acceptance testing to ensure the software meets end-user requirements. By understanding these methodologies, you will grasp how testing underpins the development of robust and user-centric software.

Chapter 13, Modern Approach to Managing Third Parties, addresses the critical role of third-party libraries in C++ development. We explore the basics of third-party library management, including the impact of static versus dynamic compilation on deployment. Given C++’s lack of a standardized library ecosystem, we examine tools like vcpkg and Conan to understand their advantages in integrating and managing libraries. Additionally, we discuss the use of Docker for creating consistent and reproducible development environments. By the end of this chapter, you’ll be equipped to choose and manage third-party libraries effectively, enhancing your development workflow and software quality.

Chapter 14, Version Control, highlights the importance of maintaining a clean commit history in software development. We discuss best practices for clear and purposeful commit messages and introduce tools like Git, Conventional Commit Specification, and commit linting. By following these principles, developers can enhance communication, collaboration, and project maintainability.

Chapter 15, Code Review, explores the critical role of code review in ensuring robust and maintainable C++ code. While automated tools and methodologies provide significant benefits, they are not foolproof. Code reviews, conducted by human reviewers, help catch errors that automated processes may miss and ensure adherence to standards. We discuss strategies and practical guidelines for effective code reviews, emphasizing their role in preventing bugs, enhancing code quality, and fostering a collaborative culture of learning and accountability.

To get the most out of this book

To get the most out of this book, you should have a solid understanding of the basics of C++. Familiarity with build systems such as Make and CMake will be beneficial. Additionally, having basic knowledge of Docker and terminal skills can enhance your learning experience, although these are optional.

If you are using the digital version of this book, we advise you to type the code yourself or access the code from the book’s GitHub repository (a link is available in the next section). Doing so will help you avoid any potential errors related to the copying and pasting of code.

Download the example code files

You can download the example code files for this book from GitHub at https://github.com/PacktPublishing/Refactoring-with-C-. If there’s an update to the code, it will be updated in the GitHub repository.

We also have other code bundles from our rich catalog of books and videos available at https://github.com/PacktPublishing/. Check them out!

Conventions used

There are a number of text conventions used throughout this book.

Code in text: Indicates code words in text, database table names, folder names, filenames, file extensions, pathnames, dummy URLs, user input, and Twitter handles. Here is an example: “Mount the downloaded WebStorm-10*.dmg disk image file as another disk in your system.”

A block of code is set as follows:

html, body, #map { height: 100%; margin: 0; padding: 0 }

When we wish to draw your attention to a particular part of a code block, the relevant lines or items are set in bold:

[default] exten => s,1,Dial(Zap/1|30) exten => s,2,Voicemail(u100) exten => s,102,Voicemail(b100) exten => i,1,Voicemail(s0)

Any command-line input or output is written as follows:

$ mkdir css $ cd css

Bold: Indicates a new term, an important word, or words that you see onscreen. For instance, words in menus or dialog boxes appear in bold. Here is an example: “Select System info from the Administration panel.”

Tips or important notes

Appear like this.

Get in touch

Feedback from our readers is always welcome.

General feedback: If you have questions about any aspect of this book, email us at [email protected] and mention the book title in the subject of your message.

Errata: Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you have found a mistake in this book, we would be grateful if you would report this to us. Please visit www.packtpub.com/support/errata and fill in the form.

Piracy: If you come across any illegal copies of our works in any form on the internet, we would be grateful if you would provide us with the location address or website name. Please contact us at [email protected] with a link to the material.

If you are interested in becoming an author: If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, please visit authors.packtpub.com.

Share Your Thoughts

Once you’ve read Refactoring with C++, we’d love to hear your thoughts! Please click here to go straight to the Amazon review page for this book and share your feedback.

Your review is important to us and the tech community and will help us make sure we’re delivering excellent quality content.

Download a free PDF copy of this book

Thanks for purchasing this book!

Do you like to read on the go but are unable to carry your print books everywhere?

Is your eBook purchase not compatible with the device of your choice?

Don’t worry, now with every Packt book you get a DRM-free PDF version of that book at no cost.

Read anywhere, any place, on any device. Search, copy, and paste code from your favorite technical books directly into your application.

The perks don’t stop there, you can get exclusive access to discounts, newsletters, and great free content in your inbox daily

Follow these simple steps to get the benefits:

Scan the QR code or visit the link below

https://packt.link/free-ebook/9781837633777

Submit your proof of purchaseThat’s it! We’ll send your free PDF and other benefits to your email directly