Dynamic memory allocation allows you to request memory from the heap instead of the stack.
int* ptr{ new int{4} }; // dynamically allocate an integer and assign the address to ptr
delete ptr; // return the memory pointed to by ptr to the operating system
ptr = nullptr; // set ptr to be a null pointer
int* value { new (std::nothrow) int }; // value will be set to a null pointer if the allocation fails
if (!value) // handle case where new returned null
{
// Handle allocation failure
}
A memory leak occurs when dynamically allocated memory is not properly deallocated.
void doSomething() { int* ptr{ new int{} }; } // pointer goes out of scope without delete
int value{ 5 };
int* ptr{ new int{} }; // allocate memory
delete ptr; // delete the memory
ptr = &value; // reassign pointer to address of value
int* array{ new int[length]{} }; // dynamically allocate an array
delete[] array; // deallocate the array
Destructors are special member functions that are called when an object is destroyed.
class IntArray
{
private:
int* m_array;
public:
~IntArray() // destructor
{
delete[] m_array; // dynamically delete the array we allocated earlier
}
};
int value{ 5 };
int* ptr{ &value };
int** ptrptr { &ptr };
std::cout << **ptrptr << '\n'; // dereference to get pointer to int, dereference again to get int value
int** array { new int*[10] }; // allocate an array of 10 int pointers — these are our rows
for (int count { 0 }; count < 10; ++count)
// deleting
for (int count { 0 }; count < 10; ++count)
delete[] array[count];
delete[] array; // this needs to be done last
void* ptr {}; // ptr is a void pointer
static_cast
it back to the appropriate type first.int* intPtr{ static_cast<int*>(voidPtr) }; // cast void pointer to an int pointer
Function pointers are pointers that point to functions.
int foo() { return 5; }
int main()
{
auto fcnPtr{ &foo };
(*fcnPtr)(); // call function foo() through fcnPtr. explicit dereference
fcnPtr(); // call function foo() through fcnPtr. implicit dereference
return 0;
}
void sort(bool (*comparisonfnc)(int, int)) {}
bool ascending(int a, int b) { return a < b; }
sort(ascending); // pass function as argument
std::function
#include <functional>
using ValidateFunction = std::function<bool(int, int)>;
bool validate(ValidateFunction fcn);
#include <sstream>
int main(int argc, char* argv[])
{
std::stringstream convert{ argv[1] }; // set up a stringstream variable named convert, initialized with the input from argv[1]
int myint{};
if (!(convert >> myint)) // do the conversion
myint = 0; // if conversion fails, set myint to a default value
}
Ellipsis allows functions to accept a variable number of arguments.
#include <cstdarg> // needed to use ellipsis
double findAverage(int count, ...)
{
int sum{ 0 };
std::va_list list; // declare a va_list
va_start(list, count); // initialize the va_list
for (int arg{ 0 }; arg < count; ++arg)
{
sum += va_arg(list, int); // get values out of the ellipsis
}
va_end(list); // cleanup the va_list
return static_cast<double>(sum) / count;
}
Lambdas are anonymous functions that can be defined within other functions.
[ captureClause ] ( parameters ) -> returnType
{
statements;
}
auto found{ std::find_if(arr.begin(), arr.end(),
[](std::string_view str) // here's our lambda, no capture clause
{
return str.find("nut") != std::string_view::npos;
}) };
int x = 5;
auto lambda = [x]() mutable { x += 2; }; // capture x by value, mutable to modify
lambda();
auto refLambda = [&x]() { x += 2; }; // capture x by reference
refLambda();
auto allByValue = [=]() {}; // auto copy capture all needed variables
auto allByRef = [&]() {}; // auto reference capture all needed variables
std::function
void repeat1(const std::function<void(int)>& fn); // Case 1: use a std::function parameter
template <typename T>
void repeat2(const T& fn); // Case 2: use a function template with a type template parameter
void repeat3(const auto& fn); // Case 3: use the abbreviated function template syntax (C++20)
void repeat4(void (*fn)(int)); // Case 4: use function pointer (only for lambda with no captures)
The memory that a program uses is typically divided into a few different areas, called segments: