Reputation: 423
Which Object Oriented Programming concept is displayed by destructors? For example, overloading shows polymorphism. Please explain the reason for your answer. I could not find this anywhere on the web..
Upvotes: 2
Views: 1083
Reputation: 3411
The concept is Garbage Collection. In oop you can relate it with freeing up space that was previously allocated to any object which is no longer needed or deleted.
To quote the wikipedia
In object-oriented programming, a destructor (sometimes shortened to dtor) is a method which is automatically invoked when the object is destroyed.
It's role is useful in cleanup, as in freeing up memory space that is longer used by your program anymore. Since most of the modern programming langauge has automatic Garbage Collection, the explicit call to the destructor is no longer needed.
Read about GC and Finalizer for more info.
Hope it helps.
Upvotes: 1
Reputation: 113222
Most of the key OOP concepts are shown by destructors. If we consider those concepts as including Inheritance, Object, Class, Encapsulation, Method, Message Passing, Polymorphism, Abstraction, Composition, Delegation and Open recursion. Then we can show all of them being at play in destructors generally.
Now, generally "destructor" means a method that is defined in a class that is automatically invoked when an object is destroyed*. That obviously covers method, object and class.
Destructors encapsulate clean-up logic. Consider a structure that can point to another structure:
struct SomeStruct
{
SomeStruct* Next;
}
If the above was written in a language that didn't support object-oriented design by letting us define a method on SomeStruct
itself, and that deletes heap objects with a global delete()
method, then to clean up all the memory used by a SomeStruct we'd need to do something like:
CleanUpSomeStruct(SomeStruct* toDelete)
{
while(toDelete != null)
{
SomeStruct* deleteNext = someStruct->Next;
delete(toDelete);
toDelete = deleteNext;
}
}
Notably:
SomeStruct
outside of SomeStruct
.Next
directly, increasing the number of places we might do something unwise with it.Next
, then while that knowledge could be stored in SomeStruct
(perhaps an ownsNext
field), the logic of acting on that knowledge is external to SomeStruct
.If we have destructors then:
struct SomeStruct
{
SomeStruct* Next;
~SomeStruct()
{
if(Next != null) delete(Next);
}
}
In contrast to the above:
SomeStruct
is in SomeStruct
, close to other SomeStruct
-related code.Next
directly, decreasing the number of places we might do something unwise with it.Next
, then that logic can be stored in one place internal to SomeStruct
.On the flip side, since encapsulation means that we may not be able to access Next
encapsulation means that we have to have destructors to be able to clean up objects (or give the method an explicit "Clean Yourself Up" method, but having to remember to call it every time, and know if it even exists in a given case, and what it's called is a drag). Obviously this example wouldn't matter if we had automatic garbage collection, but in those cases we would similarly need to have destructors of some sort if encapsulation blocked outside code from necessary clean-up task.
Likewise, if we have inheritance, we need to have inheritable destructors that can pass the clean-up task up the line either implicitly or explicitly:
struct SomeOtherStruct : SomeStruct
{
SomeStruct* Prev;
~SomeOtherStruct()
{
if(Prev != null) delete(Prev);
base.~SomeStruct(); // Possibly this would be implicit in that
// the language would automatically make a
// call to a destructor finish with a call
// to any base destructors.
}
}
This also requires that destructors be abstract in the general sense of being part of the abstract model of what an object is (it may not have anything to do with an abstract
keyword used to enforce abstraction in other contexts). They must be polymorphic so that delete(Prev)
calls the most derived destructor in the object pointed to by Prev
whether it is ~SomeStruct()
, ~SomeOtherStruct()
or the destructor of yet another derived type, so Message Passing/Dynamic Dispatch is used to find the correct implementation of the abstract/virtual destructor. (A language may enforce this, or may allow non-virtual destructors as an optimisation).
Finally destructors interact with encapsulation and open recursion in that they will have to (perhaps implicitly) call the destructors of objects they are composed of, and perhaps call into methods on themselves to do this.
*"when an object is destroyed" is a plainer concept for languages/frameworks using deterministic deletion of objects than those with non-deterministic garbage collection. Generally in the later case "destructor" refers to a method that runs when that non-deterministic collection finally happens (assuming it ever does) but if they have a separate "disposal" method that can be deterministic then it may serve some of the rôles that deterministic destructors serve. In particular while deterministic destructors can be useful in providing for the RAII technique, non-deterministic destructors do not and any use of RAII-like approaches would have to be part of a deterministic disposal.
Upvotes: 2
Reputation: 423
Destructors implement what we know as "Resource Acquisition Is Initialization"
Upvotes: 0
Reputation: 6251
The practice of freeing up your program resources exists before OOP came to life. Even in old school C, without any objects, you have to free your memory accordingly or you get memory leaks, resource locks or other not nice consequences.
Upvotes: 1