Essentially, the original question is asking: "how do I know when a local variable goes out of scope?"
The answer is: when its destructor is called.
For the code in your question, you could have ~D()
to remove itself from c1
.
This is awkward, and the discussion beneath the question brings up std::shared_ptr
,
but the code in the example uses a local variable. You can't use std::shared_ptr
on a local variable, only on a heap variable. This is not made clear in the discussion.
Here are two references to questions specifically related to shared_ptr and local variables:
Create shared_ptr to stack object
and
Set shared_ptr to point existing object
A better solution to your problem is to not use a local variable, but a heap variable.
The difference between local and heap variables seems to be causing some confusion.
C++ has several storage classes for variables:
- stack - used for local variables and function parameters
- register - sometimes used for local variables - primarily an optimization
- global/static - "one and only one", in space allocated at program load time.
- thread local - global/static but thread specific - each thread has its own copy.
- heap - allocated by the program, on the fly, via new or malloc (or equivalent). Sometimes called "free store".
C++ has some tricks with new
and delete
where you can use a custom heap,
which is memory you're managing yourself, but that memory will have been allocated
from the general heap, or possibly exist inside other variables.
Here are three different examples:
A local variable:
void some_function(C& c1)
{
D d1;
c1.add(&d1);
}
When this function exits, the stack frame that contains d1 goes away,
and the variable ceases to exist. ~D()
will get called,
and you could remove d1 from c1 if you added
a mechanism to do this, like making d1
hold a pointer to c1
, etc.,
which is awkward, but possible.
std::shared_ptr
will not help with a local variable.
A heap variable allocated with new:
void some_function(C& c1)
{
D *d1p = new D();
c1.add(d1p);
}
When this function exits, the D
variable that d1p
points to still exists.
It will continue exist until it is explicitly deleted. In this context,
the c1
variable "owns" this pointer, and it responsible for deleting it
when it has finished using it.
This is the case where adding a smart pointer can simplify reference management,
as in:
A heap variable and shared_ptr created via make_shared:
void some_function(C& c1)
{
std::shared_ptr<D> d1p = std::make_shared<D>();
c1.add(d1p);
}
This example creates a D
variable on the heap, and creates a local shared_ptr
holding a reference to that D
variable, via a small counter object,
which is also on the heap. When the shared_ptr
is passed to the C::add
method,
the reference count in the counter object is incremented. When the shared_ptr
goes out of scope, the reference count in the counter object is decremented.
The shared_ptr
in the c1
object now holds the only reference to that shared_ptr
,
and when the c1
object is destroyed, the shared_ptr
it contains will be destroyed,
releasing its reference - if the count goes to zero, the object it points to will
also be destroyed.
Besides this automatic destruction, another advantage of this approach is that you can get references to this D
variable
from c1
and use them elsewhere, and these references (and the D
variable itself)
can outlive c1
.
The C
class has to be modified for this case, e.g.
std::vector<std::shared_ptr<D>> _list;
and
void add(std::shared_ptr<D> dObject)
Here's a reference to how shared pointers work:
How do shared pointers work?
and some documentation on make_shared:
https://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared