Weird initialization of shared_ptr variable

Hi all,
first of all, I hope the "beginners" forum is ok for this question, I'm not 100% sure.
I'm trying to understand this fragment of code, which is part of a class definition:
1
2
3
4
5
std::shared_ptr<TypeA> handle_{nullptr, [](TypeA ptr) {
  if (ptr) {
    AFree(ptr);
  }
}};

TypeA is actually a pointer to a struct defined elsewhere, and
AFree is a function which frees the memory allocated inside the struct.
From what I could understand (running the code), it looks like if I try to re-initialize handle_ when the memory was already initialized, the memory gets freed (and of course, handle_ will get a nullptr value).
What I don't understand is the syntax. Something like {nullptr} would make sense to me, but the lambda function inside of a brace initializer for a variable which is not a pointer-to-variable is weird to me.
In other words, I kind of understood what is going on but I'd like to have maybe some reference to the C++ documentation about this.
std::shared_ptr is a class template that can be used similar to normal pointers. Basically, it will keep track of how many "shared pointers" point to the same object and when there are no longer any pointers pointing to the object it will automatically delete the object.

std::shared_ptr<TypeA> means a "shared pointer to TypeA". This is similar to having a TypeA*.

You say TypeA is a pointer type. This is less common but not imposible. It just means the object that the shared pointer points to is a pointer. Yes, pointers are objects.

Shared pointers can be null just like regular pointers. There are many syntaxes that you can use to create a null shared pointer. Here are some of them:
1
2
3
std::shared_ptr<TypeA> handle_;
std::shared_ptr<TypeA> handle_ = nullptr;
std::shared_ptr<TypeA> handle_ { nullptr };
handle_ will be null in all three of these cases.

By default the object will get deleted using delete. For this to work the object (i.e. pointer in this case) need to have been created with new (which is what happens if you construct objects using std::make_shared).

But sometimes you might want to use shared pointers for objects that have not been created with new. So what you can do is pass a custom deleter as a second constructor argument when constructing the shared pointer. A custom deleter is an object that has a function-call operator that accepts a pointer to the object. You can easily create such an object using a lambda.

The code you posted seems to contain a mistake. It should take a pointer to the TypeA object as argument. Something like this:
1
2
3
4
5
std::shared_ptr<TypeA> handle_{nullptr, [](TypeA* ptr) {
	if (ptr) {
		AFree(ptr); // ???
	}
}};

I think the reason it compiled before was because you passed nullptr as the first argument which can be converted into any pointer type (you said TypeA is a pointer type), but I'm not sure if it is guaranteed to work, and it would not work if you passed a TypeA* instead of nullptr as first argument.

Anyhow, using a custom deleter for a null shared pointer seems a bit pointless. The default deleter would work just fine.

I wrote ??? in the deleter above because it's not clear if AFree actually wants to take a TypeA* as argument. In your code it seemed to take a TypeA as argument. If AFree just frees the object that is pointed to by the AFree object then you would still need to free the AFree* pointer in the deleter, possibly using delete but that depends on how it was created.
1
2
3
4
5
6
std::shared_ptr<TypeA> handle_{nullptr, [](TypeA* ptr) {
	if (ptr) {
		AFree(*ptr);
		delete ptr;
	}
}};


All this seems a bit complicated. You're essentially having a pointer to a pointer. I'm wondering if the intention is not to simply have a shared pointer to whatever TypeA is a pointer to. For example, if TypeA is an alias for T* then I suspect what the author really wanted was std::shared_ptr<T>.

1
2
3
4
5
6
7
8
void AFree(T*);
T* ACreate();

std::shared_ptr<T> handle_{ACreate(), [](T* ptr) {
	if (ptr) {
		AFree(ptr);
	}
}};

Last edited on
@Peter87 thank you very much for the thorough explanation! Things definitely make sense now (I honestly couldn't remember custom deleters were a thing).
Last edited on
Topic archived. No new replies allowed.