Why is a raw pointer to shared_ptr allowed in all cases?

I read the 10 most stupid mistakes to avoid with the C ++ 11 smart pointer . Number 5 reads:

Mistake # 5: Do not assign an object (raw pointer) to shared_ptr as soon as it is created!

int main() { Aircraft* myAircraft = new Aircraft("F-16"); shared_ptr<aircraft> pAircraft(myAircraft); ... shared_ptr<aircraft> p2(myAircraft); // will do a double delete and possibly crash } 

and the recommendation looks something like this:

Use make_shared or new and immediately create a pointer with it.

Well, no doubt, problem and recommendation. However, I have a question about the design of shared_ptr . This is a very simple mistake, and the entire “safe” shared_ptr design can be thrown out with very easy misses.

Now the question is, what can be easily fixed with the alternative design of shared_ptr , in which the only constructor from the raw pointer will be that of the r-value reference?

 template<class T> struct shared_ptr{ shared_ptr(T*&& t){...basically current implementation...} shared_ptr(T* t) = delete; // this is to... shared_ptr(T* const& t) = delete; // ... illustrate the point. shared_ptr(T*& t) = delete; ... }; 

Thus, shared_ptr can only be initialized from the result of new or from some factory function.

Is this a misuse of the C ++ language in the library? or What is the point of having a constructor reference from the original pointer (l-value) if this is most likely to be misused?

Is this a historical disaster? (e.g. shared_ptr was proposed before r-value links, etc., were introduced) Backward compatibility?

(Of course, we can say std::shared_ptr<type>(std::move(ptr)); that it is easier to catch, and also work if it is really necessary.)

Did I miss something?

+5
source share
2 answers

Pointers are very easy to copy. Even if you limit the reference to the r-value, you can easily make copies (for example, when you pass a pointer as a parameter to a function), which will invalidate the security setting. In addition, you will encounter problems in templates where you can easily have T* const or T*& as a type, and you will get type mismatches.

Thus, you propose creating additional restrictions without significantly improving security, which is likely why it was not in the standard for a start.

The point of make_shared is to make_shared construction of a shared pointer. Say you have f(shared_ptr<int>(new int(5)), throw_some_exception()) . The standard does not guarantee the encapsulation of parameters. The compiler is allowed to create a new int, throw_some_exception , and then build shared_ptr, which means you could leak int (if throw_some_exception really throws an exception). make_shared simply creates an object and a shared pointer within itself, which prevents the compiler from changing the order, so it becomes safe.

+4
source

There are several cases where you cannot call make_shared() . For example, your code may not be responsible for the distribution and construction of the class in question. The following paradigm (private constructors + factory functions) is used in some C ++ base codes for various reasons:

 struct A { private: A(); }; A* A_factory(); 

In this case, if you want to insert A* , you get from A_factory() in shared_ptr<> , you will have to use a constructor that instead of the raw pointer instead of make_shared() .

Above my head, some other examples:

  • You want to get aligned memory for your type using posix_memalign() and then save it to shared_ptr<> with a custom delete that calls free() (this use case will disappear soon when we add aligned allocation to the language!).
  • You want to put a pointer to the memory mapped area created with mmap() in shared_ptr<> with a user-defined delete that calls munmap() (this use case will disappear when we get the standardized tool for shmem, which I hope to work in the next few months).
  • You want to bind the pointer selected in shared_ptr<> with user deletion.
+1
source

All Articles