[HolgerJ] 2015-06-25 - I was wondering whether objects can go out of scope and get deleted like they do in C++ (whenever the block ends) or in Java (when the reference count allows the garbage collection to remove it). At #EuroTcl2015 in Cologne we discussed possibilities, although objects in TclOO are commands and therefore are not tied to the block (or proc) where they have been created. The reason for getting this behaviour is avoiding memory leaks. If an object gets created locally and is not being used after the proc ends, there is no point in keeping it. Early return due to errors or explicit return statements can easily leave objects alive and cause memory leaks. This can cause the same headache as an open file (which is closed automatically in C++ when the stream goes out of scope). In many other cases, objects get created in procs (or methods) and get returned, which is called a factory pattern. These objects must not stop existing when the proc (or method) ends (for this case C++11 has the notions of a move constructor and a move assignment transferring ownership of all dynamic parts of the object, thus avoiding freeing of the object's memory). The first way discussed was tying the object to a local variable which I present as follows: ====== #!/bin/sh #\ exec tclsh "$0" "$@" package require TclOO namespace import oo::* # Account class from http://wiki.tcl.tk/20200 plus a little method to return current values class create Account { constructor {{ownerName undisclosed}} { my variable total overdrawLimit owner set total 0 set overdrawLimit 10 set owner $ownerName } method deposit amount { my variable total set total [expr {$total + $amount}] } method withdraw amount { my variable {*}[info object vars [self]] ;# "auto-import" all variables if {($amount - $total) > $overdrawLimit} { error "Can't overdraw - total: $total, limit: $overdrawLimit" } set total [expr {$total - $amount}] } method transfer {amount targetAccount} { my variable total my withdraw $amount $targetAccount deposit $amount set total } method getInfo {} { my variable {*}[info object vars [self]] ;# "auto-import" all variables return "account of '$owner' has a total of $total." } destructor { my variable total if {$total} {puts "remaining $total will be given to charity"} } } ;# class Account proc destroyObject {objname args} { puts "destroyObject $objname, calling destructor" $objname destroy } # This procedure creates an object and saves its name in the variable a. # That variable goes out of scope at the end of the procecure (Tcl has proc # scope, not block scope - similar to JavaScript). # The trace calls destroyObject when a goes out of scope. proc p {} { set a [Account new "John Doe"] trace add variable a unset [list destroyObject $a] puts "** created object named '$a' **" puts "depositing 200" $a deposit 200 puts [$a getInfo] try { puts "withdrawing 60" $a withdraw 60 } on error e { puts stderr $e } puts [$a getInfo] } ;# proc p puts "back in main (from proc p) and end of script." ====== The second way discussed was using a try/finally block to make sure that the destroying is always called, even in case of an error or other kind of early return from the proc. ====== # The class is the same as above. # This procedure creates an object and saves its name in the variable a. # That variable goes out of scope at the end of the procecure (Tcl has proc # scope, not block scope - similar to JavaScript). # The trace calls destroyObject when a goes out of scope. proc p {} { try { set a [Account new "John Doe"] puts "** created object named '$a' **" puts "depositing 200" $a deposit 200 puts [$a getInfo] puts "withdrawing 300" $a withdraw 300 return [$a getInfo] } finally { $a destroy } } ;# proc if [catch { puts [p] } err] { puts "error: $err" } puts "back in main (from proc p) and end of script." ====== <> Example | Object orientation