ClassesΒΆ
This primer covers the declaration and use of classes.
A class is a type that can contain variables and constants, called
fields, as well as functions and iterators called methods. A new
class type is declared using the class
keyword.
class C {
var a, b: int;
proc printFields() {
writeln("a = ", a, " b = ", b);
}
}
The new
keyword creates an instance of a class by calling an
initializer. The class C
above does not declare any initializers,
and so the compiler-generated one is used. The compiler-generated
initializer has an argument for each field in the class. Once a class
has been initialized, its methods can be called.
Class types include a strategy for freeing them. We'll discuss this more below.
A class variable can refer to an instance of a class.
var foo = new owned C(1, 3);
foo.printFields();
Default output is supported so a class can be written by making a
call to write
or writeln
. Default input is also supported.
writeln(foo);
A class variable can refer to an instance of a class. Different class
variables can refer to the same instance. For example, alias
below
refers to the same memory that stores the fields of foo
.
We'll talk more about borrow
below.
var alias = foo.borrow();
now alias.b and foo.b refer to the same field, so the next line also modifies foo.b
alias.b -= 1;
writeln(foo);
Methods can also be defined outside of the class body by prefixing
the method name with the class name. All methods have an implicit
this
argument that is a reference to the class instance, or
object. The this
argument can be used to access a field
explicitly. For example, in the method below, the this
argument
is used to access the b
field which is otherwise shadowed by the
b
argument.
proc C.sum_a_b_b(b: int) {
return a + b + this.b;
}
writeln(foo.sum_a_b_b(3));
Here, a class named D
is declared as a derived class from C
.
This new class has all of the fields and methods from C
, plus any
additional fields or methods it declares. The printFields
method
has the same signature as a method from C
-- it is overridden.
class D: C {
var c = 1.2, d = 3.4;
override proc printFields() {
writeln("a = ", a, " b = ", b, " c = ", c, " d = ", d);
}
}
The static type of the variable foo
, declared above, is C
.
Because the class D
is derived from C
, the variable foo
can
reference an object of type D
. If an overridden method such as
printFields
is called, it is dynamically dispatched to the method
with the most specific dynamic type.
Note that since foo
is an owned C
, assigning to it
will delete the previous instance "owned" by that variable.
foo = new owned D(3, 4);
foo.printFields();
A class type includes a memory management strategy. The currently supported
strategies are owned
, shared
, unmanaged
, and borrowed
.
var unm: unmanaged C = new unmanaged C();
'unm' refers to a manually managed instance. It needs to have delete
called on it to free the memory.
delete unm;
var own: owned C = new owned C(1, 10);
The instance referred to by 'own' is deleted when it is no longer in scope. Only one owned C can refer to a given instance at a time but the ownership can be transferred to another variable.
var share: shared C = new shared C(1, 10);
The instance referred to by 'share' is reference counted -- that is, several shared C variables can refer to the same instance and will be reclaimed when the last one goes out of scope.
var tmp: borrowed C = new borrowed C(1, 10);
The instance referred to by 'tmp' will be deleted when it is no longer in scope. The ownership can't be transferred to another variable.
It is possible to borrow
from another class pointer.
One way to do that is by calling the borrow()
method directly:
var b1 = own.borrow();
now b1 and own refer to the same instance it is illegal to: * use the borrow after whatever it is borrowed from goes out of scope * use the borrow after the instance is deleted (for example if own is assigned to)
A class type without a decorator, such as C
, is the same
as borrowed C
. The this
argument of a method is a borrow as well.
The compiler automatically adds conversion from owned
, shared
,
or unmanaged
in the process of resolving a function call,
method call, or variable initialization.
var b2: borrowed C = own; // same as b2 = own.borrow();
own.printFields(); // same as own.borrow().printFields();
proc printSum(arg: borrowed C) {
var sum = arg.a + arg.b;
writeln(sum);
}
printSum(own); // same as printSum(own.borrow())
There are a few method names that cause the method to have special
meaning. A method named this
allows a class to be indexed like
an array. A method named these
allows a class to be iterated
over.
class ArrayLike {
var a, b, c, d: int;
proc this(i:int) ref {
select i {
when 1 do return a;
when 2 do return b;
when 3 do return c;
when 4 do return d;
otherwise halt("index out of bounds", i);
}
}
iter these() ref {
yield a;
yield b;
yield c;
yield d;
}
}
This ArrayLike
object can be indexed like a 4 element array and it
can be iterated over in a loop.
var a = new owned ArrayLike();
a(2) = 1; // call to this method
a(4) = 2; // call to this method
for elt in a do // invocation of these iterator
writeln(elt);