Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I use shared_ptr<Object> myObject = (shared_ptr<Object>) new Object() to access private constructors?

I am working with a code base which makes extensive use of the following syntax:

shared_ptr<Object> myObject = (shared_ptr<Object>) new Object();

I noticed that I cannot access private constructors with make_shared, but shared_ptr<Object> myObject = (shared_ptr<Object>) new Object(); works just fine. Should I be using it just because it seems to work? Are there any dangers? How is it different than make_shared?

I am aware of the answer in this question, which makes comparisons between make_shared and:

std::shared_ptr<Object> p2(new Object("foo"));

but I haven't been able to find a reference to the syntax I came across. Is it different, or is it the same as the above?

like image 804
Nikos Kazazakis Avatar asked Jun 13 '17 16:06

Nikos Kazazakis


People also ask

When should you use shared_ptr?

So, we should use shared_ptr when we want to assign one raw pointer to multiple owners. // referring to the same managed object. When to use shared_ptr? Use shared_ptr if you want to share ownership of a resource.

Can shared_ptr be copied?

The ownership of an object can only be shared with another shared_ptr by copy constructing or copy assigning its value to another shared_ptr . Constructing a new shared_ptr using the raw underlying pointer owned by another shared_ptr leads to undefined behavior.

What is a shared_ptr in C++?

The shared_ptr type is a smart pointer in the C++ standard library that is designed for scenarios in which more than one owner might have to manage the lifetime of the object in memory.

What is boost :: shared_ptr?

shared_ptr is now part of the C++11 Standard, as std::shared_ptr . Starting with Boost release 1.53, shared_ptr can be used to hold a pointer to a dynamically allocated array. This is accomplished by using an array type ( T[] or T[N] ) as the template parameter.


Video Answer


1 Answers

make_shared allocates the object in the same block of memory as the control block. This improves cache coherency and cuts out an allocation.

One way to permit make_shared to do this is to have a private token within your class, and a public constructor that consumes that token.

class Object {
private:
  struct token_t{ private: token_t() {}; friend class Object; };
  static token_t token() { return {}; }
  Object() = default;
public:
  Object( token_t ):Object() {}
};

now we can make_shared<Object>( Object::token() ).

This gives us one allocation, and doesn't violate the privacy of the construction as only things with access to private fields of Object can call that constructor. They can, however, pass the token to another function (like make_shared) and it in turn can call the constructor in question. This works with more arguments, naturally.

As for your syntax:

std::shared_ptr<Object> myObject = (std::shared_ptr<Object>) new Object();

(std::shared_ptr<Object>) new Object(); simply explicitly constructs a shared_ptr<Object> from new Object(). It is equivalent to std::shared_ptr<Object>(new Object).

We then take this prvalue and construct myObject from it. In C++03 11 and 14, this copy/move construction is elided. In C++17, the prvalue "construction instructions" are directly applied to myObject. In practice, this results in the same machine code (unless you are a fool and explicitly tell your compiler to not elide construction).

In short, it works. The only downside is the double-allocation of the separate control-block from the Object allocation.

like image 64
Yakk - Adam Nevraumont Avatar answered Oct 22 '22 00:10

Yakk - Adam Nevraumont