Smart Pointers

#include <memory>  

std::unique_ptr

class Foo
{
  public:
       string name;

   Foo(string n): name{move(n)}{ 
      cout << "CTOR " << name << '\n'; 
   }

   ~Foo() { cout << "DTOR " << name << '\n'; }
};
void process_item(unique_ptr<Foo> p)
{
   if (!p) { return; }
   cout << "Processing " << p->name << '\n';
}
int main()
{
    {
        unique_ptr<Foo> p1 {new Foo{"foo"}};
        auto            p2 (make_unique<Foo>("bar"));
    }
    process_item(make_unique<Foo>("foo1"));
    auto p1 (make_unique<Foo>("foo2"));
    auto p2 (make_unique<Foo>("foo3"));
    process_item(move(p1));//unique_ptr have only move constructor and move assigment
    cout << "End of main()\n";
}

After we left the scope, both objects are destructed immediately and their memory is released to the heap. Let’s have a look at the process_item function and how to use it with unique_ptr now. If we construct a new Foo instance, managed by a unique_ptr in the function call, then its lifetime is reduced to the scope of the function. When process_item returns, the object is destroyed
f we want to call process_item with an object that already existed before the call, then we need to transfer ownership because that function takes a unique_ptr by value, which means that calling it would lead to a copy. But unique_ptr cannot be copied, it can only be moved. Let’s create two new Foo objects and move one into process_item. By looking at the terminal output later, we will see that foo2 is destroyed when process_item returns because we transferred ownership to it. foo3 will continue living until the main function returns

CTOR foo
CTOR bar
DTOR bar
DTOR foo
CTOR foo1
Processing foo1
DTOR foo1
CTOR foo2
CTOR foo3
Processing foo2
DTOR foo2
End of main()
DTOR foo3

std::shared_ptr

class Foo
{
  public:
       string name;

   Foo(string n): name{move(n)}{ 
      cout << "CTOR " << name << '\n'; 
   }

   ~Foo() { cout << "DTOR " << name << '\n'; }
};

void f(shared_ptr<Foo> sp)
{
      cout << "f: use counter at " << sp.use_count() << '\n';
}
int main()
{
   shared_ptr<Foo> fa;
   {
        cout << "Inner scope begin\n";
        shared_ptr<Foo> f1 {new Foo{"foo"}};
        auto            f2 (make_shared<Foo>("bar"));
        cout << "f1's use counter at " << f1.use_count() << '\n';
        fa = f1;
        cout << "f1's use counter at " << f1.use_count() << '\n';
   }
    cout << "Back to outer scope\n";
    cout << fa.use_count() << '\n';
    cout << "first f() call\n";
    f(fa);
    cout << "second f() call\n";
    f(move(fa));
    cout << "end of main()\n";
}
Inner scope begin
CTOR foo
CTOR bar
f1's use counter at 1
f1's use counter at 2
DTOR bar
Back to outer scope
1
first f() call
f: use counter at 2
second f() call
f: use counter at 1
DTOR foo
end of main()

std::weak_ptr

class Foo
{
  public:
       string name;

   Foo(string n): name{move(n)}{ 
      cout << "CTOR " << name << '\n'; 
   }

   ~Foo() { cout << "DTOR " << name << '\n'; }
};

void weak_ptr_info(const weak_ptr<Foo> &p)
{
   cout << "---------" << boolalpha
   << "\nexpired:   " << p.expired()
   << "\nuse_count: " << p.use_count()
   << "\ncontent:   ";
   if (const auto sp (p.lock()); sp) {
      cout << sp->value << '\n';
   } else {
      cout << "<null>\n";
   }
}

The expired function of weak_ptr tells us if the object it points to still really exists, because holding a weak pointer to an object does not prolong its lifetime! The use_count counter tells us how many shared_ptr instances are currently pointing to the object in question:

int main()
{
   weak_ptr<Foo> weak_foo;
   weak_ptr_info(weak_foo);
   {
        auto shared_foo (make_shared<Foo>(1337));
        weak_foo = shared_foo;
        weak_ptr_info(weak_foo);
   }
   weak_ptr_info(weak_foo);
}
---------
expired: true
use_count: 0
content: <null>
---------
expired: false
use_count: 1
content: 1337
DTOR Foo 1337
---------
expired: true
use_count: 0
content: <null>

Placement new operator in C++

Placement new is a variation new operator in C++. Normal new operator does two things : (1) Allocates memory (2) Constructs an object in allocated memory.

Placement new allows us to separate above two things. In placement new, we can pass a preallocated memory and construct an object in the passed memory.

new vs placement new

  • Normal new allocates memory in heap and constructs objects tehre whereas using placement new, object construction can be done at known address.
  • With normal new, it is not known that, at what address or memory location it’s pointing to, whereas the address or memory location that it’s pointing is known while using placement new.
  • The deallocation is done using delete operation when allocation is done by new but there is no placement delete, but if it is needed one can write it with the help of destructor
new (address) (type) initializer

When to prefer using placement new ?

As it allows to construct an object on memory that is already allocated , it is required for optimizations as it is faster not to re-allocate all the time. There may be cases when it is required to re-construct an object multiple times so, placement new operator might be more efficient in these cases.

#include<iostream> 
using namespace std; 
  
int main() 
{ 
    // buffer on stack 
    unsigned char buf[sizeof(int)*2] ; 
  
    // placement new in buf 
    int *pInt = new (buf) int(3); 
  
    int *qInt = new (buf + sizeof (int)) int(5); 
    int *pBuf = (int*)(buf+0) ; 
    int *qBuf = (int*) (buf + sizeof(int)); 
    cout << "Buff Addr             Int Addr" << endl; 
    cout << pBuf <<"             " << pInt << endl; 
    cout << qBuf <<"             " << qInt << endl; 
    cout << "------------------------------" << endl; 
    cout << "1st Int             2nd Int" << endl; 
    cout << *pBuf << "                         "
         << *qBuf << endl; 
  
    return 0; 
}
This image has an empty alt attribute; its file name is 1-7.pngThis image has an empty alt attribute; its file name is 2-3.png
#include<iostream> 
using namespace std; 
int main() 
{ 
    // initial value of X 
    int X = 10; 
  
    cout << "Before placement new :" << endl; 
    cout << "X : " << X << endl; 
    cout << "&X : " << &X << endl; 
  
    // Placement new changes the value of X to 100 
    int *mem = new (&X) int(100); 
  
    cout << "\nAfter placement new :" << endl; 
    cout << "X : " << X << endl; 
    cout << "mem : " << mem << endl; 
    cout << "&X : " << &X << endl; 
  
    return 0; 
} 
Before placement new :
X : 10
&X : 0x69fee8

After placement new :
X : 100
mem : 0x69fee8
&X : 0x69fee8

How to delete the memory allocated by placement new ?

he operator delete can only delete the storage created in heap, so when placement new is used delete operator cannot be used to delete the storage. In the case of memory allocation using placement new operator , since it is created in stack the compiler knows when to delete it and it will handle deallocation of the memory automatically. If required, one can write it with the help of destructor as shown below.

#include<iostream> 
#include<cstdlib> 
#include<cmath> 
using namespace std; 
  
class Complex 
{ 
private: 
    double re_, im_; 
public: 
    // Constructor 
    Complex(double re = 0, double im = 0): re_(re), im_(im) 
    { 
        cout << "Constructor : (" << re_ 
             << ", " << im_ << ")" << endl; 
    } 
  
    // Destructor 
    ~Complex() 
    { 
        cout << "Destructor : (" << re_ << ", "
             << im_ << ")" << endl; 
    } 
  
    double normal() 
    { 
        return sqrt(re_*re_ + im_*im_); 
    } 
  
    void print() 
    { 
        cout << "|" << re_ <<" +j" << im_ 
             << " | = " << normal() << endl; 
    } 
}; 
  
// Driver code 
int main() 
{ 
    // buffer on stack 
    unsigned char buf[100]; 
  
    Complex* pc = new Complex(4.2, 5.3); 
    Complex* pd = new Complex[2]; 
  
    // using placement new 
    Complex *pe = new (buf) Complex(2.6, 3.9); 
  
    // use objects 
    pc -> print(); 
    pd[0].print(); 
    pd[1].print(); 
    pe->print(); 
  
    // Release objects 
    // calls destructor and then release memory 
    delete pc; 
  
    // Calls the destructor for object pd[0] 
    // and then release memory 
    // and it does same for pd[1] 
    delete [] pd; 
  
    // No delete : Explicit call to Destructor. 
    pe->~Complex(); 
  
    return 0; 
} 
Constructor : (4.2, 5.3)
Constructor : (0, 0)
Constructor : (0, 0)
Constructor : (2.6, 3.9)
|4.2 +j5.3 | = 6.7624
|0 +j0 | = 0
|0 +j0 | = 0
|2.6 +j3.9 | = 4.68722
Destructor : (4.2, 5.3)
Destructor : (0, 0)
Destructor : (0, 0)
Destructor : (2.6, 3.9)

When will placement new operator show segmentation fault ?

The placement new operator should be used with care. The address which is passed can be a reference or a pointer  pointing to a valid memory location. It may show error when the address passed is :

  • A pointer such as NULL pointer.
  • A pointer that is not pointing to any location.
  • It cannot be a void pointer unless it points to some location.
#include<iostream> 
using namespace std; 
  
int main() 
{ 
    // Fine 
    int i = 10; 
    int *ipt = &i ; 
    int *i1 = new(ipt) int(9) ; 
  
    // Incorrect as ip may not 
    // be a valid address 
    int *ip; 
    int *i2 = new(ip) int(4) ; 
  
    // Fine 
    void *vd = &i; 
    int *i3 = new(vd) int(34) ; 
  
    // Incorrect as x is not an address 
    int x; 
    int *i5 = new(x) int(3) ; 
  
    return 0; 
} 

Advantages of placement new operator over new operator

  • The address of memory allocation is known before hand.
  • Useful when building a memory pool, a garbage collector or simply when performance and exception safety are paramount.
  • There’s no danger of allocation failure since the memory has already been allocated, and constructing an object on a pre-allocated buffer takes less time.
  • This feature becomes useful while working in an environment with limited resources.

Qt Meta Type

The QMetaType class manages named types in the meta-object system

The class is used as a helper to marshall types in QVariant and in queued signals and slots connections. It associates a type name to a type so that it can be created and destructed dynamically at run-time. Declare new types with Q_DECLARE_METATYPE() to make them available to QVariant and other template-based functions. Call qRegisterMetaType() to make types available to non-template based functions, such as the queued signal and slot connections.

Q_DECLARE_METATYPE(MyStruct)
MyStruct s;
QVariant var;
var.setValue(s); // copy s into the variant
// retrieve the value
MyStruct s2 = var.value<MyStruct>();

what can you do once you register type with meta types?

  • create object with type id
  • value as Qvariant
  • streaming data of object (save ,load ,<<,>>,…)(qRegisterMetaTypeStreamOperators<type>(type_name))
  • pass values betweens queued signals and slots connections

struct Test2
{
    int i;
};
Q_DECLARE_METATYPE(Test2);

QDataStream &operator<<(QDataStream &out, const Test2 &myObj)
{
    out<<myObj.i;
    return out;
}

QDataStream &operator>>(QDataStream &in, Test2 &myObj)
{
    in>>myObj.i;
    return in;
}

qRegisterMetaTypeStreamOperators<Test2>("Test2");

QVariant var1;
Test2 t1{.i=45};
var1.setValue(t1);
QFile f("./hello.txt");
f.open(QIODevice::WriteOnly);
QDataStream ds(&f);
ds<<var1;
f.close();

QVariant var2;
Test2 t2;
var2.setValue(t2);
f.open(QIODevice::ReadOnly);
ds>>var2;
f.close();
 qDebug()<<"value is : "<<var2.value<Test2>().i;//45
qMetaTypeId<type>()//return type id of type
QMetaType::fromType<type>()//return QMetaType 
QMetaType::type("Test2");//return type id by type name

or you can use member functions

QMetaObject* metaObject();// if it subclass of QObject return meta object
void* create(const void *copy = 0)// return pointer for copy of copy
void* construct(void *where, const void *copy = 0)// create copy of copy with determined adress (where)
destroy(void*)//
destruct(int type,void* where)// call destruct function without delete operator

QVariant

//before you can use any type as variant you sould to use Q_DECLARE_METATYPE
qvariant_cast<Type>(variant);//cast variant to type
variant.value<Type>();//cast value to type