Non-copyable types
A type that owns a resource has to decide what a copy means. If a Buffer
that owns a block of memory were copied field by field, two objects would
hold the same address, and two destructors would free it twice. For a
resource with one owner, the right answer is usually to refuse copies.
'' A Buffer owns a block of memory for exactly as long as it lives.
'' A copy would share the block, and two destructors would free it
'' twice, so copying is declared private and never defined.
type Buffer
declare constructor( byval length as int32 )
declare destructor( )
declare function Size( ) as int32
declare function At( byval i as int32 ) byref as uint8
private:
declare constructor( byref other as Buffer ) '' no copies
declare operator let( byref other as Buffer ) '' no assignment
as uint8 ptr bytes
as int32 count
end type
constructor Buffer( byval length as int32 )
bytes = callocate( length, 1 )
count = length
print " allocated"; length; " bytes"
end constructor
destructor Buffer( )
deallocate( bytes )
print " freed"; count; " bytes"
end destructor
function Buffer.Size( ) as int32
return count
end function
function Buffer.At( byval i as int32 ) byref as uint8
return bytes[i]
end function
'' Borrowing by reference is fine: no second owner is made.
function Total( byref b as Buffer ) as int32
dim as int32 sum = 0
for i as int32 = 0 to b.Size( ) - 1
sum += b.At( i )
next
return sum
end function
scope
dim block as Buffer = Buffer( 4 )
for i as int32 = 0 to 3
( block.At( i ) ) = i * 10
next
print " total"; Total( block )
end scope
print "after the scope"
Output
allocated 4 bytes
total 60
freed 4 bytes
after the scope
The constructor allocates and the destructor frees, so the code that uses a
Buffer contains no cleanup at all: block is freed at end scope.
The two private declarations are what make it safe. The copy constructor
and operator let are declared and never defined, and because they are
private, no code outside the type can reach them. Passing the buffer
byref, as Total does, makes no copy, so it is allowed. Anything that
would make a second owner is refused when the program is compiled:
type Buffer
declare constructor( byval size as int32 )
private:
declare constructor( byref other as Buffer ) '' no copies
as int32 count
end type
constructor Buffer( byval size as int32 )
count = size
end constructor
dim a as Buffer = Buffer( 4 )
dim b as Buffer = a '' a second owner? refused
nocopy.tiko(13,5): error TKO0633: 'b' needs a constructor or destructor that is not accessible here
|
13 | dim b as Buffer = a '' a second owner? refused
| ^
Assigning one buffer to another, b = a, reaches the private operator let
instead and is refused with TKO0746: the operator let of 'buffer' is private or protected and cannot be reached from here. Passing a Buffer byval
would copy it and is refused with TKO0633 too.
A type that owns something can copy it properly, be built from parts that copy themselves, or refuse to be copied. For a resource the system gives you once, such as a block of memory, a lock or a handle, refusing is usually right.
Activities
- Add a
Fill( byval value as uint8 )method and use it beforeTotal. - Write a
Totalthat takes the bufferbyval, and read the error.