Data Structures and Algorithms with the C++ STL - John Farrier - E-Book

Data Structures and Algorithms with the C++ STL E-Book

John Farrier

0,0
32,39 €

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

While the Standard Template Library (STL) offers a rich set of tools for data structures and algorithms, navigating its intricacies can be daunting for intermediate C++ developers without expert guidance. This book offers a thorough exploration of the STL’s components, covering fundamental data structures, advanced algorithms, and concurrency features.
Starting with an in-depth analysis of the std::vector, this book highlights its pivotal role in the STL, progressing toward building your proficiency in utilizing vectors, managing memory, and leveraging iterators. The book then advances to STL’s data structures, including sequence containers, associative containers, and unordered containers, simplifying the concepts of container adaptors and views to enhance your knowledge of modern STL programming. Shifting the focus to STL algorithms, you’ll get to grips with sorting, searching, and transformations and develop the skills to implement and modify algorithms with best practices. Advanced sections cover extending the STL with custom types and algorithms, as well as concurrency features, exception safety, and parallel algorithms.
By the end of this book, you’ll have transformed into a proficient STL practitioner ready to tackle real-world challenges and build efficient and scalable C++ applications.

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

EPUB
MOBI

Seitenzahl: 563

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.



Data Structures and Algorithms with the C++ STL

A guide for modern C++ practitioners

John Farrier

Data Structures and Algorithms with the C++ STL

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

Publishing Product Manager: Samriddhi Murarka

Book Project Manager: Manisha Singh

Senior Editor: Aditi Chatterjee

Technical Editor: Jubit Pincy

Copy Editor: Safis Editing

Indexer: Hemanigini Bari

Production Designer: Gokul Raj S.T

DevRel Marketing Coordinator:Shrinidhi Manoharan

First published: February 2024

Production reference: 1160224

Published by

Packt Publishing Ltd.

Grosvenor House

11 St Paul’s Square

Birmingham

B3 1RB, UK.

ISBN 978-1-83546-855-5

www.packtpub.com

To my parents, John and Sharon, who ignited the spark of curiosity within me by introducing me to the world of computers and not being too upset when I broke them—thank you for setting me on this path. To my wife, Lisa, whose unwavering support has been my cornerstone throughout this journey. And to my children, Corbin and Regan, who give every day a purpose and every challenge a reward—this book is for you. Your love and belief in me make every endeavor worthwhile.

Contributors

About the author

John Farrier has over 25 years of experience as a successful founder and software engineering leader, particularly noted for delivering high-value projects to the U.S. Air Force clients.

Under his leadership as Co-Founder and CEO, his first company, Hellebore, saw remarkable growth and was pivotal in the defense sector, particularly in designing advanced mission systems architectures for next-generation aircraft.

In his expansive technical repertoire, John commands expertise in Design Patterns, C++, Python, DevOps, AI, Game Engine Design, Large-Scale Agile Project Management, and Modeling & Simulation. Leveraging Agile principles and stream-aligned teams, he constantly explores the outer reaches of software engineering possibilities. John’s credentials are reinforced by numerous publications in the realm of Modeling and Simulation.

John’s commitment extends beyond mere technical excellence. He’s an unwavering advocate for fostering strong software cultures, emphasizing collaboration and career evolution. He operates with a strong ethos of principle-based decision-making and hyper-transparency, fostering both trust and clarity in professional relationships.

At present, John leads Polyrhythm Software, one of a new generation of software companies focused on delivering high-value software to the Department of Defense and commercial clients.

About the reviewer

Kevin Carpenter, an experienced Software Engineer, excels in crafting high-availability C++ solutions for Linux and Windows, with expertise in transaction software, financial modeling, and system integration. As a Lead Project Engineer, he ensures secure, high-speed credit card transactions. In his prior position, he played a lead role in developing an interest rate risk model for large credit unions, enhancing legacy code, and optimizing ERP data integration.

Kevin actively engages in the C++ community, volunteering at conferences such as CppCon, C++ on Sea, and C++Now, where he holds key positions of Speaker Liaison and Volunteer Coordinator/Chair. His diverse contributions to the C++ community showcase his commitment to excellence and drive for collaborative growth, leaving a lasting impact on the tech world.

Table of Contents

Preface

Part 1: Mastering std::vector

1

The Basics of std::vector

Technical requirements

The significance of std::vector

A basic comparison of C-style arrays and std::vector

Comparison of C-style arrays and std::vector for memory management

Declaring and initializing std::vector

Declaring a vector

Initializing a vector

Accessing elements

Random access

Accessing the first and last elements

Vector size

Adding and removing elements

Adding elements

Removing elements

