Some slightly obscure features of C++ are used with PlBlob 
and
ContextType, and can easily cause subtle bugs or memory 
leaks if not used carefully.
When a C++ object is created, its memory is allocated (either on the stack or on the heap using new), and the constructors are called in this order:
There are special forms of the constructor for copying, moving, and 
assigning. The “copy constructor” has a signature Type(const 
Type& and is used when an object is created by copying, for 
example by assignment or passing the object on the stack in a function 
call. The “move constructor” has the signature Type(Type&& 
and is equivalent to the copy constructor for the new object followed by 
the destructor for the old object. (Assignment is usually allowed to 
default but can also be specified).
Currently, the copy and move constructors are not used, so it is best to explicitly mark them as not existing:
Type(const Type&) = delete; Type(Type&&) = delete; Type& operator =(const Type&) = delete; Type& operator =(Type&&) = delete;
A constructor may throw an exception - good programming style is to not leave a “half constructed” object but to throw an exception. Destructors are not allowed to throw exceptions,17because the destructor might be invoked by another exception, and C++ has no mechanism for dealing with a second exception. which complicates the API somewhat.
More details about constructors and destructors can be found in the FAQs for constructors and destructors.
Many classes or types have a constructor that simply assigns a 
default value (e.g., 0 for int) and the destructor does 
nothing. In particular, the destructor for a pointer does nothing, which 
can lead to memory leaks. To avoid memory leaks, the smart pointer
std::unique_ptr18The 
name “unique” is to distinguish this from a “shared” pointer. 
A shared pointer can share ownership with multiple pointers and the 
pointed-to object is deleted only when all pointers to the object have 
been deleted. A unique pointer allows only a single pointer, so the 
pointed-to object is deleted when the unique pointer is deleted. 
can be used, whose destructor deletes its managed object. Note that std::unique_ptr 
does not enforce single ownership; it merely makes single ownership easy 
to manage and it detects most common mistakes, for example by not having 
copy constructor or assignment operator.
For example, in the following, the implicit destructor for p 
does nothing, so there will be a memory leak when a Ex1 
object is deleted:
class Ex1 {
public:
  Ex1() : p(new int) { }
  int *p;
};
To avoid a memory leak, the code could be changed to this:
class Ex1 {
public:
  Ex1() p(new int) { }
  ~Ex1() { delete p; }
  int *p;
};
but it is easier to do the following, where the destructor for
std::unique_ptr will free the memory:
class Ex1 {
public:
  Ex1() p(new int) { }
  std::unique_ptr<int> p;
};
The same concept applies to objects that are created in code - if a 
C++ object is created using new, the programmer must 
manage when its destructor is called. In the following, if the call to
data->validate() fails, there will be a memory 
leak:
MyData *foo(int some_value) {
  MyData *data = new MyData(...);
  data->some_field = some_value;
  if (! data->validate() )
    throw std::runtime_error("Failed to validate data");
  return data;
}
Ths could fixed by adding delete data before throwing 
the runtime_error; but this doesn't handle the situation of data->validate() 
throwing an exception (which would require a catch/throw). Instead, it's 
easiser to use std::unique_ptr, which takes care of every 
return or exception path:
MyData *foo(int some_value) {
  std::unique_ptr<MyData> data(new MyData(...));
  data->some_field = some_value;
  if (! data->validate() )
    throw std::runtime_error("Failed to validate data");
  return data.release(); // don't delete the new MyData
}
The destructor for std::unique_ptr will delete the data 
when it goes out of scope (in this case, by return or throw) unless the
std::unique_ptr::release() method is called.19The 
call to unique_ptr<MYData>::release 
doesn't call the destructor; it can be called using std::unique_ptr::get_deleter().
In the code above, the throw will cause the
unique_ptr’s destructor to be called, which will free 
the data; but the data will not be freed in the return 
statement because of the unique_ptr::release(). Using this style, 
a pointer to data on the heap can be managed as easily as data on the 
stack. The current C++ API for blobs takes advantage of this - in 
particular, there are two methods for unifying a blob:
unique_ptr allows specifying the delete function. For 
example, the following can be used to manage memory created with PL_malloc():
std::unique_ptr<void, decltype(&PL_free)> ptr(PL_malloc(...), &PL_free);
or, when memory is allocated within a PL_*() function (in this case, using the Plx_*() wrapper for PL_get_nchars()):
size_t len; char *str = nullptr; Plx_get_nchars(t, &len, &str.get(), BUF_MALLOC|CVT_ALL|CVT_WRITEQ|CVT_VARIABLE|REP_UTF8|CVT_EXCEPTION); std::unique_ptr<char, decltype(&PL_free)> _str(str, &PL_free);
The current C++ API assumes that the C++ blob is allocated on the 
heap. If the programmer wishes to use the stack, they can use std::unique_ptr 
to automatically delete the object if an error is thrown -
PlTerm::unify_blob(std::unique_ptr<PlBlob>*) 
prevents the automatic deletion if unification succeeds.
A unique_ptr needs a bit of care when it is passed as an 
argument. The unique_ptr::get() method can be used to get the “raw” pointer; 
the delete must not be used with this pointer. Or, the unique_ptr::release() 
method can be used to transfer ownership without calling the object's 
destructor.
Using unique_ptr::release() is a bit incovenient, so instead 
the
unique_ptr can be passed as a pointer (or a reference). 
This does not create a new scope, so the pointer must be assigned to a 
local variable. For example, the code for unify_blob() is 
something like:
bool PlTerm::unify_blob(std::unique_ptr<PlBlob>* b) const
{ std::unique_ptr<PlBlob> blob(std::move(*b));
  if ( !unify_blob(blob.get()) )
    return false;
  (void)blob.release();
  return true;
}
The line declaration for blob uses the “move 
constructor” to set the value of a newly scoped variable (std::move(*b) 
is a cast, so unique_ptr’s move constructor is used). 
This has the same effect as calling b->reset(), 
so from this point on,
b has the value nullptr.
Alternatively, the local unique_ptr could be set by
std::unique_ptr<PlBlob> blob(b->release());
or
std::unique_ptr<PlBlob> blob; blob.swap(*b);
If the call to PlTerm::unify_blob() 
fails or throws an exception, the virtual destructor for blob 
is called. Otherwise, the call to blob.release() 
prevents the destructor from being called - Prolog now owns the blob 
object and can call its destructor when the garbage collector reclaims 
it.