SmartPointers from C++11 onwards

In C++, pointers are a powerful feature that allows you to manipulate memory directly, but they can be difficult to use correctly. Improper use of pointers can lead to a variety of problems, including memory leaks, dangling pointers, and undefined behavior.

Smart pointers were introduced in C++ to help alleviate these problems by providing a higher-level abstraction for memory management. Smart pointers are classes that wrap raw pointers and manage their lifetime automatically, based on a set of rules that ensure safe memory management.

The need for smart pointers arises from the fact that it can be difficult to manually manage the lifetime of objects that are created dynamically with new. For example, if you create an object with new and then forget to delete it, you have a memory leak. On the other hand, if you delete the object twice, you have undefined behavior.

Smart pointers help to solve these problems by taking care of the memory management automatically. For example, a shared_ptr ensures that the memory it points to is deleted when the last shared_ptr pointing to it goes out of scope. Similarly, a unique_ptr ensures that the memory it points to is deleted when the unique_ptr itself goes out of scope , anyways Iet's discuss about it later.

Overall, the use of smart pointers in C++ helps to improve code safety, simplify memory management, and reduce the likelihood of memory-related bug.


To fix this issue, C++11 introduced the concept of smart pointers, which includes:

  1. shared pointers (shared_ptr<T>)
  2. unique pointers (unique_ptr<T>)
  3. weak pointers (weak_ptr<T>)

These new pointers provide more robust memory management while avoiding the pitfalls of the auto pointer and solve the problem of managing memory dynamically. Smart pointers are objects that behave like pointers but with additional features that provide automatic memory management.

Shared Pointers

A shared pointer is a smart pointer that allows multiple pointers to refer to the same dynamically allocated object. The shared pointer keeps track of the number of references to the object and automatically deletes the object when the last shared pointer that references it goes out of scope.

To use a shared pointer, you can declare a shared pointer object and initialize it with a new object, like this:

std::shared_ptr<int> ptr = shared_ptr<int> ptr(new int(101));
//OR
std::shared_ptr<int> ptr = std::make_shared<int>(101) ; // Recommended.        

In this example, a new integer object is dynamically allocated with the value 101, and a shared pointer object is created and initialized to point to the new object.

To create a new shared pointer that refers to the same object, you can use the copy constructor or the assignment operator:

//shared_ptr<int> ptr1(new int(101)); // Not recommanded
shared_ptr<int> ptr1 = make_shared<int>(101); // Recommended.
shared_ptr<int> ptr2 = ptr1; // using copy constructor 
shared_ptr<int> ptr3; 
ptr3 = ptr1; // using assignment operator         

In this example, three shared pointers are created, and all of them refer to the same dynamically allocated integer object.

Advantages of Shared Pointers

Shared pointers have several advantages over raw pointers, including:

  1. Automatic memory management: Shared pointers automatically delete the object they point to when the last shared pointer that references it goes out of scope, which helps to prevent memory leaks and dangling pointers.
  2. Reference counting: Shared pointers use reference counting to keep track of the number of pointers that reference the same object. This helps to ensure that the object is not deleted prematurely, and that it is deleted when no longer needed.
  3. Safe and easy to use: Shared pointers are safe and easy to use, as they provide automatic memory management and do not require manual memory allocation or deallocation.

Disadvantages of Shared Pointers

Shared pointers also have some disadvantages, including:

  1. Overhead: Shared pointers have some overhead associated with reference counting, which can impact performance in some cases.
  2. Circular references: Shared pointers can lead to circular references, where two or more objects reference each other, which can prevent the objects from being deleted. This can be addressed with weak pointers, which are discussed below.
  3. Limited compatibility: Shared pointers are not compatible with some C++ APIs, such as the C API, which can limit their usefulness in some situations.

When to use shared pointers:

  • Used when multiple objects need to share ownership of a resource.
  • When there are multiple references to an object, and it should not be deleted until all references have been released.
  • When a function returns an object, but the ownership needs to be shared with the caller.
  • Used in a factory method that creates objects and returns a shared pointer.

APIs provided by shared pointer:

  • std::shared_ptr<T>: A template class that manages a pointer to a dynamically allocated object of type T.
  • std::make_shared<T>(args...): A function template that creates and returns a std::shared_ptr<T> object that manages a new object of type T constructed with the given arguments.
  • std::shared_ptr<T>(ptr): A constructor that creates a std::shared_ptr<T> object that shares ownership of the object pointed to by ptr.
  • reset(): Resets the std::shared_ptr to manage no object.
  • reset(ptr): Resets the std::shared_ptr to manage the object pointed to by ptr.
  • use_count(): Returns the number of std::shared_ptr objects that share ownership of the managed object.
  • get(): Returns a pointer to the managed object.
  • operator*(): Dereferences the managed object.
  • operator->(): Returns a pointer to the managed object.
  • std::shared_ptr allows multiple smart pointers to share ownership of the same dynamically allocated object. The reference count is maintained internally, and the object is automatically deleted when the last std::shared_ptr that owns it is destroyed or reset.


Unique Pointers:

Unique Pointers are introduced in C++11 to replace the auto_ptr. Unique Pointers provide exclusive ownership semantics, which means that only one Unique Pointer can own the dynamically allocated memory at any given time. Unique Pointers provide a mechanism for custom deleters. Unique Pointers cannot be copied but can be moved. Unique Pointers are used when there is a single owner of the dynamically allocated memory.

Advantages of Unique Pointers:

  1. They are safe and efficient to use.
  2. They prevent memory leaks by ensuring that the memory allocated to a pointer is freed when the pointer goes out of scope.
  3. They are moveable but not copyable, which helps avoid issues with ownership of resources.
  4. They provide a clear ownership model, making it easier to reason about resource management in code.

Disadvantages of Unique Pointers:

  1. They cannot be shared, which can be a limitation in some cases.
  2. They require more boilerplate code compared to raw pointers.
  3. They cannot be used with arrays, as they assume ownership of a single object.

When to use Unique pointers:

  • Used when there is only one owner of an object.
  • When you need to transfer ownership of an object from one part of the code to another.
  • When a function returns an object, but it is intended to have exclusive ownership of the object.

Example:

#include <iostream>
#include <memory>
using namespace std;

int main() {
    //unique_ptr<int> uptr(new int(10)); //Not Recommended. 
    unique_ptr<int> uptr = make_unique<int>(10); // Recommended.
    cout << "The value of the pointer is: " << *uptr << endl;
    *uptr = 20;
    cout << "The new value of the pointer is: " << *uptr <<endl;
    return 0;
}        

In the above example, we create a Unique Pointer to dynamically allocated memory that stores an integer value of 10. We access the value using the dereference operator (*). Then we change the value of the dynamically allocated memory to 20. When the program exits, the Unique Pointer automatically deallocates the memory.

APIs provided by unique_ptr:

  • std::unique_ptr<T>: A template class that manages a pointer to a dynamically allocated object of type T.
  • std::make_unique<T>(args...): A function template that creates and returns a std::unique_ptr<T> object that manages a new object of type T constructed with the given arguments.
  • std::unique_ptr<T>(ptr): A constructor that creates a std::unique_ptr<T> object that takes ownership of the object pointed to by ptr.
  • reset(): Resets the std::unique_ptr to manage no object.
  • reset(ptr): Resets the std::unique_ptr to manage the object pointed to by ptr.
  • release(): Releases ownership of the managed object and returns a pointer to it.
  • get(): Returns a pointer to the managed object.
  • operator*(): Dereferences the managed object.
  • operator->(): Returns a pointer to the managed object.

  1. std::unique_ptr is designed to have a single owner for a dynamically allocated object. When the std::unique_ptr goes out of scope or is reset, the object it manages is automatically deleted.


Weak Pointers:

Weak pointers are a type of smart pointer that provides a non-owning, weak reference to an object that is managed by a shared pointer. Weak pointers are used to break circular references between objects managed by shared pointers.

The main advantage of weak pointers is that they allow you to safely reference an object managed by a shared pointer without extending the lifetime of the object. This can be useful when you need to reference an object in a callback or event handler, for example.

Weak pointers are created from shared pointers using the weak_ptr constructor. You can use the lock() method on a weak pointer to obtain a shared pointer to the managed object, but the lock() method will return an empty shared pointer if the object has already been deleted.

Here is an example of using a weak pointer:

#include <iostream>
#include <memory>
udsing namespace std;

class MyClass {
public:
    MyClass() { cout << "MyClass constructor" << endl; }
    ~MyClass() {cout << "MyClass destructor" << endl; }
};

int main() {
    shared_ptr<MyClass> sharedPtr = make_shared<MyClass>();
    weak_ptr<MyClass> weakPtr = sharedPtr;

    if (!weakPtr.expired()) {
        shared_ptr<MyClass> sharedPtr2 = weakPtr.lock();
        cout << "Managed object still exists" << endl;
    } else {
        cout << "Managed object has been deleted" << endl;
    }

    sharedPtr.reset();

    if (!weakPtr.expired()) {
        shared_ptr<MyClass> sharedPtr2 = weakPtr.lock();
        cout << "Managed object still exists" << endl;
    } else {
        cout << "Managed object has been deleted" << endl;
    }

    return 0;
}

/*
amit@DESKTOP-9LTOFUP:~/OmPracticeC++$ ./a.out
MyClass constructor
Managed object still exists
MyClass destructor
Managed object has been deleted

*/        

In this example, we create a shared pointer to a MyClass object and then create a weak pointer to the same object. We use the expired() method to check if the managed object still exists, and then use the lock() method to obtain a shared pointer to the managed object.

When the shared pointer is reset, the managed object is deleted and the weak pointer's expired() method will return true.

Advantages of Weak Pointers:

  1. They provide a way to safely reference objects managed by shared pointers without extending their lifetime.
  2. They help avoid circular references and memory leaks.
  3. They are efficient and lightweight compared to shared pointers.

Disadvantages of Weak Pointers:

  1. They cannot be used to access the managed object directly, as the lock() method returns a shared pointer.
  2. They require the use of shared pointers, which may not be suitable in all cases.
  3. Risk of dangling pointers: If the weak pointer is used after the object it is pointing to has been deleted, it can lead to a runtime error or crash. Therefore, it is important to check if the weak pointer is valid before dereferencing it.
  4. Increased complexity: Using weak pointers can add additional complexity to the code, as they require careful management to avoid errors. Developers need to be aware of the relationships between objects and how they are managed to prevent errors.
  5. Limited functionality: Weak pointers can only be used to observe the lifetime of an object and cannot be used to access its members or methods. This can limit their usefulness in some scenarios where stronger access is needed.
  6. Performance overhead: There is a small performance overhead associated with weak pointers as they require additional memory to store the reference count and can also add additional processing overhead when checking for null references.

Despite these disadvantages, weak pointers are a useful tool for managing object lifetimes in C++ and can help prevent memory leaks and improve code reliability.

When to use weak pointers:

  • Used to observe objects that are owned by shared pointers, without extending the lifetime of the object.
  • When there is a need to avoid circular references between shared pointers, which can lead to memory leaks.

APIs provided by weak_ptr:

  • std::weak_ptr<T>: A template class that provides a non-owning reference to a std::shared_ptr<T>.
  • lock(): Returns a std::shared_ptr<T> object that shares ownership of the managed object with the std::weak_ptr<T>.
  • expired(): Returns true if the std::shared_ptr<T> that the std::weak_ptr<T> refers to has already been destroyed.

std::weak_ptr is used to avoid circular references between std::shared_ptr objects. It allows a std::shared_ptr to be safely used without preventing the managed object from being deleted when no std::shared_ptr is referencing it anymore.