Capacity

Prefer using empty() when possible

Clearing all elements

Summary

2

Mastering Iterators with std::vector

Technical requirements

Types of iterators in the STL

Input iterators

Output iterators

Forward iterators

Reverse iterators

Bidirectional iterators

Random access iterators

Basic iteration techniques with std::vector

Iterating over std::vector

Basic iteration using iterators

Using constant iterators

Benefits of iteration

Using std::begin and std::end

Understanding iterator requirements

Range-based for loops

Overview of range-based for loops

When to use range-based for loops

Modifying elements during iteration

Creating a custom iterator

The appeal of custom iterators

Core requirements

Iterator categories and their specialties

A custom iterator example

Custom iterator challenges and use cases

Illustrative use cases of custom iterators

Summary

3

Mastering Memory and Allocators with std::vector

Technical requirements

Understanding capacity versus size

Revisiting the basics

What exactly is capacity?

Why this distinction matters

Looking under the hood

Resizing and reserving memory

The power of resize()

Enter reserve()

Optimizing with shrink_to_fit()

Real-world relevance

Custom allocator basics

The role and responsibility of an allocator

Under the hood – the allocator interface

Trade-offs and the need for custom allocators

Choosing std::allocator over new, delete, and managed pointers

Creating a custom allocator

Custom allocators – the heart of memory flexibility

Understanding the motivation behind custom allocators

Memory pools – a popular custom allocator strategy

Unlocking the potential of custom allocators

Allocators and container performance

Why allocators matter in performance

The performance characteristics of std::allocator

When to consider alternative allocators

Profiling – the key to making informed decisions

Summary

4

Mastering Algorithms with std::vector

Technical requirements

Sorting a vector

Getting started with std::sort

The engine under the hood – introsort

Efficiency unparalleled – O(n log n)

Sorting in descending order

Sorting custom data types

Pitfalls and precautions

Searching elements

Linear search with std::find

Binary search techniques

Using std::lower_bound and std::upper_bound

Binary search versus linear search – efficiency and versatility

Manipulating vectors

Transforming with std::copy

Reversing elements with std::reverse

Rotating vectors with std::rotate

Filling a vector with std::fill

Putting manipulation to use

Considerations in manipulation

Custom comparators and predicates

Understanding comparators

The power of predicates

Crafting effective comparators and predicates

User-defined structs and classes

Understanding container invariants and iterator invalidation

Understanding iterator invalidation

Strategies to counteract invalidation

Dealing with invalidation in multi-threaded scenarios

Summary

5

Making a Case for std::vector

Performance considerations

Comparison with other containers

The memory advantage

The takeaway

Practical use cases

A resizable dynamic array at heart

Data processing and analytics

Graphics and game development

Beyond just containers

Versatility and efficiency

A testament to versatility

Efficiency isn’t just about speed

A safe default, but not the only option

Summary

Part 2: Understanding STL Data Structures

6

Advanced Sequence Container Usage

Technical requirements

std::array

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Example

Best practices

std::vector

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Special interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Example

Best practices

std::deque

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Example

Best practices

std::list

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Example

Best practices

std::forward_list

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Special interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Example

Best practices

std::string

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Special interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Example

Best practices

7

Advanced Ordered Associative Container Usage

Technical requirements

std::set

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Special interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Example

Best practices

std::map

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Special interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Example

Best practices

std::multiset

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Special interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Example

Best practices

std::multimap

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Special interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Example

Best practices

8

Advanced Unordered Associative Container Usage

Technical requirements

std::unordered_set

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Special interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Example

Best practices

std::unordered_map

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Special interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Example

Best practices

std::unordered_multiset

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Special interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Example

Best practices

std::unordered_multimap

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Special interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Example

Best practices

9

Advanced Container Adaptor Usage

Technical requirements

std::stack

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Special interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Example

Best practices

std::queue

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Special interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Example

Best practices

std::priority_queue

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Special interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Example

Best practices

std::flat_set

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Special interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Best practices

std::flat_map

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Best practices

std::flat_multiset

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Special interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Best practices

std::flat_multimap

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Best practices

10

Advanced Container View Usage

Technical requirements

std::span

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Example

Best practices

std::mdspan

Purpose and suitability

Ideal use cases

Performance

Memory management

Thread safety

Extensions and variants

Sorting and searching complexity

Special interface and member functions

Comparisons

Interactions with algorithms

Exceptions

Customization

Best practices

Part 3: Mastering STL Algorithms

11

Fundamental Algorithms and Searching

Technical requirements

Sorting

Checking conditions

Counting and finding

Searching and comparison

Best practices

Summary

