C# parameters by reference and .net garbage collection - c#

I have been trying to figure out the intricacies of the .NET garbage collection system and I have a question related to C# reference parameters. If I understand correctly, variables defined in a method are stored on the stack and are not affected by garbage collection. So, in this example:
public class Test
{
public Test()
{
}
public int DoIt()
{
int t = 7;
Increment(ref t);
return t;
}
private int Increment(ref int p)
{
p++;
}
}
the return value of DoIt() will be 8. Since the location of t is on the stack, then that memory cannot be garbage collected or compacted and the reference variable in Increment() will always point to the proper contents of t.
However, suppose we have:
public class Test
{
private int t = 7;
public Test()
{
}
public int DoIt()
{
Increment(ref t);
return t;
}
private int Increment(ref int p)
{
p++;
}
}
Now, t is stored on the heap as it is a value of a specific instance of my class. Isn't this possibly a problem if I pass this value as a reference parameter? If I pass t as a reference parameter, p will point to the current location of t. However, if the garbage collector moves this object during a compact, won't that mess up the reference to t in Increment()? Or does the garbage collector update even references created by passing reference parameters? Do I have to worry about this at all? The only mention of worrying about memory being compacted on MSDN (that I can find) is in relation to passing managed references to unmanaged code. Hopefully that's because I don't have to worry about any managed references in managed code. :)

If I understand correctly, variables defined in a method are stored on the stack and are not affected by garbage collection.
It depends on what you mean by "affected". The variables on the stack are the roots of the garbage collector, so they surely affect garbage collection.
Since the location of t is on the stack, then that memory cannot be garbage collected or compacted and the reference variable in Increment() will always point to the proper contents of t.
"Cannot" is a strange word to use here. The point of using the stack in the first place is because the stack is only used for data which never needs to be compacted and whose lifetime is always known so it never needs to be garbage collected. That why we use the stack in the first place. You seem to be putting the cart before the horse here. Let me repeat that to make sure it is clear: the reason we store this stuff on the stack is because it does not need to be collected or compacted because its lifetime is known. If its lifetime were not known then it would go on the heap. For example, local variables in iterator blocks go on the heap for that reason.
Now, t is stored on the heap as it is a value of a specific instance of my class.
Correct.
Isn't this possibly a problem if I pass this value as a reference parameter?
Nope. That's fine.
If I pass t as a reference parameter, p will point to the current location of t.
Yep. Though the way I prefer to think of it is that p is an alias for the variable t.
However, if the garbage collector moves this object during a compact, won't that mess up the reference to t in Increment()?
Nope. The garbage collector knows about managed references; that's why they're called managed references. If the gc moves the thing around, the managed reference is still valid.
If you had passed an actual pointer to t using unsafe code then you would be required to pin the container of t in place so that the garbage collector would know to not move it. You can do that using the fixed statement in C#, or by creating a GCHandle to the object you want to pin.
does the garbage collector update even references created by passing reference parameters?
Yep. It would be rather fragile if it didn't.
Do I have to worry about this at all?
Nope. You're thinking about this like an unmanaged C++ programmer -- C++ makes you do this work, but C# does not. Remember, the whole point of the managed memory model is to free you from having to think about this stuff.
Of course, if you enjoy worrying about this stuff you can always use the "unsafe" feature to turn these safety systems off, and then you can write heap and stack corrupting bugs to your heart's content.

No, you don't need to worry about it. Basically the calling method (DoIt) has a "live" reference to the instance of Test, which will prevent it from being garbage collected. I'm not sure whether it can be compacted - but I suspect it can, with the GC able to spot which variable references are part of objects being moved.
In other words - don't worry. Whether it can be compacted or not, it shouldn't cause you a problem.

It is exactly how you mention it in the last sentence. The GC will move all needed references when it compacts the heap (except for references to unmanaged memory).
Note that using the stack or heap is related to an instance variable being of a value or reference type. Value types (structs and 'simple' types like int, double, etc) are always on the stack, classes are always in the heap (what is in the stack is the reference - the pointer - to the allocated memory for the instance).
Edit: as correctly noted below in the comment, the second paragraph was written much too quickly. If a value type instance is a member of a class, it will not be stored in the stack, it will be in the heap like the rest of the members.

