Article by Ayman Alheraki in October 12 2024 03:26 PM
Procedural programming (also known as structured programming) focuses on dividing programs into procedures or functions to improve clarity and organization. While Object-Oriented Programming (OOP) has become the dominant approach in modern software development, procedural programming remains powerful and efficient when used correctly. Over the past two decades, this approach has been instrumental in building effective and secure programs, though it sometimes struggled with memory management and safety issues.
However, with the introduction of modern C++ features starting from C++11 and continuing through C++20, procedural programming has been significantly strengthened. New additions such as smart pointers for memory management, improvements in multithreading, and enhanced functional programming features have made procedural programming even more efficient and secure. These improvements not only add flexibility to C++ but also provide enhanced performance and safety.
One of the biggest challenges in procedural C++ programming used to be manual memory management with raw pointers. Common errors like memory leaks were frequent and could lead to instability or crashes. With the introduction of smart pointers such as std::unique_ptr
and std::shared_ptr
in C++11, memory management is now automated, reducing the risk of leaks and making the code safer and easier to maintain.
void process() {
std::unique_ptr<int> ptr = std::make_unique<int>(100);
std::cout << "Value: " << *ptr << std::endl;
} // Memory is automatically freed when the function exits
Benefits:
No need for manual memory deallocation using delete
.
Reduces the likelihood of memory leaks.
Automatically manages object lifetimes based on scope.
C++11 introduced built-in support for multithreading through the std::thread
library, making it easier to create and manage concurrent tasks without relying on external libraries. This addition brought multithreading directly into the C++ standard, allowing developers to take full advantage of multi-core processors.
void task(int n) {
std::cout << "Task running: " << n << std::endl;
}
int main() {
std::thread t1(task, 1);
std::thread t2(task, 2);
t1.join(); // Waits for t1 to finish
t2.join(); // Waits for t2 to finish
}
Benefits:
Built-in support for concurrent programming.
Efficient use of multi-core systems.
Fine control over synchronization using tools like std::mutex
and std::future
.
Another major improvement in C++11 and beyond is the addition of functional programming features, making C++ more flexible and allowing developers to adopt modern programming styles. With the introduction of lambda expressions and higher-order functions, C++ now supports more concise and expressive functional programming techniques.
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// Using a lambda to print the numbers
std::for_each(numbers.begin(), numbers.end(), [](int n) {
std::cout << n << " ";
});
return 0;
}
Benefits:
Reduces complexity and increases clarity by allowing small functions to be defined inline.
Supports functional programming paradigms with features like lambda capturing of local variables.
Enhances flexibility for tasks like sorting, filtering, or applying transformations to collections.
C++11 introduced constexpr
, which allows functions and expressions to be evaluated at compile time, improving performance by reducing runtime computation. C++20 has further enhanced constexpr
capabilities, allowing even more functions and logic to be executed during compilation.
constexpr
:
constexpr int factorial(int n) {
return (n <= 1) ? 1 : (n * factorial(n - 1));
}
int main() {
constexpr int result = factorial(5);
std::cout << "Factorial of 5: " << result << std::endl;
}
Benefits:
Enables computations to be done at compile time, reducing runtime overhead.
Improves performance, especially in resource-constrained environments.
Increases flexibility in procedural programming by allowing constant expressions to be used more widely.
auto
and Type DeductionWith C++11, the introduction of the auto
keyword allows the compiler to automatically deduce the type of variables from their initialization. This reduces verbosity, especially when dealing with complex types, and improves readability.
auto
:
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// Using auto for type deduction
for (auto n : numbers) {
std::cout << n << " ";
}
return 0;
}
Benefits:
Reduces the need for explicitly specifying complex types.
Improves code readability and maintainability.
Minimizes type errors that can occur when manually specifying types.
C++20 introduces several improvements to templates, making procedural programming even more powerful. One of the most notable enhancements is the introduction of concepts, which allow you to define constraints on template parameters, ensuring type correctness and reducing template-related errors.
template<typename T>
requires std::integral<T> // Restricting the type to integral types only
T add(T a, T b) {
return a + b;
}
int main() {
std::cout << add(5, 10) << std::endl;
// std::cout << add(5.5, 10.2) << std::endl; // Error: Not an integral type
}
Benefits:
Provides clearer and more intuitive error messages.
Ensures that only valid types are used with templates.
Improves code readability and correctness.
C++11 introduced range-based for loops, which simplify iteration over containers like arrays, vectors, and lists. C++20 takes this further with the ranges library, which provides a more functional approach to handling collections by combining the power of algorithms and iterators.
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
for (int n : numbers) {
std::cout << n << " ";
}
return 0;
}
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
for (int n : numbers | std::views::filter([](int n) { return n % 2 == 0; })) {
std::cout << n << " "; // Output: 2 4
}
return 0;
}
Benefits:
Simplifies iteration over collections.
Supports a functional programming style with powerful filtering, mapping, and transformation capabilities.
Reduces boilerplate code for handling ranges and collections.
Procedural programming in C++ has evolved significantly with the features introduced in C++11 and later versions. From smart pointers that revolutionized memory management to built-in multithreading capabilities, functional programming features, and compile-time expressions, modern C++ offers a wide range of tools to improve performance, safety, and flexibility in structured programming.
C++20 continues to expand on these improvements, making procedural programming a viable and powerful approach for modern software development. With the addition of concepts, auto
type deduction, and range-based operations, procedural programming in C++ is more efficient, readable, and easier to maintain than ever before, without the need to always rely on object-oriented techniques.