12

Manipulation and Transformation

Technical requirements

Copying and moving in STL containers

Copying semantics in the STL

Moving semantics in the STL

Copying versus moving – a deliberate choice

Exploring return value optimization

Filling and generating in STL containers

Populating with static assignment

Dynamic generation with the STL

Ensuring relevance and efficiency

Removing and replacing in STL containers

The essence of removal

Replacement

A balancing act

Swapping and reversing in STL containers

Swapping – the art of interchanging

Reversing – a glimpse from the end

Deduplication – singling out the unique

Sampling – a slice of the whole

Best practices

Summary

13

Numeric and Range-Based Operations

Technical requirements

Basic numeric operations

Generating sequences with std::iota

Summing elements with std::accumulate

Adjacent elements and their interactions with std::adjacent_difference

Inner products with std::inner_product

Advanced numeric operations

Operations on sorted ranges

Best practices

Summary

14

Permutations, Partitions, and Heaps

Technical requirements

Partitioning

std::partition and its power

Checking partitions with std::is_partitioned

The utility of std::partition_point

Partitioning beyond basic sequences

Permutations

Generating permutations with std::next_permutation

Predecessor permutations with std::prev_permutation

Shuffling elements randomly with std::shuffle

Rotating sequences with std::rotate

Heap operations

Constructing heaps with std::make_heap

Adding and removing elements – std::push_heap and std::pop_heap

Heap-based sorting – the power of std::sort_heap

Checking heap validity with std::is_heap

The significance of heaps in today’s computing

Best practices

Summary

15

STL with Ranges

Technical requirements

Introduction to ranges

Understanding the essence of ranges

Why the shift to ranges?

A glimpse into range operations

Looking ahead – the power of modern STL

Ranges for sorting algorithms

Traditional STL sorting – a recap

Range-based sorting – the basics

Embracing composability in sorting

Advantages beyond syntax – why ranges shine in sorting

The revolution of ranges in sorting

Ranges for searching algorithms

Finding elegance – range-based searching

Chaining and filtering – the beauty of composability

Understanding views in searches

The extended toolkit – more than just find

Best practices

Embracing the power of chaining

Guarding against range pitfalls – lifetime awareness

Performance considerations – laziness and evaluation

Readability over brevity – striking the balance

Adhering to range idioms – keep it standard

Summary

Part 4: Creating STL-Compatible Types and Algorithms

16

Creating STL-Types Containers

Technical requirements

The advantages of STL-compatible types

One language, one approach

Reusability – the gift that keeps giving

Efficiency in the familiar

Paving the way forward

Interacting with STL algorithms

The centrality of iterators

Adapting to algorithmic expectations

Error handling and feedback mechanisms

Algorithmic efficiency and your type

Laying a solid foundation

Essential requirements for compatibility

The cornerstones of compatibility

The vitality of iterators

Embracing value semantics

Operational guarantees

Size and capacity queries

Element access and manipulation

Consistency in exception safety

Looking forward to enhanced integration

Crafting iterators for custom types

Choosing the right iterator type

Crafting the basic components

Addressing iterator categories with type traits

End iterators – signifying the finish line

Considerations for const iterators

Performance optimizations and advanced techniques

Embracing the iterative spirit

Effective operator overloading

Operator overloading in C++

Considerations in overloading

Implementing arithmetic operators for custom types

Overloading relational operators for clear comparisons

Simplifying tasks with assignment and compound assignment

Stream operators for efficient I/O

Operator precedence and associativity in overloading

The role of operator overloading in C++

Creating custom hash functions

Interoperability with STL containers

Custom type semantics

The characteristics of a good hash function

Example for the creation of a custom hash function

Summary

17

Creating STL-Compatible Algorithms

Technical requirements

Template functions

A primer on function templates

Variadic templates – multiplicity in templates

SFINAE – fine-tuning template substitution

Harnessing SFINAE with std::enable_if

Overloading

Crafting multiple algorithm versions for STL containers

Function resolution – navigating the intricacies

Overloading with care – clarity and consistency

Creating generic algorithms

Toward a type-independent approach

Embracing iterators – the bridge to generics

Predicates – customizing algorithm behavior

The magic of functors – enhancing flexibility

Customizing existing algorithms

Looking at the decorator pattern in action

Harnessing the power of lambda functions

Mixing patterns with lambdas for ultimate customization

Summary

18

Type Traits and Policies

Technical requirements

Understanding and using type traits

Enhancing code adaptability with type traits

Empowering metaprogramming with type traits

Toward more informed and adaptable code

Utilizing type traits with the STL

Working with data types

Working with algorithms

Understanding and using policies in C++