Related

How to delete an instance of a class within that instance

class Foo
{
int Data;
public Foo(int data)
{
Data = data;
}
public void Diminish()
{
this.Data--;
}
}
Foo Foo1 = new Foo(5);
Foo1.Diminish();
Is there Any way delete Foo1 When Data is 0 within Foo1?
(Free up the memory used by that instance)
An object never decides when to "delete" or garbage collect itself. It can't know what else has a reference to it. When there are no references left to an object then it gets garbage collected.
In the scenario described in the question - suppose you had a List<Foo>() and then one instance of Foo spontaneously decided, based on its own internal logic, that it should no longer exist? I don't know what that would even look like - all references to it suddenly become null?
If that were possible, the defects it would cause would be beyond all mortal comprehension. We have enough trouble when two threads modify the same object or property because of improper/nonexistent locking, and so from the viewpoint of one thread something has changed when it shouldn't.
But this would create similar scenarios even in single-threaded applications.
var myFoo = new Foo(1);
myFoo.Diminish();
var x = myFoo.Data; // Null reference exception! WTH - it nullified itself!
A fundamental concept of OOP is that classes don't need to know too much about the internal state of other classes. But now you'd need to "know" that a class could suddenly choose to opt-out and jump off the cliff into the GC, and constantly null-check objects that already weren't null.
It's a very good thing that this is not possible.
As some comments and at least one answer explain, a normal reference in a .NET program is exactly what keeps that object alive. It is not possible to "delete" the object explicitly in the way you seem to mean.
An object will remain alive as long as it is "reachable" (i.e. it is possible to start with a "rooted" reference and find the reference to that object). Once it is no longer reachable (which could happen even when a reference to the object exists, as long as that reference can't be reached from a rooted reference…e.g. a circular reference between two objects does not prevent garbage collection of those two objects, as long as the only reference to each is from the other).
All of this is discussed in detail in documentation references provided elsewhere.
Now, all that said, depending on what you're actually trying to do, you might find that the WeakReference<T> class is what you need. This class provides a way to reference objects without preventing them from being garbage-collected. You can combine this with reference-counting and an object repository to maintain non-weak references to keep the object alive to produce the results you want.
For example:
class RefCounted
{
private static readonly HashSet<RefCounted> _alive = new HashSet<RefCounted>();
private int _referenceCount;
public RefCounted()
{
AddRef();
}
public AddRef()
{
if (_referenceCount == 0)
{
// the collection ensures a strong reference to the object, to prevent
// the object from being garbage-collected even if the only other
// references are weak references.
_alive.Add(this);
}
_referenceCount++;
}
public Release()
{
if (--_referenceCount)
{
// no longer need to force the object to stay alive; if the only remaining
// references to the object are weak references, then the garbage
// collector may collect the object.
_alive.Remove(this);
}
}
}
Then you can use it like this:
WeakReference<RefCounted> o = new WeakReference<RefCounted>(new RefCounted());
RefCounted r;
if (o.TryGetTarget(out r))
{
// do stuff with r
// done with the object? Then call:
r.Release();
}
It is important to note that this is not precisely what you're asking for. The above still does not give you deterministic control over the actual garbage collection of the object. Furthermore, once you retrieve the target of the weak reference into e.g. a local variable, you now hold a strong reference to the object for the lifetime of that local variable (i.e. up to the last point that variable is used) and the object cannot be collected during that lifetime, even if the reference count goes to 0 (though at that point the object will of course be collected once that variable no longer exists or is reachable).
But if you really have a scenario where you need to use some type of reference counting to track and in some way control the lifetime of an object, the above is how you'd do it. You would have to store the WeakReference<T> objects rather than direct references to T, retrieving the target reference at each place in the code where you need it.
Of course, there remains the question of why do you think you need reference counting. And even if you need reference counting, why should the reference count be directly related to the lifetime of the object? Your question does not explain these aspects, and so I can't really address them directly. I will say that reference counting is not needed at all for effective management of managed objects in .NET programs. As long as you don't actively interfere with the maintenance and use of references to managed objects, the .NET garbage collection system will do a far better job of dealing with the lifetimes of objects than you or I could.
An object instance will remain "alive" as long as any piece of code references it. So, it's something you cannot really control from within a class.
From MSDN:
The .NET Framework's garbage collector manages the allocation and release of memory for your application. Each time you create a new object, the common language runtime allocates memory for the object from the managed heap. As long as address space is available in the managed heap, the runtime continues to allocate space for new objects. However, memory is not infinite. Eventually the garbage collector must perform a collection in order to free some memory. The garbage collector's optimizing engine determines the best time to perform a collection, based upon the allocations being made. When the garbage collector performs a collection, it checks for objects in the managed heap that are no longer being used by the application and performs the necessary operations to reclaim their memory.
So the garbage collector will take care of releasing the object when it goes out of scope unless you need to release some expensive (memory consumption) resource. You can sets the reference of the instance to null, this will terminate the object, unless there is some other reference to the object:
Foo Foo1 = new Foo(5);
Foo1.Diminish();
if(Foo1.Data == 0)
{
Foo1 = null;
}
C# is a managed language, you can't "delete" anything, once something can no longer be referenced the value clean up will be handled by the Garbage Collector.
There are edge cases where you need to handle clean up, or be more aware of how you are handling creation:
Instances of classes that implement IDisposable interface should be disposed of by calling the Dispose() method or wrapped in a using statement
Unmanaged resource wrappers, such as IntPtr may need to be cleaned up using Marshal.FreeHGlobal(IntPtr) or Marshal.FreeCoTaskMem(IntPtr) if you are required to manage them and you're making a call to unmanaged code
Static (and possibly long living) values will never be un-referenceable unless you actively set variables to null or remove items from collections

How are reference types cleared from memory?

As objects are reference types they are stored in the heap and primitive data types are store on the stack.
But an object is a collection of primitive datatypes as well reference type i.e. a object may have a integer data member and/or may have another object within it.
When the scope ends the primitive data memory is released from the stack but the heap memory is handled by the garbage collector.
Now my question is: if an object also has a primitive data member then when are they removed?
As objects are reference types they are stored in the heap and primitive data types are store on the stack.
Not quite. Value types, which includes the primitives, but also struct types are stored on the stack when they are locals. They can also be stored on the heap if boxed or in an array or, as you note, a field of a reference type.
Reference types have one or more references which might also be stored on the stack—the local(s) you address it through—and the representation of the object itself on the heap.
When the scope ends the primitive data memory is released from the stack but the heap memory is handled by the garbage collector.
Not quite.
First, there isn't really a "releasing" operation. Say we were using 4 slots on the stack to store the values 1-4*:
[1][2][3][4][ ][ ][ ][ ]
^
Using up to here.
(I'm going to completely ignore the matter of what happens between function calls for the sake of simplicity).
Now say we stop using the last 2 slots. There's no need to "release" anything:
[1][2][3][4][ ][ ][ ][ ]
^
Using up to here.
Only if we go to, e.g. use 1 new slot to store the value 5, need we overwrite anything:
[1][2][5][4][ ][ ][ ][ ]
^
Using up to here.
The "releasing" just changed which memory was considered in use and which considered available.
Now consider the following C# code:
public void WriteOneMore(int num)
{
int result = num + 1;
Console.WriteLine(result);
}
Say you call that with the value 42. The relevant portion of the stack is:
[42]
^
Using up to here.
Now, after int result = num + 1; there are two values in scope; result and num. As such the stack might be:
[42][43]
^
Using up to here.
However, num is never used again. The compiler and jitter know this, so they might have reused the same slot:
[43]
^
Using up to here.
Because "in scope" refers to the source code, and what variables can be used in particular places, but the stack is used according to what variables actually are used in particular places, so it can often use less stack space than the source may suggest. Conversely, sometimes you find the same variable becoming more than one slot, if it makes things easier for the compiler in some way. This is no big deal here, but becomes important when we come to reference types.
the heap memory is handled by the garbage collector.
Let's consider what that actually means.
If an application needs heap memory for new objects, it takes that memory from a free part of a heap. If there isn't enough heap memory available it could ask the OS for more, but before that it may try garbage collecting.
When this happens, first the garbage collector makes a note of what heap-stored (reference types including boxed value types) objects it can't get rid of.
One set of such objects are those that are in a static variable.
Another is those that are in reachable parts of the stack. So if the stack is like:
["a"]["b"]["c"]["d"]["e"]
^
Using up to here.
Then the values "a", "b" and "c" cannot be collected.
The next set is any object that can be reached via a field of one of the objects that it already knows can't be collected, or through a field in one of those, and so on.
(A final step is any object that isn't ineligible due to the above, which needs to be finalised, they get put on the finalisation queue here, so they'll be eligible after the finaliser thread has dealt with them).
Now. On the heap, the object looks a bit like;
[Sync][RTTI][Field0][Field1] … [FieldN]
Here "Sync" marks the sync block used if you lock on the object. "RTTI" marks a pointer to type information, used to obtain the type and to enable virtual methods to work. The rest is fields, whether value-types contained directly or references to other reference types.
Okay. Let's say this object is one that the collector decides it can collect.
It simply changes that block of memory from being considered not available to use, to being available to use. That's it.
In a subsequent step all in-use objects get moved together to compact the used memory into one block and the free into another. Our old object might be overwritten at this point, or it might not be overwritten for some time to come. We don't really care, because the corpse of that dead object is just a bunch of 1s and 0s sitting there doing nothing now, waiting for the palimpsest of volatile memory to be written to once more.
So the primitive fields are released at the point where the object's memory is considered available to use, but again, they may still be present in RAM for some time, or not, they're just ignored.
It's worth remembering, that just as the values on the stack may not correspond to what is "in scope" in the source code, so therefore an object can be collected while it's in scope; garbage collection depends on the real use of the stack, not the source. This mostly doesn't affect anything, because most attempts to use something in the code means that it is now part of the real use of the stack and therefore won't be collected. Of the very few cases where it can affect something probably the most common is an attempt to use a Timer that is only referenced through a local; the main thread doesn't use it any more so that stack space can be used up and then the timing thread finds no such timer. This is where GC.KeepAlive() comes in.
*When it comes to the running code, locals might be stored in registers and never actually in the stack of memory. At the level of considering how the .NET code works, it's generally easiest just to consider them also "on the stack". At the level of considering how the machine code works, that's not true. When the garbage collector looks at what is "on the stack" to see what it can't delete, it also looks at what references are in registers.
It is very hard to explain such a fundamental but not always easy to understand things. However in the last 15 years many good explanation was written.
In case you do not want to read them (obviously...) here is a very short and (consequently not complete) wrap up: (note: still I strongly recommend to investigate toward in the literature)
Note: The following part is edited slightly based on comment conversation about "primitive type" terminology:
(edit)
In this question's context it is more appropriate to talk about "value type" instead of "primitive type". Regardless of the type is primitive or not, it only matters is it value type or reference type in this context.
(end edit)
Now the point:
Reference type have a reference (anywhere, like in heap or stack) which points the instance allocated always on the heap. Value type are stored (anywhere, like in the heap or stack) immediately embedded that place, so there is no indirection.
Samples:
Local variable of a value type: stack
Local variable of a reference type: instance itself on the heap, and the reference is on the stack
Member variable (value type): Embedded into the allocated space of the instance which's member variable it is.
Member variable (reference type): Its reference embedded into the allocated space of the instance which's member variable it is, and its instance on the heap.
Now my question is: if an object also has a primitive data member then when are they removed?
Answer: When the containing object is removed. (Hopefully it is clear based on the 4 samples: The containing object can be on the heap or on the stack, so the "containing object removal" could be a GC collection or a simple stack pointer set when returning from a method.)

Can the .NET garbage collector collect an object that is a member to a class?

I have a class that contains a pointer to an object inheriting off IDisposable
class Foo
{
private Bar bar;
private test()
{
bar = CreateBarFactory();
}
};
bar never is used beyond when it is set, in test();
I'm wondering if the GC is permitted to dispose of bar even if the Foo instance is still around?
if so is there a way I can prevent this? bar is a pointer to an object that contains a ref count, if it's Disposed() then the ref is released causing some functionality to be disabled
If the instance of foo is still around (i.e. someone is holding a reference to it.. keeping it alive), bar will not be collected. It will be treated as 'reachable' by the GC i.e. in use.
Members of reachable objects are also considered to be reachable.
Please don't mix up terminology. C# does have pointers, but that's not what you have there. You have a reference. Importantly, references do affect the garbage collector, whereas pointers, in and of themselves, do not.(*)
So long as the instance of Foo is reachable, it's reference to bar will keep that object reachable too. If Foo and Bar have finalizers, then, in the finalizer of Foo is the one circumstance where you shouldn't assume anything about bar - it may have already been finalized itself.
Even though, as you've indicated, no other method will be called that accesses bar, the GC and JIT don't perform this sort of analysis. For object references, the entire object is considered to be reachable, and any references it contains are followed and the objects located similarly are marked as reachable. This has to be done so that Reflection based access to an object will never obtain an invalid reference. This is also why, if your object has allocated a large helper object that you will no longer use, it can be (in this limited circumstance) useful to set the reference to null.
The only lifetime analysis that does consider what future code will run is the code within a single method body, with respect to the local variables. A local reference variable within a method is not sufficient to keep an object alive if no further references to that variable will occur. This is worked out collaboratively between the JIT and GC.
(*) people sometimes think that pointers keep objects alive. This is not strictly true. The act of pinning, which can yield a pointer, will keep an object alive, but there's nothing stopping you from keeping a pointer beyond the time that the object was pinned. Of course, de-referencing the pointer at any such time would be unsafe...
And, another terminology issue:
I'm wondering if the GC is permitted to dispose of bar
The GC doesn't dispose of anything. Disposal occurs when an object implements IDisposable and the user of the object calls Dispose on it (either directly or via e.g. a using block).
The GC doesn't call Dispose. It may call a finalizer, if one is defined on an object (and so my warning above would apply) and it then collects the object.
To work properly with this class, you should inherit it from IDisposable iterface and realize method Dispose() that will call bar.Dispose(). It will garantee that this expansive resource will be freed exactly when it can be freed, not when GC decide to Collect.

Is it true that - Garbage Collector won't collection the object of struct type

Yesterday, we had a discussion on Gargbage collection.
It was discussed that the objects created using Classes are collected by Garbage collector, but it cannot be collected by GC if it is being created using struct
I know that structures uses stack and classes uses heap.
But, I guess GC never collects unmanaged codes only. So does that mean that the Structure types are unmanaged code. (I don't think so).
OR is it that the GC take care of Heap only and not Stack?
If yes, then what about int data type. int is struct not class. So if we defined the object of type int, isn't it managed by GC?
The GC will collect any managed objects (and Structures are managed objects) if they cannot be accessed from a GC root.
but it cannot be collected by GC, if it is being created using struct.
What you were told is incorrect. It doesn't matter how a managed object was created - if there are no references to it anymore, it will end up being collected.
OR is that means the GC take care of Heap only not Stack?
The GC takes care of the object graph - if objects are reachable by any of the GC roots, they will not be collected, if they are not, they will end up being collected. The Stack and Heap are not relevant.
So if we defined the object of type int, isn't it managed by GC?
int (AKA System.Int32) is a managed object - a structure. If you declare an int field in a class and the class goed out of scope, the int will end up being collected by the GC, for example.
As #leppie commented, in many situations, structures will get placed on the stack and when the stack is popped they will no longer exist - the GC, in such cases, is not involved (and doesn't need to be).
it cannot be collected by GC if it is being created using struct.
Not true. If it's not referenced it will be collected eventually.
I know that structures use stack and classes use heap.
That is a common misconception. See this Lippert's article for details.
I think that the idea you're referencing to is that most often GC does not bother with collecting any data located on stack, because stack will be destroyed as soon as program execution leaves its scope. So that means that any data put directly into stack (which might mean value types and should mean references to all the other data) will be cleared automatically, no need to use GC. GC's work is to clear heap data independently of its (data) type. If it's not referenced - it's collected.
But, I guess GC never collects unmanaged codes only. So is that means the Structure types are unmanaged code. (I don't think so).
I don't understand what you are asking here.
OR is that means the GC take care of Heap only not Stack?
Yes and No. GC takes care of memory required for instances of reference types (always created on the managed heap). You can view the "stack" as a piece of memory associated with current thread of execution. The stack can contain handles to reference types allocated on the managed heap. In this case the GC "cares": it will not collect the memory from the managed heap for these instances until these references on the stack exist. The stack can also contain instances (not references to!) of value types, in which case the GC does not care...
If yes, then what about int data type. int is struct not class. So if we defined the object of type int, isn't it managed by GC?
This question is a bit misleading. Let's say you "allocate" an instance of an int on the stack:
void Foo()
{
// ...
int tTmp;
//...
}
In this case the GC does not care about tTmp. It is placed on the current thread's stack and removed, if it gets out of scope. But if you do this:
void Foo()
{
//...
var tTmp = new int [] {
1, 2, 3, 4
};
//...
}
then the array of 4 integers is created on the managed heap and GC takes care of tTmp. It also "indirectly" takes care of the memory required for the contents of the array, which happen to be the space required for the four integers...
Let's see what the .NET standard (ECMA-334) says
Value types differ from reference types in that variables of the value
types directly contain their data, whereas variables of the reference
types store references to their data, the latter being known as
objects. With reference types, it is possible for two variables to
reference the same object, and thus possible for operations on one
variable to affect the object referenced by the other variable. With
value types, the variables each have their own copy of the data, and
it is not possible for operations on one to affect the other.
In other words, there is no reason for garbage collector to care about value types, because they clean-up after themselves (when they go out of scope), they contain their own data. GC is for clean-up of shared (references) data.
Note that not only structures are value types:
A value type is either a struct type or an enumeration type. C#
provides a set of predefined struct types called the simple types. The
simple types are identified through reserved words.
So e.g. "int" type is so called "simple type", value type. It is bit different from other structures, because operations like +-*/ could end up being compiled into primitive operations, not function calls.

How does c++ auto_ptr relate to managed pointers (Java, C#...)

I come from a managed world and c++ automatic memory management is quite unclear to me
If I understand correctly, I encapsulate a pointer within a stack object and when auto_ptr becomes out of scope, it automatically calls delete on the pointed object?
What kind of usage should I make of it and how should I naturally avoid inherent c++ problems?
auto_ptr is the simplest implementation of RAII in C++. Your understanding is correct, whenever its destructor is called, the underlying pointer gets deleted.
This is a one step up from C where you don't have destructors and any meaningful RAII is impossible.
A next step up towards automagic memory management is shared_ptr. It uses reference counting to keep track of whether or not the object is alive. This allows the programmer to create the objects a bit more freely, but still not as powerful as the garbage collection in Java and C#. One example where this method fails is circular references. If A has a ref counted pointer to B and B has a ref counted pointer to A, they will never get destructed, even though no other object is using either.
Modern object orianted languages use some sort of variation of mark and sweep. This technique allows managing circular references and is reliable enough for most programming tasks.
Yes, std::auto_ptr calls delete on its content when it goes out of scope. You use auto_ptr only if no shared ownership takes place.
auto_ptr isn't particularly flexible, you can't use it with objects created with new[] or anything else.
Shared ownership is usually approached with shared pointers, which e.g. boost has implementations of. The most common usage, implemented e.g. in Boosts shared_ptr, employs a reference counting scheme and cleans up the pointee when the last smart pointer goes out of scope.
shared_ptr has one big advantage - it lets you specify custom deleters. With that you can basically put every kind of resource in it and just have to specify what deleter it should use.
Here's how you use a smart pointer. For the sake of example, I'll be using a shared_ptr.
{
shared_ptr<Foo> foo(new Foo);
// do things with foo
}
// foo's value is released here
Pretty much all smart pointers aim to achieve something similar to the above, in that the object being held in the smart pointer gets released at the end of the smart pointer's scope. However, there are three types of smart pointers that are widely used, and they have very different semantics on how ownership is handled:
shared_ptr uses "shared ownership": the shared_ptr can be held by more than one scope/object, and they all own a reference to the object. When the last reference falls off, the object is deleted. This is done using reference counting.
auto_ptr uses "transferable ownership": the auto_ptr's value can be held only in one place, and each time the auto_ptr is assigned, the assignee receives ownership of the object, and the assigner loses its reference to the object. If an auto_ptr's scope is exited without the object being transferred to another auto_ptr, the object is deleted. Since there is only one owner of the object at a time, no reference counting is needed.
unique_ptr/scoped_ptr uses "nontransferable ownership": the object is held only at the place it's created, and cannot be transferred elsewhere. When the program leaves the scope where the unique_ptr is created, the object is deleted, no questions asked.
It's a lot to take in, I'll grant, but I hope it'll all sink in soon. Hope it helps!
You should use boost::shared_ptr instead of std::auto_ptr.
auto_ptr and shared_ptr simply keep an instance of the pointer and because they are local stack objects they get deallocated when they go out of scope. Once they are deallocated they call delete on internal pointer.
Simple example, the actuall shared_ptr and auto_ptr are more sophisticated (they have methods for assignment and conversion/access to internal pointer):
template <typename T>
struct myshrdptr
{
T * t;
myshrdptr(T * p) : t(p) {}
~myshrdptr()
{
cout << "myshrdptr deallocated" << endl;
delete t;
}
T * operator->() { return t; }
};
struct AB
{
void dump() { cout << "AB" << endl; }
};
void testShrdptr()
{
myshrdptr<AB> ab(new AB());
ab->dump();
// ab out of scope destructor called
// which calls delete on the internal pointer
// which deletes the AB object
}
From somewhere else:
int main()
{
testShrdptr();
cout << "done ..." << endl;
}
output something like (you can see that the destructor is called):
AB
myshrdptr deallocated
done ...
Rather than trying to understand auto_ptr and its relation to garbage-collected references, you should really try to see the underlying pattern:
In C++, all local objects have their destructors called when they go out of scope. This can be harnessed to clean up memory. For example, we could write a class which, in its constructor, is given a pointer to heap-allocated memory, and in its destructor, frees this pointer.
That is pretty much what auto_ptr does. (Unfortunately, auto_ptr also has some notoriously quirky semantics for assignment and copying)
It's also what boost::shared_ptr or other smart pointers do. There's no magic to any of those. They are simply classes that are given a pointer in their constructor, and, as they're typically allocated on the stack themselves, they'll automatically go out of scope at some point, and so their destructor is called, which can delete the pointer you originally passed to the constructor. You can write such classes yourself. Again, no magic, just a straightforward application of C++'s lifetime rules: When a local object goes out of scope, its destructor is called.
Many other classes cut out the middleman and simply let the same class do both allocation and deallocation. For example, std::vector calls new as necessary to create its internal array -- and in its destructor, it calls delete to release it.
When the vector is copied, it takes care to allocate a new array, and copy the contents from the original one, so that each object ends up with its own private array.
auto_ptr, or smart pointers in general, aren't the holy grail. They don't "solve" the problem of memory management. They are one useful part of the recipe, but to avoid memory management bugs and headaches, you need to understand the underlying pattern (commonly known as RAII) -- that is, whenever you have a resource allocation, it should be tied to a local variable which is given responsibility for also cleaning it up.
Sometimes, this means calling new yourself to allocate memory, and then passing the result to an auto_ptr, but more often, it means not calling new in the first place -- simply create the object you need on the stack, and let it call new as required internally. Or perhaps, it doesn't even need to call new internally. The trick to memory management is really to just rely on local stack-allocated objects instead of heap allocations. Don't use new by default.
Choose an imperative language (such as C, C++, or ADA) that provides pointer types.
Redesign that language to abolish pointer types, instead allowing programmers to define recursive types directly.
Consider carefully the issue of copy semantics vs reference semantics. Implement an interpreter for the language using DrRacket .

Categories

Resources