Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

smart pointers + "this" considered harmful?

In a C++ project that uses smart pointers, such as boost::shared_ptr, what is a good design philosophy regarding use of "this"?

Consider that:

  • It's dangerous to store the raw pointer contained in any smart pointer for later use. You've given up control of object deletion and trust the smart pointer to do it at the right time.

  • Non-static class members intrinsically use a this pointer. It's a raw pointer and that can't be changed.

If I ever store this in another variable or pass it to another function which could potentially store it for later or bind it in a callback, I'm creating bugs that are introduced when anyone decides to make a shared pointer to my class.

Given that, when is it ever appropriate for me to explicitly use a this pointer? Are there design paradigms that can prevent bugs related to this?

like image 712
Drew Dormann Avatar asked Dec 19 '08 20:12

Drew Dormann


People also ask

Are smart pointers safe?

However, they provide full thread safety for distinct pointers that reference the same object. So one thread can modify a shared_ptr while another thread is accessing a shared_ptr to that same underlying object whose lifetime is managed by the smart pointers. @LWimsey yes, that is the case.

Why should I consider using smart pointers?

Smart pointers try to prevent memory leaks by making the resource deallocation automatic: when the pointer to an object (or the last in a series of pointers) is destroyed, for example because it goes out of scope, the pointed object is destroyed too.

Should I use smart pointers everywhere?

You (or your library) should use a smart pointer to keep track of the entire allocated string, but you might use a raw pointer to step through each character to parse or modify. You wouldn't use a smart pointer because the object it is pointing at is not owned by the pointer.


2 Answers

Wrong question

In a C++ project that uses smart pointers

The issue has nothing to do with smart pointers actually. It is only about ownership.

Smart pointers are just tools

They change nothing WRT the concept of ownership, esp. the need to have well-defined ownership in your program, the fact that ownership can be voluntarily transferred, but cannot be taken by a client.

You must understand that smart pointers (also locks and other RAII objects) represent a value and a relationship WRT this value at the same time. A shared_ptr is a reference to an object and establishes a relationship: the object must not be destroyed before this shared_ptr, and when this shared_ptr is destroyed, if it is the last one aliasing this object, the object must be destroyed immediately. (unique_ptr can be viewed as a special case of shared_ptr where there is zero aliasing by definition, so the unique_ptr is always the last one aliasing an object.)

Why you should use smart pointers

It is recommended to use smart pointers because they express a lot with only variables and functions declarations.

Smart pointers can only express a well-defined design, they don't take away the need to define ownership. In contrast, garbage collection takes away the need to define who is responsible for memory deallocation. (But do not take away the need to define who is responsible for other resources clean-up.)

Even in non-purely functional garbage collected languages, you need to make ownership clear: you don't want to overwrite the value of an object if other components still need the old value. This is notably true in Java, where the concept of ownership of mutable data structure is extremely important in threaded programs.

What about raw pointers?

The use of a raw pointer does not mean there is no ownership. It's just not described by a variable declaration. It can be described in comments, in your design documents, etc.

That's why many C++ programmers consider that using raw pointers instead of the adequate smart pointer is inferior: because it's less expressive (I have avoided the terms "good" and "bad" on purpose). I believe the Linux kernel would be more readable with a few C++ objects to express relationships.

You can implement a specific design with or without smart pointers. The implementation that uses smart pointer appropriately will be considered superior by many C++ programmers.

Your real question

In a C++ project, what is a good design philosophy regarding use of "this"?

That's awfully vague.

It's dangerous to store the raw pointer for later use.

Why do you need to a pointer for later use?

You've given up control of object deletion and trust the responsible component to do it at the right time.

Indeed, some component is responsible for the lifetime of the variable. You cannot take the responsibility: it has to be transferred.

If I ever store this in another variable or pass it to another function which could potentially store it for later or bind it in a callback, I'm creating bugs that are introduced when anyone decides to use my class.

Obviously, since the caller is not informed that the function will hide a pointer and use it later without the control of the caller, you are creating bugs.

The solution is obviously to either:

  • transfer responsibility to handle the lifetime of the object to the function
  • ensure that the pointer is only saved and used under the control of the caller

Only in the first case, you might end up with a smart pointer in the class implementation.

The source of your problem

I think that your problem is that you are trying hard to complicate matters using smart pointers. Smart pointers are tools to make things easier, not harder. If smart pointers complicate your specification, then rethink your spec in term of simpler things.

Don't try to introduce smart pointers as a solution before you have a problem.

Only introduce smart pointers to solve a specific well-defined problem. Because you don't describe a specific well-defined problem, it is not possible to discuss a specific solution (involving smart pointers or not).

like image 103
curiousguy Avatar answered Oct 10 '22 06:10

curiousguy


While i don't have a general answer or some idiom, there is boost::enable_shared_from_this . It allows you to get a shared_ptr managing an object that is already managed by shared_ptr. Since in a member function you have no reference to those managing shared_ptr's, enable_shared_ptr does allow you to get a shared_ptr instance and pass that when you need to pass the this pointer.

But this won't solve the issue of passing this from within the constructor, since at that time, no shared_ptr is managing your object yet.

like image 27
Johannes Schaub - litb Avatar answered Oct 10 '22 04:10

Johannes Schaub - litb