/Develop/Projects/Vcc/ |
|
k1.spdns.de / Develop / Projects / Vcc / |
intro | invocation | preprocessor | types | functions | operators | instructions
Memory is typically allocated and copied on request, and deleted automatically. The following names are related to memory management:
T alloc() return object cleared to all zero/null
T[] alloc(int count) return array
T realloc(T¢) return flat copy of objectvoid dealloc(T)not defined. internal only
T new(…) no keyword! suggested name for creator methods
T copyof(T¢) no keyword! suggested name for identical copy creator
member functions:
T T.kill(T) deconstruct object; prepare for dealloc()
void T.retain(T¢) increment reference count of object
void T.dispose(T¢) decrement reference count of object and evtl. delete it
bool T.is_shared(T¢) test reference count of object
meta data:
uint T.refcnt return reference count of object
alloc allocates a new object. All members or items are cleared to null or zero. This function is implicitely defined for all classes. If objects need no special initialization, they may be simply created with alloc().
According to Vcc syntax you can call a function from a set of identically named functions not only by selecting it by the argument types, but also by the return type, which is needed to use alloc:
MyType a = alloc MyType()
MyType[] b = alloc MyType[](count)
MyType[] c = alloc MyType[count]()
Arrays are objects and text strings are arrays. Arrays are allocated by passing a count value to alloc():
int[] a = alloc int[](55)
int[] b = alloc int[66]()
Version 1 allocates an array of 55 integers by passing the count as an argument to alloc().
Version 2 allocates an array "with an initial count of 66" and stores it in variable b.
New objects must be created explicitley. Just defining a new variable assigns them null as initial value. If objects have no special initialization, they may be created with alloc() instead.
new is not a keyword. Instead you can name any method you like new. But new is the recommended name for factory methods. A typical implementation for a class might be:
MyType new(int a)
{
MyType v = alloc MyType() // allocate it
v.myvalue = a // initialize it
return v // return it to caller
}
If you want to use new() instead of alloc() you can define it as opcode alloc:
MyType new() = opcode alloc
The compiler never duplicates objects of it's own. When a 'copy' is needed, it actually just increments a reference count in the object and still uses this object. This way objects can easily be shared if more than one instance needs to know about it.
If you want to create a copy of an object, you can either allocate it with alloc() and copy data members as required or use realloc(<type>¢), which provides a flat copy of the source object and start from there. realloc(<type>¢) is a built-in function for all types.
copyof is the suggested name for the method which creates an identical copy of an object. As new, this is not a keyword.
MyType copyof ( MyType¢ q )
{
MyType v = alloc MyType() // allocate cleared object
v.a = q.a // copy contents
return v // return it
}
MyType copyof ( MyType¢ q )
{
MyType v = realloc(q) // allocate object with flat copy of q
v.a = q.a// no need to do this here
return v // return it
}
The use of realloc() introduces a problem with data members which are itself allocated, because realloc() returns an invalid object: All new references to the contained objects did not have their reference count incremented. This must be corrected before the object can be returned:
My2Type copyof ( My2Type¢ q )
{
My2Type v = realloc(q) // allocate object with flat copy of q
v.text1 := v.text1
v.text2 := copyof(v.text2)
v.text3.retain()
return v // return it
}
Version 1 stores text1 (strings are arrays and arrays are objects!) into text1 again using the assignment operator := which ignores the old content of the target.
Version 2 stores an explicit copy of text2 into text2 again, again with the assignment operator :=.
Version 3 calls the built-in member function retain() to fix the reference count.
Objects are automatically destroyed when their reference count sinks to zero. This typically happens when local variables come out of scope. Normally you will never need to call dealloc() manually. If you want to free memory before a variable comes out of scope assign it null:
myFoo = null;
Before deallocation objects are deconstructed. The user may define a member function called kill() to do this. If kill is not defined, the compiler generates a default version. This releases all contained allocated objects which - if they are not referenced by other variables - also are destroyed.
Example of a kill() member function:
scope My2Type // must be a member function
{
My2Type kill ( My2Type v ) // this is the required signature!
{
LogStr("My2Type object destroyed\n")
v.text1=null
v.text2=null
v.text3.dispose()
return v
}
}
If you need to know whether an object is shared with other referencers you can get the reference count refcnt or use member function is_shared().
operator=() is built-in for all types and cannot be replaced. operator=() stores the source value in the destination variable and then destroys the old content, calling kill() and dealloc() if the data type is an object.
operator:=() is built-in for all types and cannot be replaced. operator:=() just stores the source value in the destination variable ignoring the old content. Only use this to populate a new object allocated with alloc() or realloc(…) or if you know for shure that the old content is null.
Pointers '*' and references '&' are supported but discouraged. If the target system uses a memory model with moving memory – typical for small systems – then any memory allocation can force a garbage collection and a pointer to a variable inside the dynamic memory become void. These variables are items in an array and data members in structured objects, global and local variables itself are typically not affected. Even if the system uses a memory model with fixed addresses then the object or array may be deallocated while you are still using the pointer. E.g. resizing an array creates a new object while deleting the old one! If you cannot guarantee that this won't happen, you must assume that it will happen sometime! So in general: don't use references and pointers.
Objects may also be passed as 'quick copy'. The reference count is not incrementented when the quick copy is created and it is not decrement when it is destroyed. This is sometimes used for procedure arguments.
void CentFoo ( str¢ text )
{
print(text)
}
Faster and shorter, because the reference counter is not incremented/decremented and because of that no destructor code is required.
You must assert yourself that the object is not destroyed while you use the 'quick' copy.
If you pass a temporary value to this function, then the compiler must add the destructor code on the caller side:
CentFoo( numstr(66) );
numstr() returns a value and since CentFoo() does not destroy it the compiler must take care of this.
The compiler takes care not to destroy objects which are still in use. But you can easily create objects which are never ever released.
type Foo =
{
Foo a
}
Foo init(Foo v) { v.a=v }
Foo new() { return alloc Foo().init() }
Objects of this class will never be deleted because there will always one reference left: the one they hold themself.
Another example, a linked list:
type Foo =
{
Foo next,prev
}
Foo root
Foo init(Foo v)
{
if((int)root) { v.next=root; v.prev=root.prev; root.prev=v }
else { v.next=v v.prev=v root=v }
}
An object of this class keeps it's neighbours alive. You must manually release the ring, e.g. with an unlink() function.
Name | Letzte Änderung | Länge | |||
---|---|---|---|---|---|
lib-Z80-Rop/ | 2020-11-30 15:50 | 21 | |||
lib-Z80-Vss/ | 2017-11-12 09:55 | 452 | |||
lib/ | 2019-10-30 17:02 | 3 | |||
Libraries/ | 2019-10-30 17:11 | 6 | |||
OSX-Qt40/ | 2019-10-30 17:02 | 5 | |||
OSX/ | 2020-09-05 12:54 | 6 | |||
Source/ | 2019-10-30 17:02 | 11 | |||
Tests/ | 2019-10-30 17:02 | 9 | |||
LICENSE | 2019-10-30 17:25 | 1323 | |||
README.md | 2019-10-30 17:25 | 288 | |||
TODO.txt | 2020-10-18 12:42 | 340 |