see: 2020 Full-time Interview CCJ’s Preparation (1): Commonly Asked C++ Interview Questions, at this link.
see: 2020 Full-time Interview CCJ’s Preparation (2): Commonly Asked C++ Interview Questions in a Table, at this link.
see: https://docs.microsoft.com/en-us/cpp/cpp/smart-pointers-modern-cpp?view=msvc-160
Smart pointers are defined in the std namespace in the <memory>
header file. They are crucial to the RAII or Resource Acquisition Is Initialization programming idiom. The main goal of this idiom is to ensure that resource acquisition occurs at the same time that the object is initialized, so that all resources for the object are created and made ready in one line of code.
In practical terms, the main principle of RAII is to give ownership of any heap-allocated resource—for example, dynamically-allocated memory or system object handles — to a stack-allocated object whose destructor contains the code to delete or free the resource and also any associated cleanup code.
In most cases, when you initialize a raw pointer or resource handle to point to an actual resource, pass the pointer to a smart pointer
immediately. In modern C++, raw pointers are only used in small code blocks of limited scope, loops, or helper functions where performance is critical and there is no chance of confusion about ownership.
The following example compares a raw pointer declaration to a smart pointer declaration.
void UseRawPointer()
{
// Using a raw pointer -- not recommended.
Song* pSong = new Song(L"Nothing on You", L"Bruno Mars");
// Use pSong...
// Don't forget to delete!
delete pSong;
}
void UseSmartPointer()
{
// Declare a smart pointer on stack and pass it the raw pointer.
unique_ptr<Song> song2(new Song(L"Nothing on You", L"Bruno Mars"));
// Use song2...
wstring s = song2->duration_;
//...
} // song2 is deleted automatically here.
As shown in the example, a smart pointer
is a class template
that you declare on the stack, and initialize by using a raw pointer that points to a heap-allocated object. After the smart pointer is initialized, it owns the raw pointer. This means that the smart pointer is responsible for deleting the memory that the raw pointer specifies. The smart pointer destructor contains the call to delete, and because the smart pointer is declared on the stack
, its destructor is invoked when the smart pointer goes out of scope, even if an exception is thrown somewhere further up the stack.
Access the encapsulated pointer by using the familiar pointer operators, ->
and *
, which the smart pointer class overloads to return the encapsulated raw pointer.
The C++ smart pointer idiom resembles object creation in languages such as C#: you create the object and then let the system take care of deleting it at the correct time.
The difference is that no separate garbage collector
runs in the background;
memory is managed through the standard C++ scoping rules
so that the runtime environment is faster and more efficient
.
The following example shows how a unique_ptr
smart pointer type from the C++ Standard Library could be used to encapsulate a pointer to a large object.
class LargeObject
{
public:
void DoSomething(){}
};
void ProcessLargeObject(const LargeObject& lo){}
void SmartPointerDemo()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject());
//Call a method on the object
pLarge->DoSomething();
// Pass a reference to a method.
ProcessLargeObject(*pLarge);
} //pLarge is deleted automatically when function block goes out of scope.
The example demonstrates the following essential steps for using smart pointers.
1) Declare the smart pointer as an automatic (local) variable. (Do not use the new or malloc expression on the smart pointer itself.)
2) In the type parameter, specify the pointed-to type of the encapsulated pointer.
3) Pass a raw pointer to a new-ed object in the smart pointer constructor. (Some utility functions or smart pointer constructors do this for you.)
4) Use the overloaded ->
and *
operators to access the object.
) `Let the smart pointer delete the object.
Smart pointers are designed to be as efficient as possible both in terms of memory and performance
. For example, the only data member in unique_ptr
is the encapsulated pointer. This means that unique_ptr
is exactly the same size as that pointer, either four bytes or eight bytes. Accessing the encapsulated pointer by using the smart pointer overloaded * and ->
operators is not significantly slower than accessing the raw pointers directly.
Smart pointers have their own member functions, which are accessed by using "dot"
notation. For example, some C++ Standard Library smart pointers have a reset member function that releases ownership of the pointer. This is useful when you want to free the memory owned by the smart pointer before the smart pointer goes out of scope, as shown in the following example.
void SmartPointerDemo2()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject());
//Call a method on the object
pLarge->DoSomething();
// Free the memory before we exit function block.
pLarge.reset();
// Do some other work...
}
Smart pointers usually provide a way to access their raw pointer directly. C++ Standard Library smart pointers have a get
member function for this purpose, and CComPtr
has a public p
class member. By providing direct access to the underlying pointer, you can use the smart pointer to manage memory in your own code and still pass the raw pointer to code that does not support smart pointers.
void SmartPointerDemo4()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject());
//Call a method on the object
pLarge->DoSomething();
// Pass raw pointer to a legacy API
LegacyLargeObjectFunction(pLarge.get());
}
Use these smart pointers as a first choice for encapsulating pointers to plain old C++ objects (POCO).
unique_ptr
:Allows exactly one owner of the underlying pointer. Use as the default choice for POCO unless you know for certain that you require a shared_ptr
. unique_ptr
can be moved to a new owner, but not copied or shared. Replaces auto_ptr
, which is deprecated. Compare to boost::scoped_ptr
, unique_ptr
is small and efficient; the size is one pointer and it supports rvalue references for fast insertion and retrieval from C++ Standard Library collections. Header file: <memory>
.
shared_ptr
:Reference-counted smart pointer. Use when you want to assign one raw pointer to multiple owners, for example, when you return a copy of a pointer from a container but want to keep the original. The raw pointer is not deleted until all shared_ptr
owners have gone out of scope or have otherwise given up ownership. The size is two pointers; one for the object and one for the shared control block that contains the reference count. Header file: <memory>
. `
weak_ptr
:Special-case smart pointer for use in conjunction with shared_ptr
. A weak_ptr
provides access to an object that is owned by one or more shared_ptr
instances, but does not participate in reference counting. Use when you want to observe an object, but do not require it to remain alive. Required in some cases to break circular references
between shared_ptr
instances. Header file: <memory>
.
Things to learn in this article:
Importance of pointer in C/C++.
Problems with normal pointer.
Why smart pointers introduce.
Types of smart pointer.
Pointers are used for accessing the resources which are external to the program like heap memory
. So for accessing the heap memory if anything is created inside heap memory Pointers are used.
Let’s understand what’s the main problem with the normal pointer by taking a small C++ program using this illustration.
#include <iostream>
using namespace std;
class Rectangle {
private:
int length;
int breadth;
};
void fun()
{
// By taking a pointer p and
// dynamically creating object
// of class rectangle
Rectangle* p = new Rectangle();
}
int main()
{
// Infinite Loop
while (1) {
fun();
}
}
So what happens is it’ll have a pointer ‘p’
and this will be pointing to an object of type rectangle which will have length and breadth. Once the function ends
this ‘p’
is deleted because p
is a local variable to the function which will end but a new rectangle that is allocated inside heap that will not be deallocated
. And it will return it will come back as its infinite loop again it will call, so again new p
is created then again a new object is created for rectangle same length and breadth. So then what about the previous object, it’ll not be deleted, again extra new object, it’ll also not deleted. So every time it will create an object but not it’s deleting so this is causing leakage of memory
from the heap memory. Like for memory for length and breadth is unused though it’s allocated but not in use. So slowly the entire heap memory may become unused because it’s infinite. So at one stage because of a lack of heap memory, the program will crash. So at the last of the fun() we should use ‘delete p’
if we do not mention this, this will cause a very severe problem. So because of the laziness or carelessness of the programmer this type of problem may arise. So to help the programmer C++ 11 takes responsibility and introduces smart pointers.
The problem with heap memory is that when you don’t need it you must deallocate
itself. So mostly the programmers are too lazy in writing the code for deallocation of objects and that causes severe problem like memory leak which will cause the program to crash. So the languages like Java, C#, .Net Framework they provide a garbage collection mechanism to deallocate
the object which is not in use.
So in C++ 11, it introduces smart pointers that automatically manage memory
and they will deallocate the object when they are not in use when the pointer is going out of scope automatically it’ll deallocate the memory.
Consider the following simple C++ code with normal pointers.
brightness_4
MyClass* ptr = new MyClass();
ptr->doSomething();
// We must do delete(ptr) to avoid memory leak
Using Smart Pointers
, we can make pointers to work in a way that we don’t need to explicitly call delete. A smart pointer is a wrapper class over a pointer with an operator like *
and ->
overloaded. The objects of smart pointer class look like a pointer but can do many things that a normal pointer can’t, like automatic destruction (yes, we don’t have to explicitly use delete), reference counting and more.
The idea is to take a class
with a pointer, destructor and overloaded operators like *
and ->
. Since the destructor is automatically called when an object goes out of scope
, the dynamically allocated memory would automatically be deleted (or reference count can be decremented). Consider the following simple smart ptr
class.
#include <iostream>
using namespace std;
class SmartPtr {
int* ptr; // Actual pointer
public:
// Constructor: Refer https:// www.geeksforgeeks.org/g-fact-93/
// for use of explicit keyword
explicit SmartPtr(int* p = NULL) { ptr = p; }
// Destructor
~SmartPtr() { delete (ptr); }
// Overloading dereferencing operator
int& operator*() { return *ptr; }
};
int main()
{
SmartPtr ptr(new int());
*ptr = 20;
cout << *ptr;
// We don't need to call delete ptr: when the object
// ptr goes out of scope, the destructor for it is automatically
// called and destructor does delete ptr.
return 0;
}
Output:
20
Yes, we can use templates
to write a generic smart pointer class. Following C++ code demonstrates the same.
#include <iostream>
using namespace std;
// A generic smart pointer class
template <class T>
class SmartPtr {
T* ptr; // Actual pointer
public:
// Constructor
explicit SmartPtr(T* p = NULL) { ptr = p; }
// Destructor
~SmartPtr() { delete (ptr); }
// Overloading dereferncing operator
T& operator*() { return *ptr; }
// Overloading arrow operator so that
// members of T can be accessed
// like a pointer (useful if T represents
// a class or struct or union type)
T* operator->() { return ptr; }
};
int main()
{
SmartPtr<int> ptr(new int());
*ptr = 20;
cout << *ptr;
return 0;
}
Output:
20
Note: Smart pointers are also useful in the management of resources, such as file handles or network sockets.
If you are using a unique pointer then if one object is created and pointer P1 is pointing to this one them only one pointer can point this one at one time. So we can’t share with another pointer, but we can transfer the control to P2 by removing P1.
#include <iostream>
using namespace std;
#include <memory>
class Rectangle {
int length;
int breadth;
public:
Rectangle(int l, int b)
{
length = l;
breadth = b;
}
int area()
{
return length * breadth;
}
};
int main()
{
unique_ptr<Rectangle> P1(new Rectangle(10, 5));
cout << P1->area() << endl; // This'll print 50
// unique_ptr<Rectangle> P2(P1);
unique_ptr<Rectangle> P2;
P2 = move(P1);
// This'll print 50
cout << P2->area() << endl;
// cout<<P1->area()<<endl;
return 0;
}
Output:
50
50
If you are using shared_ptr
then more than one pointer can point to this one object at a time and it’ll maintain a Reference Counter
using use_count()
method.
#include <iostream>
using namespace std;
#include <memory>
class Rectangle {
int length;
int breadth;
public:
Rectangle(int l, int b)
{
length = l;
breadth = b;
}
int area()
{
return length * breadth;
}
};
int main()
{
shared_ptr<Rectangle> P1(new Rectangle(10, 5));
// This'll print 50
cout << P1->area() << endl;
shared_ptr<Rectangle> P2;
P2 = P1;
// This'll print 50
cout << P2->area() << endl;
// This'll now not give an error,
cout << P1->area() << endl;
// This'll also print 50 now
// This'll print 2 as Reference Counter is 2
cout << P1.use_count() << endl;
return 0;
}
Output:
50
50
50
2
It’s much more similar to shared_ptr
except it’ll not maintain a Reference Counter
. In this case, a pointer will not have a strong hold on the object. The reason is if suppose pointers are holding the object and requesting for other objects then they may form a Deadlock
.
C++ libraries provide implementations of smart pointers in the form of auto_ptr
, unique_ptr
, shared_ptr
and weak_ptr
.
End of the file
Nov. 28, 2020, by CCJ at Secaucus, NJ, US.