Welcome to the lesson on creating functional objects in C++! In this session, we'll explore why functional objects are vital in modern C++ programming. You'll learn how they encapsulate behavior within objects for more modular, reusable code. Our aim is to understand what functional objects are and how to create and use them through practical examples.
Functional objects are objects you can call like functions. Essentially, they encapsulate a function's logic within an object. This is particularly useful for passing functions as arguments, storing them, or configuring them with state information.
Unlike standard functions, which cannot hold state, functional objects can maintain state because they are object-oriented. This allows them to keep information across function calls, enabling more complex behavior than a simple function pointer or lambda expression can provide.
Let's start by designing a basic class for our functional object. We'll set up a constructor for state initialization and define the callable operator, operator()
, to make the object callable like a function. Note that in C++ it is common to call such objects "functors". We will call them this way through this course as well.
However, in general functional programming theory, "functor" is usually used to describe a more general object, which we will talk about in the next course of this path.
Consider creating a functor to filter people based on age:
Here:
- The constructor
older_than(int limit)
initializes the age limit state to a specified value. - The
operator()
method makes the object callable like a function and contains the logic to check if a person's age is greater than the limit.
Now, let’s break down the functional objects and their supporting classes in a full example. First, define a class to represent a person:
This class has:
- A constructor
person_t(std::string name, int age)
to initialize the person’s name and age attributes. - Getter methods,
age()
andname()
, to access these attributes, ensuring encapsulation.
Next, let’s complete our main function to demonstrate filtering persons by age using the older_than
functor:
In this example:
- We created a list of
person_t
objects namedpeople
. - We instantiated the
older_than
functor with a limit of 42. - We used the functor to filter the list and print the names of people older than 42 using the Boost library’s adaptors and for_each utility functions.
Functors are great for scenarios needing encapsulated, stateful operations tied to specific conditions or parameters. Common scenarios include:
- Custom sorting or filtering criteria (as shown in our example).
- Parameterized actions like transformations on data or predicates for conditions.
- Complex algorithms where configuration parameters might change, but the core logic remains the same.
In our example, we used a functor to create a filtering mechanism for a list of persons based on age, demonstrating how functors can encapsulate logic and state effectively.
To make functors more versatile, you can use std::function
from the C++ Standard Library. This template class can encapsulate any callable target, such as functors, function pointers, or lambda expressions, providing greater flexibility.
Here, std::function<bool(const person_t&)>
abstracts the callable target, providing more flexibility and allowing us to easily switch between different callable types without changing the main logic.
You've learned the fundamentals of creating functional objects (functors) in C++. We covered:
- What functors are and their usefulness.
- Designing and implementing a functor in C++ with a practical example.
- Enhancing usability with
std::function
.
Now it's time to put theory into practice! You will move to a hands-on exercise session where you'll create and use functional objects to reinforce the concepts learned in this lesson. Ready to get started? Let's dive in!