Few more commonly used functions and operations associated with smart pointers in C++:

  1. unique_ptr::reset(): This function resets the unique_ptr by releasing its ownership of the pointed object. If the unique_ptr was the sole owner of the object, the object is deleted. The function takes no arguments and returns void.
  2. shared_ptr::reset(): This function resets the shared_ptr by releasing its ownership of the pointed object. If the shared_ptr was the last owner of the object, the object is deleted. The function takes no arguments and returns void.
  3. shared_ptr::use_count(): It is a member function of the shared_ptr class that returns the number of shared_ptr objects that share ownership of the same pointer. It takes no arguments and returns an integer.
  4. make_unique<T>(): This function creates a unique_ptr object that owns a newly constructed object of type T with the specified constructor arguments. The function returns a unique_ptr that owns the object. Using make_unique is usually more efficient than using?unique_ptr<T>(new T())?as it eliminates the overhead of separate allocations.
  5. make_shared<T>(): This function creates a shared_ptr object that owns a newly constructed object of type T with the specified constructor arguments. The function returns a shared_ptr that owns the object. It internally creates an object and a control block in a single memory allocation. Using make_shared is usually more efficient than using shared_ptr<T>(new T()) as it eliminates the overhead of separate allocations.
  6. move(): This function transfers the ownership of a smart pointer from one object to another. It takes a reference to the smart pointer object and returns an rvalue reference to the same object. This is useful for transferring ownership of a resource from one object to another.
  7. weak_ptr::lock(): It is a member function of the weak_ptr class. This function returns a shared_ptr that shares ownership of the same object as the weak_ptr if the object still exists, or an empty shared_ptr if the object has been deleted. It takes no arguments and returns a shared_ptr.
  8. weak_ptr::expired() The expired() function of a weak_ptr checks whether the shared_ptr that owns the object still exists or not. If the shared_ptr is still valid, then expired() returns false.

Below examples demonstrate above functions:

#include <iostream>
#include <memory>
#include <string>
using namespace std;

class MyClass {
public:
    MyClass(const int& value, const string& str) : _value(value), _str(str) {
        cout << "Constructor called for MyClass(" << value << ", " << str << ")" << endl;
    }
    
    void Print() const {
        cout << "MyClass object: " << _value << ", " << _str << endl;
    }
    
private:
    int _value;
    string _str;
};

int main() {
    // Shared pointer example
    shared_ptr<MyClass> sptr1 = make_shared<MyClass>(10, "Hello");
    cout << "Shared pointer count: " << sptr1.use_count() << endl;
    {
        shared_ptr<MyClass> sptr2 = sptr1;
        cout << "Shared pointer count: " << sptr1.use_count() << endl;
    }
    cout << "Shared pointer count: " << sptr1.use_count() << endl;
    
    // Unique pointer example
    unique_ptr<MyClass> uptr1 = make_unique<MyClass>(20, "World");
    uptr1->Print();
    unique_ptr<MyClass> uptr2 = move(uptr1);
    if (uptr1 == nullptr) {
        cout << "Unique pointer 1 is null" << endl;
    }
    if (uptr2 != nullptr) {
        uptr2->Print();
    }
    
    // Weak pointer example
    weak_ptr<MyClass> wptr1 = sptr1;
    if (auto sptr3 = wptr1.lock()) {
        sptr3->Print();
    } else {
        cout << "Shared pointer is null" << endl;
    }
    
    return 0;
}
/*
O/P => 
Constructor called for MyClass(10, Hello)
Shared pointer count: 1
Shared pointer count: 2
Shared pointer count: 1
Constructor called for MyClass(20, World)
MyClass object: 20, World
Unique pointer 1 is null
MyClass object: 20, World
MyClass object: 10, Hello
*/        

The above function is self-explanatory.

Example 2:

In Below example tried to cover all the scenario of all 3 smart pointers.

#include <iostream>
#include <memory>

using namespace std;

class MyClass {
? ? public:
? ? ? ? int data;
? ? ? ? MyClass() { cout << "MyClass Constructor" << endl; }
? ? ? ? ~MyClass() { cout << "MyClass Destructor" << endl; }
};

int main() {
? ? // create a shared pointer using make_shared
? ? shared_ptr<MyClass> my_shared_ptr = make_shared<MyClass>();
? ? cout << "my_shared_ptr use count before weak ptr assignment : " << my_shared_ptr.use_count() << endl;


? ? // create a weak pointer from the shared pointer
? ? weak_ptr<MyClass> my_weak_ptr = my_shared_ptr;
? ? cout << "my_shared_ptr use count after weak ptr assignment: " << my_shared_ptr.use_count() << endl;
? ? cout << "my_weak_ptr use count: " << my_weak_ptr.use_count() << endl;

? ? // use lock to get a shared pointer from the weak pointer
? ? shared_ptr<MyClass> my_shared_ptr_2 = my_weak_ptr.lock();
? ? if (my_shared_ptr_2 != nullptr) {
? ? ? ? cout << "my_shared_ptr_2 use count: " << my_shared_ptr_2.use_count() << endl;
? ? } else {
? ? ? ? cout << "my_weak_ptr is expired" << endl;
? ? }
? ??
? ? // Empty unique_ptr object
? ? unique_ptr<int> ptr;
	?
? ? // Check if unique pointer object is empty
? ? if(!ptr)
? ? ? ? cout<<"ptr is empty"<<endl;
	?
	 // Check if unique pointer object is empty
? ? if(ptr == nullptr)
? ? ? ? cout<<"ptr is empty"<<endl;
	?
? ? // can not create unique_ptr object by initializing through assignment
? ? // unique_ptr<MyClass> taskPtr2 = new MyClass(); // Compile Error

? ? // create a unique pointer using make_unique
? ? unique_ptr<MyClass> my_unique_ptr = make_unique<MyClass>();
? ? cout << "my_unique_ptr data: " << my_unique_ptr->data << endl;
?
? ? // Create a unique_ptr object through raw pointer
? ? unique_ptr<MyClass> my_unique_raw_ptr(new MyClass());

? ? // Check if taskPtr is empty or it has an associated raw pointer
? ? if(my_unique_raw_ptr!= nullptr)
? ? ? ? cout<<"my_unique_raw_ptr is? not empty"<<endl;

? ? cout<<"Reset the my_unique_raw_ptr"<<endl;
? ? // Reseting the unique_ptr will delete the associated
? ? // raw pointer and make unique_ptr object empty
	
	 my_unique_raw_ptr.reset();?
	?
	// reseting is only possible when used with raw pointer i.e new operator
	// my_unique_ptr.reset();? // Runtime error , Segmentation fault (core dumped)
	?
? ? // Check if my_unique_raw_ptr is empty or it has an associated raw pointer
		if(my_unique_raw_ptr == nullptr)
? ? cout<<"my_unique_raw_ptr is? empty"<<endl;


? ? // unique_ptr object is Not copyable
	// unique_ptr<MyClass> my_unique_ptr1 = my_unique_ptr; //compile error
? ? //my_unique_raw_ptr =? my_unique_ptr; //compile error


? ? // unique_ptr object is Not copyable
	//unique_ptr<MyClass> my_unique_ptr_2 =? my_unique_ptr;

? ? // use move to transfer ownership of the unique pointer
? ? {
? ? ? ? unique_ptr<MyClass> my_unique_ptr_2 = move(my_unique_ptr);
? ? ? ? cout << "my_unique_ptr_2 data: " << my_unique_ptr_2->data << endl;
? ? ? ? //my_unique_ptr_2 goes out of scope and calls the destructor after this block
		// if raw pointer is used it deletes the assocaited raw pointer after this block
? ? }


? ? // Create a unique_ptr object through raw pointer
? ? unique_ptr<MyClass>? my_unique_ptr_3(new MyClass);
	?
	if(my_unique_ptr_3 != nullptr)
		cout<<"my_unique_ptr_3 is not empty"<<endl;
	?
	// Release the ownership of object from raw pointer
	MyClass* ptr1 = my_unique_ptr_3.release();
? ? if(my_unique_ptr_3== nullptr)
? ? ? ? cout<<"my_unique_ptr_3 is empty after my_unique_ptr_3.release() i.e releasing ownership "<<endl;
? ? delete ptr1;


? ?
? ? // use reset to release ownership of the shared pointer
? ? my_shared_ptr.reset();
? ? cout << "my_shared_ptr is null after reset to release ownership : " << (my_shared_ptr == nullptr) << endl;


? ? // use reset to release ownership of the unique pointer
? ? my_unique_ptr.reset();
? ? cout << "my_unique_ptr is null after reset to release ownership: " << (my_unique_ptr == nullptr) << endl;


? ? return 0;
}
/*
MyClass Constructor
my_shared_ptr use count before weak ptr assignment : 1
my_shared_ptr use count after weak ptr assignment: 1
my_weak_ptr use count: 1
my_shared_ptr_2 use count: 2
ptr is empty
ptr is empty
MyClass Constructor
my_unique_ptr data: 0
MyClass Constructor
my_unique_raw_ptr is? not empty
Reset the my_unique_raw_ptr
MyClass Destructor
my_unique_raw_ptr is? empty
my_unique_ptr_2 data: 0
MyClass Destructor
MyClass Constructor
my_unique_ptr_3 is not empty
my_unique_ptr_3 is empty after my_unique_ptr_3.release() i.e releasing ownership
MyClass Destructor
my_shared_ptr is null after reset to release ownership : 1
my_unique_ptr is null after reset to release ownership: 1
MyClass Destructor
*/        