Benefits with respect to the STL

Building modular components using policies

Potential challenges

The role of policies in modern C++

Using policies with the STL

Memory allocation policies

Sorting policies for versatile algorithms

Fine-tuning data structures with policies

Summary

Part 5: STL Data Structures and Algorithms: Under the Hood

19

Exception Safety

Technical requirements

Basic exception safety

The pivotal role of program invariants in the STL

Resource integrity – the guardian of robust software

Harnessing the STL for basic exception safety

Strong exception safety

Navigating STL containers with strong guarantees

Crafting custom STL containers with strong guarantees

Infusing exception safety into custom STL algorithms

Exception safety is the path to robust applications

The effect of noexcept on STL operations

An introduction to noexcept

Application to STL data types

Application to STL algorithms

Summary

20

Thread Safety and Concurrency with the STL

Technical requirements

Concurrency versus thread safety

Thread safety – a pillar for stable concurrency

The interplay of concurrency and thread safety

Challenges and rewards

Concurrency without thread safety – a recipe for chaos

Understanding thread safety

Thread safety in STL containers – laying the groundwork

Grasping the thread-safe nature of STL algorithms

Race conditions – the ghosts in the machine

Safeguarding concurrency – the way forward

Race conditions

Steering clear of a silent peril – race conditions in the STL

The anatomy of a race condition in the STL

More than meets the eye

Anticipating race conditions

Safeguarding your code – a proactive stance

Mutexes and locks

From manual to automatic – lock guards and unique locks

Avoiding the stalemate – deadlock prevention

Incorporating mutexes with STL containers

STL containers and thread safety

When safety needs reinforcements – concurrent modifications

Container iterators – the fragile bridge

Containers with a built-in shield – concurrent containers

Specific container concerns

Behaviors of std::vector in multi-threading

Characteristics of std::list in concurrency

Considerations with associative containers

Concurrency aspects of unordered containers

Insights into container adaptors

Concurrency support within the STL

Introduction to threads

The advent of asynchronous tasks

Atomic operations

Potential concurrent challenges

Using the STL’s concurrency features

Using std::thread, std::async, std::future, and thread-local storage

Initiating threads using std::thread

Managing asynchronous operations with std::async and std::future

Preserving data consistency using thread-local storage

Integrating tools for proficient concurrency

Concurrent data structures in the STL

The STL’s concurrency-optimized containers

Striving for maximum efficiency in concurrent environments

Best practices in action

Summary

21

STL Interaction with Concepts and Coroutines

Technical requirements

Concepts

A brief introduction to concepts

Refined constraints in STL algorithms

Effectively constraining templates

Enhanced data structures with explicit requirements

Custom concepts and STL interactions

Potential challenges and caveats

Coroutines

Understanding coroutines – a refresher

STL algorithms and coroutine integration

Coroutines and STL data structures

Potential synergies with ranges and views

Looking ahead – a paradigm shift

Summary

22

Parallel Algorithms with the STL

Technical requirements

Introduction to execution policies

The <execution> header– enabling parallelism in STL algorithms

Implementing parallel execution

Reflecting on the transition to parallel STL

Incorporating execution policies

Integrating policies with standard algorithms

Understanding parallel execution policies

Selecting the appropriate execution policy

The impact of constexpr on algorithms and containers

Evolution of constexpr

Algorithms and the role of constexpr

Containers and constexpr integration

Performance considerations

Parallelism overhead

Determining optimal data size

Data access and synchronization challenges

False sharing – a subtle performance issue

Load balancing

The importance of profiling

Summary

Index

Other Books You May Enjoy

Part 1: Mastering std::vector

In this Part, we will build our knowledge of C++ Standard Template (STL) Library containers and algorithms on a foundation of a comprehensive examination of std::vector. We will start with introducing std::vector, contrasting it with traditional C-style arrays, and covering essential operations such as initializing, accessing, and modifying elements. We then advance to the intricacies of iterators, revealing their types and uses in std::vector operations and the elegance of range-based for loops. Memory management is demystified by constructing an understanding of optimizing the allocation and deallocation of resources, including an introduction to creating custom allocators. The section then builds into applying algorithms to sort, search, and manipulate vector contents efficiently, emphasizing the role of custom comparators and the importance of understanding iterator invalidation. The final chapter encapsulates the performance considerations and practical applications of std::vector, cementing its status as the default container choice for C++ developers.

This part has the following chapters:

Chapter 1: The Basics of std::vectorChapter 2: Mastering Iterators with std::vectorChapter 3: Mastering Memory and Allocators with std::vectorChapter 4: Mastering Algorithms with std::vectorChapter 5: Making a Case for std::vector