Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Qt::make_shared for creating QSharedPtr as std::make_shared for creating std::shared_ptr

As stated in Bjarne Stroustrup's "A tour of C++", and as a known C++14 practice, one should avoid naked new and delete in the code. Standard library offers std::make_shared and std::make_unique for creating smart pointers to immediately store allocated objects in.

However, it is not possible to use these routines for non-standard smart pointers, like in Qt. Qt has its own memory management model (with parents), but also provides smart pointer classes like QSharedPointer and QPointer (though the latter is not actually an owning pointer).

My question is: isn't it convenient to create Qt analogs of std::make_shared? Like this, for creating QSharedPtr:

namespace Qt
{
  template<class T, class... Args>
  QSharedPointer<T> make_shared(Args&&... args)
  {
    return QSharedPointer<T>(new T(std::forward<Args>(args)...));
  }
}

Or like this, for creating QPointer:

namespace Qt
{
  template<class T, class... Args>
  QPointer<T> make_ptr(Args&&... args)
  {
     return QPointer<T>(new T(std::forward<Args>(args)...));
  }
}

And it can be used like:

auto pCancelButton = Qt::make_ptr<QPushButton>("Cancel", this);

Are there any caveats for this approach? Are there any publicly known usage of this approach?

UPDATE I claim that Qt::make_ptr is useful as long as QPointer is useful, because it will hide new operator and make sure new is called only for something that inherits QObject. Qt users do a lot of new's, but in this way we can be sure new is used only in Qt context. Any arguments on this thought?

like image 480
Mikhail Avatar asked Oct 10 '14 08:10

Mikhail


People also ask

What is std :: Make_shared?

std::make_shared performs a single heap-allocation accounting for the space necessary for both the control block and the data. In the other case, new Obj("foo") invokes a heap-allocation for the managed data and the std::shared_ptr constructor performs another one for the control block.

What does Make_shared return in C++?

It constructs an object of type T passing args to its constructor, and returns an object of type shared_ptr that owns and stores a pointer to it.

What is the purpose of the shared_ptr <> template?

std::shared_ptr is a smart pointer that retains shared ownership of an object through a pointer.

What happens if Make_shared fails?

So, if you throw exception from your class' constructor, then std::make_shared will throw it too. Besides exceptions thrown from constructor, std::make_shared could throw std::bad_alloc exception on its own. Therefore, you don't need to check if the result of std::make_shared is nullptr .


1 Answers

Features such as make_shared strictly rely on the perfect forwarding feature, which is only available since C++11 and the introduction of universal (forwarding) references. Having said that, without a perfect forwarding, using this function may be inefficient. Qt is quite older than the recent C++ standard, hence it was not available till Qt 5.1 (just like previously you had to use Qt's internal pre-processing macros like SIGNAL and SLOT for making connections).

Qt 5.1 already provides its own implementation for making smart pointers for QSharedPointer.

That static create member function can be used as follows:

auto ptr = QSharedPointer<QPushButton>::create("Cancel", this);

But note the description:

Note: This function is only available with a C++11 compiler that supports perfect forwarding of an arbitrary number of arguments. If the compiler does not support the necessary C++11 features, you must use the overload that calls the default constructor.

There are two major advantages of using make_shared and create functions rather than directly calling constructor with allocating memory with new:

  1. That special perfectly forwarding function can allocate in a single system call a memory for both stored object and reference counter.

  2. The memory allocation is separated from the calling context, so you avoid memory leaks in case of an exception is thrown while constructing another object in e.g. a function call (where a compiler is free to choose the order in which arguments are evaluated). Consider:

    foo(QSharedPointer<QPushButton>(new QPushButton("Cancel", this)), MayThrow());
    

That is, if the compiler first executes the new QPushButton("Cancel", this) expression, and then calls the MayThrow() function before calling the constructor of QSharedPointer, you may leak memory if the MayThrow() function throws an exception.

like image 194
Piotr Skotnicki Avatar answered Sep 22 '22 01:09

Piotr Skotnicki