Please read the above program carefully, then you will understand yourself.


What is the problem when a container is used to store objects that are managed by raw pointers or auto pointers, it can lead to issues such as double deletion or dangling pointers.

When auto_ptr was used with containers like vector, it caused issues due to its unique ownership semantics. The main problem was that auto_ptr does not support proper copy semantics. When an auto_ptr was copied, ownership of the managed resource was transferred, leaving the source auto_ptr in a null state. This meant that adding an auto_ptr to a container like vector would result in transferring ownership of the resource and leaving the original auto_ptr in an invalid state.

Consider the following example:

std::vector<std::auto_ptr<int>> vec; 
std::auto_ptr<int> ptr1(new int(42)); 
vec.push_back(ptr1);         

In this case, after pushing ptr1 into the vector, ptr1 becomes null. If you try to access ptr1 later, it would result in undefined behavior.

Also, if the vector is resized or elements are removed, the auto_ptr objects can become invalid. For example, if an element is removed from the middle of the vector, the auto_ptr objects in the vector after the removed element will be shifted to fill the gap. This shift can cause the auto_ptr objects to point to the wrong elements or even to be invalidated.

To solve these issues, modern C++ provides smart pointers such as unique_ptr and shared_ptr that can be used instead of raw pointers or auto_ptr objects. These smart pointers manage the memory of the pointed-to object and ensure that it is properly deallocated when the pointer goes out of scope or is no longer needed.

For example, consider the following code that uses unique_ptr and shared_ptr:

vector<unique_ptr<MyClass>> vec1; 
vec1.push_back(make_unique<MyClass>()); 
vector<shared_ptr<MyClass>> vec2; 
vec2.push_back(make_shared<MyClass>());         

In this code, the vector objects are used to store unique_ptr and shared_ptr objects. These smart pointers ensure that the pointed-to objects are properly deallocated when the pointers go out of scope or are removed from the vector. Moreover, since unique_ptr objects cannot be copied, the issue of reference counting is avoided.

Which method to use for creating the unique pointer:

There are 2 ways to make the unique pointer as below:

1.Create a unique_ptr using make_unique

unique_ptr<MyClass> my_unique_ptr = make_unique<MyClass>();

2. Create a unique_ptr object through raw pointer

unique_ptr<MyClass> my_unique_raw_ptr(new MyClass());

Both allocate memory on the heap. However, the new operator is not used when calling make_unique<MyClass>(). Instead, make_unique uses new internally to allocate the memory and construct the object, and returns a unique_ptr that owns the memory.

So, the main difference between the two methods is that make_unique is preferred because it's safer and less error-prone. It's a single-step operation that both allocates the memory and constructs the object, and returns a unique_ptr that owns the memory.

On the other hand, using new to create a unique_ptr i.e using the raw pointer requires two separate steps: allocating the memory with new, and then passing the raw pointer to the unique_ptr constructor. This increases the risk of errors, such as forgetting to delete the object, or accidentally leaking memory if an exception is thrown before the unique_ptr is created.

Therefore, it's generally recommended to use make_unique over manually calling new when creating unique_ptrs.


Application of smart pointers in Embedded systems:

Smart pointers can be used in embedded systems just like any other C++ feature, with some considerations and adaptations to the specific constraints of embedded systems. Here are a few ways smart pointers can be used in embedded systems:

  1. Resource Management: Smart pointers, such as std::unique_ptr and std::shared_ptr, can be used to manage resources like dynamically allocated memory, hardware peripherals, or file handles in embedded systems. They help ensure proper resource cleanup and avoid memory leaks or resource leaks.
  2. Memory Optimization: In resource-constrained environments, managing memory efficiently is crucial. Smart pointers can be used to allocate and deallocate memory dynamically, providing memory management features like automatic deallocation and preventing memory leaks. For example, std::unique_ptr can be used to manage dynamic memory allocation with automatic deallocation when the pointer goes out of scope.
  3. Ownership Transfer: Smart pointers facilitate ownership transfer of resources between different components or modules in an embedded system. This can be useful when passing ownership of a resource from one module to another or when sharing resources among multiple components.
  4. Custom Memory Allocators: In some cases, embedded systems may require custom memory allocation strategies or the use of specialized memory allocators. Smart pointers can be customized to use specific memory allocators or custom memory management schemes tailored to the requirements of the embedded system.

It's important to note that the usage of smart pointers in embedded systems should consider the specific constraints of the system, such as limited memory, real-time requirements, and hardware limitations. Careful consideration should be given to the use of dynamic memory allocation, as it may not be suitable for all scenarios in embedded systems.

Additionally, some embedded systems may have their own resource management mechanisms or specific guidelines that govern memory management. In such cases, the usage of smart pointers may need to align with the system's requirements and constraints.

Summary:

Smart pointers are a type of pointer used in C++ that automatically manage the lifetime of dynamically allocated objects. Here are some common scenarios where different types of smart pointers can be useful:

  1. Shared pointer:Shared pointers are used to manage the ownership of an object that can be shared among multiple parts of the code. This type of pointer keeps a reference count of how many pointers are pointing to the object and automatically deletes the object when the reference count reaches zero. Shared pointers are useful when multiple parts of the code need access to the same object, but you want to avoid memory leaks and ensure the object is only deleted when all the references to it are gone.
  2. Unique pointer:Unique pointers are used to manage the ownership of an object that can only be owned by a single part of the code. This type of pointer ensures that there is only one pointer pointing to the object, which means it can't be accidentally deleted or modified by other parts of the code. Unique pointers are useful when you want to transfer ownership of an object from one part of the code to another or when you want to ensure that an object is deleted as soon as it's no longer needed.
  3. Weak pointer:Weak pointers are used to keep a non-owning reference to a shared object. This type of pointer doesn't increment the reference count of the object, so it can't prevent the object from being deleted. Weak pointers are useful when you want to have a reference to a shared object but don't want to keep the object alive longer than necessary or if you want to avoid circular references that can lead to memory leaks.


Thanks for reading, please comment if you have any.

要查看或添加评论,请登录

Amit Nadiger的更多文章

  • List of C++ 17 additions

    List of C++ 17 additions

    1. std::variant and std::optional std::variant: A type-safe union that can hold one of several types, useful for…

  • List of C++ 14 additions

    List of C++ 14 additions

    1. Generic lambdas Lambdas can use auto parameters to accept any type.

    6 条评论
  • Passing imp DS(vec,map,set) to function

    Passing imp DS(vec,map,set) to function

    In Rust, we can pass imp data structures such as , , and to functions in different ways, depending on whether you want…

  • Atomics in C++

    Atomics in C++

    The C++11 standard introduced the library, providing a way to perform operations on shared data without explicit…

    1 条评论
  • List of C++ 11 additions

    List of C++ 11 additions

    1. Smart Pointers Types: std::unique_ptr, std::shared_ptr, and std::weak_ptr.

    2 条评论
  • std::lock, std::trylock in C++

    std::lock, std::trylock in C++

    std::lock - cppreference.com Concurrency and synchronization are essential aspects of modern software development.

    3 条评论
  • std::unique_lock,lock_guard, & scoped_lock

    std::unique_lock,lock_guard, & scoped_lock

    C++11 introduced several locking mechanisms to simplify thread synchronization and prevent race conditions. Among them,…

  • Understanding of virtual & final in C++ 11

    Understanding of virtual & final in C++ 11

    C++ provides powerful object-oriented programming features such as polymorphism through virtual functions and control…

  • Importance of Linux kernal in AOSP

    Importance of Linux kernal in AOSP

    The Linux kernel serves as the foundational layer of the Android Open Source Project (AOSP), acting as the bridge…

    1 条评论
  • AOSP

    AOSP

    Android System Development AOSP stands for the Android Open Source Project. Its the foundation of the Android operating…

社区洞察

其他会员也浏览了