-
So far these notes have focussed on variables and programming statements to manipulate those variables. We have also seen that functions allow the programmer to work information that is passed to them, or if the information is stored in
- Objects of Desire OOP is a powerful way to program but it is not easy to grasp especially if you are just beginning to program. The goal of this section is not to get the reader ready to write their own OOP code. Rather it is to support using objects in your program that are have been designed by others.
CAR / \ Toyota VW / \ / \ Camry Corolla Golf Beetle / \ / \ / \ / \ Bill's Joe's Sue's Josie's Abdul's Maria's Tao's Yuki's
Now we have one "superclass" of things with the name CAR, then two subclasses based on make and four sub-sub-classes based on model. Then we see 2 objects of each type identified with their owner's name. So there are 8 objects derived from 4 different classes. But those classes are in turn derived from classes which are derived from CAR. We could build on top by adding a bigger class, AUTOMOBILE, of which CAR is just one class along with TRUCK, SUV, etc. Add we could add at the bottom to distinguish between different kinds of Corolla's and Golf's.
PLAIN Ox OOP Ox -------------------------------------- variable ⇔ member function ⇔ method
That is, if you use - A Touch of Class Switching from the car analogy to the student analogy: Queen's has to keep track of large amount of information about each student. You know that somehow all this information is kept together and that your GPA is never accidentally matched with someone else's student number. The system does this by creating an object of class student for each student. Then all
- Defining a class to store a student's record
class Student { static decl nextid; const decl name, id, yr; decl major; Student(newname,newyr); declare(newmajor); }
Think of - Creating student objects
decl s1, s2; s1 = new Student("Amy Smith", 2017); s2 = new Student("Elon Musk", 1998);
The keyword Student::Student(newname,newyr) { name = newname; yr = newyr; id = nextid; ++nextid; }
Notice that the name of a method (or function) that belongs to a class starts with a prefix of the name of the class. So Student ------- nextid: 2 s1 s2 name: | Amy Smith | Elon Musk id: | 0 | yr: | 2017 | 1998 major: | ?? | ??
- Deriving Miss Daisy♭Worst Best Movie Ever? Recall for the car example where the Camry is a kind of Toyota. A class may derive from a previously declared class. In our student example, we need to track more information or process the same information differently depending on the kind of student. For example, students who transfer to Queen's from another university are like other students but their transfer credits have to be accounted for:
- Derive a transfer class from student
class Transfer : Student { const decl prevuniv; decl tcredits; Transfer(newname,newyr,prev); TCredits(transcript); }
Transfer::Transfer(newname,newyr,prev) { Student(newname,newyr); prevuniv = prev; }
So the constructor for - Virtual Reality Virtual functions allow a derived class to supply a new version of the virtual function in the derived class, replacing the version of the base class. When the base class calls the virtual function, it will actually use the function of the derived class. For a virtual function, the call can only be resolved at run time. Then, the object type is known, and the called function is the one first found in the object, when moving from the highest class towards the base class. A virtual function cannot be static.
globalvariables. Many traditional languages from the 1950s on had this kind of approach. However, modern like languages including Ox can process data in a more sophisticated way. This approach is called object oriented programming (OOP).
There is jargon that goes along with OOP which makes sense after you understand what is going on, but does not make it much easier to get started. We'll start with an analogy. Consider a real world thing, cars. The car in your driveway would be an object, and each different car is a different object. But each car is unique but each is an instance of a model, such as Toyota Corolla. In this analogy, Corolla would be a class (using Ox's standard terminology). Another class/model would be a Volkswagen Beetle. Models are not real things; they are a design for making cars that are real. So in Ox a class is like a car model. It is a description of what each instance or object of that class is.
A few more OOP concepts can be squeezed out of this analogy before we switch to something more specific. First, Toyota does not make only Corollas. But any car made by Toyota shares some things with all other Toyotas. So a Toyota Camry is a different model but of the same make as a Corolla. And VW Golf's are not Beetles but they are both VW makes. Now we have introduced four different classes (models of cars). But all of them are cars. So we can think of cars as being organized like this:
One thing you might be tempted to do would make this analogy with classes in Ox breakdown. Namely, Camrys and Corollas are both a style of car called a SEDAN, while a Golf is a HATCHBACK. This would mean that Bill's car inherits its properties from two different tree diagrams, the make/model tree shown above and the body style tree not shown. However, keep in mind that in most OOP languages, including Ox, each object is only derived from one tree. You can't build an objective as an intersection of two different classes, but you can make an object that is based on a hierarchy of classes so that different objects are like siblings, cousins, second cousins, etc. (You have cousins on both parents' sides but OOP doesn't allow that kind of double inheritance.)
The point of OOP is that the programmer (either you or the person who wrote the code that you want to use in your program) can define their own classes. As you know, Ox has pre-defined types of things: integer, double, matrix, array, etc. A program can have different sized matrices or different length arrays but an Ox program can't just decide to have its own type. However, we have not talked about one type of thing in Ox: a class. And the important thing is that the programmer gets to define their own classes. That concept sounds very flexible, but it is probably not as flexible as you might think. A user can only specialize specific aspects of class in both Ox and other OOP languages.
In particular, a class is a collection of variables combined with functions operating on those variables. There is more lingo now, which is confusing at first but eventually helpful.
decl x
you are creating a variable in Ox. However, if that is done inside a class, the Ox documentation will call that a data member or just member. Similarly, if you write myfunc();
you are declaring a function. But inside a class this would be called a method. This distinction is not too important, but mentioning it now might make it easier to read Ox documentation.
Besides being user defined
the powerful part of a class is that the data and functions are packaged together. We'll use the car analogy one last time before going on. Bill and Joe both own Camry's, but once they drive them off the lot the cars are different: different mileage, different scratches, different drinks spilled on the seats, etc. When they take their cars to the mechanic for service all of that information goes along with their car/object. The mechanic does not accidentally see the oil level of Bill's Camry when servicing Joe's Camry. So we can think of the mechanic's service like a function or method. And applying that function to the object (the specific car) is packaged with all the specifics.
However, suppose you see this in an Ox program: service(mileage,oil_level,brake_wear)
. That is, the function service
looks as mileage, oil, and brake status and decides what needs to be done. But the function can't guarantee that the programmer will send the correct values for each car. You might have (by mistake) service(miles_bill,oil_bill,brake_joe)
. The service will be wrong because Joe's brake status was sent by mistake instead of Bill's.
One advantage of grouping data and functions together is to avoid this mistake. That is, you can have the equivalent of Bill took his Camry for service
which makes such a mix up impossible. In Ox, this might look like: bills_car -> service()
. This code would ensure that all of the aspects of Bill's car (including that it is a Camry) would be sent to service. Not only that, what service
means can be different depending on whether Bill owns a Camry or a Golf.
methodsare guaranteed to have the right information because the data and functions go together. Let's create an Ox class for a student that tracks a few things:
Studentlike
Camry. It is a concept not an actual thing (although this will have to clarified below). On the other hand, think of "Bill's car" like
Amy Smith, a Queen's student. This class only keeps track of four things (name, id, yr and major). Obviously Solus keeps track of much more than that.
The keyword class
tells Ox that this is a declaration of a class named Student. This is similar to decl v;
except v
is a thing that exists once that declaration is executed. On the other hand, Student
is declared by the code above but it does not create any particular student. Here is how this is done:
new
creates a new object. It is always followed by the name of the class to use as a template for the object. Once this statement is executed s1holds an object of class Student that is associated with Amy Smith. The identifier that contains the object has to be declared (it has to exist at runtime in memory to store the object). In this sense
class
is like integer
or matrix
in that it is a kind of thing that an Ox variable can contain during execution. The difference is that the class is itself defined by the program's code and different variables can contain objects of different classes (or instances of the same class).
Notice that Student()
and declare()
are defined inside the curly brackets of Student just like you might define other functions. By being inside the class it means that they are functions that go along with the data in the class. The functions (or methods) can do all sorts of thing to information outside the class but when the variable id
is used inside the function it is guaranteed to be the information that goes along with that student. There is no way to create the mistake above where Joe's brake information was mixed with Bill's mileage.
So, notice that Student
is both the name of the class and the name of a method of the class. This is not a coincidence. This special method (the one with the same name as the class itself) is called the constructor. When your program has new Student(…)
the constructor will be called in order to initialize values in the new object. A class does not need to have a constructor. If there isn't one the new object is created with no data members set. Otherwise, if the constructor is declared inside the curly brackets of the class then the program has to provide its definition, just as with functions. Here is how it might look in this case:
Student::declare()
is a method of the class Student. (This next point may help you see the logic of what is going on: ordinary functions in Ox, such as print()
can also be referred to as ::print()
. In other words, print()
doesn't belong to any one class.) Other than the prefix the rest of the definition of the function is the same as any other Ox function. But a method in a class always can seeall the data members and other methods in the class. So on the first line of the constructor the
name
of the student is set equal to the name passed as an argument of the new Student()
command.
There is only one copy of a static member, shared by all objects of a class. A static member may not have the same name as the class it is in. Data members that are not static
, but are const
, can only be initialized in the constructor function.
A member function provides access to data members of an object. It is defined as its class name, followed by ::
and the function name. The function name must have been declared in the class. Member functions cannot be declared outside a class; the class declaration contains the member function declaration.
The new
operator creates an object of the specified class, calls the constructor function, and returns a reference to it. A member function is called through a member reference, which is a class object named followed by ->
or a dot.
The member function with the same name as the class is called the constructor, and is automatically invoked when creating an object of the class. If the constructor function is absent the object is created but none of its data members will be set.
Only the constructor function may set const
data members.
: Student
says this new class is derived from the already defined class Student
. That means that everything about a student is still available for a transfer. So if we create a new transfer we probably need all the information that a generic student need (in this simple case just name and year of enrolment). But we need to know where the transfer student is coming from so that is additional information. The constructor for a transfer student might look like this:
Transfer
calls the constructor for its parent class, Student
. This does not have to be true, but often it will make sense. Then the constructor does what is special about this class.
Students who have learned about OOP will probably see the value of this, but if you are new to programming this might seem unnecessary for computational economics. But even if you never create your own classes you may want to use built-in classes in Ox or another object-oriented language in the future.
A derived class will inherit all members from its base class, and can access these inherited members as its own members. However, if the derived class has members with the same name as members of the base class, the former take precedence. In this way, a class can redefine the functionality of its base class. If a function is redefined, the base class name followed by ::
may be used to refer to the base class function.