Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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).

One example of correct use is return *this; in functions like operator++() and operator<<().

When you are using a smart pointer class, you are right that is dangerous to directly expose "this". There are some pointer classes related to boost::shared_ptr<T> that may be of use:

  • boost::enable_shared_from_this<T>
    • Provides the ability to have an object return a shared pointer to itself that uses the same reference counting data as an existing shared pointer to the object
  • boost::weak_ptr<T>
    • Works hand-in-hand with shared pointers, but do not hold a reference to the object. If all the shared pointers go away and the object is released, a weak pointer will be able to tell that the object no longer exists and will return you NULL instead of a pointer to invalid memory. You can use weak pointers to get shared pointers to a valid reference-counted object.

Neither of these is foolproof, of course, but they'll at least make your code more stable and secure while providing appropriate access and reference counting for your objects.

If you need to use this, just use it explicitly. Smart pointers wrap only pointers of the objects they own - either exclusivelly (unique_ptr) or in a shared manner (shared_ptr).

I personally like to use the this pointer when accessing member variables of the class. For example:

void foo::bar ()
{
    this->some_var += 7;
}

It's just a harmless question of style. Some people like it, somepeople don't.

But using the this pointer for any other thing is likely to cause problems. If you really need to do fancy things with it, you should really reconsider your design. I once saw some code that, in the constructor of a class, it assigned the this pointer to another pointer stored somewhere else! That's just crazy, and I can't ever think of a reason to do that. The whole code was a huge mess, by the way.

Can you tell us what exactly do you want to do with the pointer?

Another option is using intrusive smart pointers, and taking care of reference counting within the object itself, not the pointers. This requires a bit more work, but is actually more efficient and easy to control.

Another reason to pass around this is if you want to keep a central registry of all of the objects. In the constructor, an object calls a static method of the registry with this. Its useful for various publish/subscribe mechanisms, or when you don't want the registry to need knowledge of what objects/classes are in the system.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top