Lesson 23 - Classes
Vocabulary
Member: a variable that is part of a class is known as a “member” of that class. For example, let’s say we have a Person class that stores the height and age of a person. The variables for height and age are “members.”
Method:
Instance: Creating an “instance” of a class is essentially creating a variable with the class name as the data type. For example, “Person Ashwin;” creates an instance of the Person class where the “Object” is “Ashwin.” Similar to how “int firstNumber” creates an integer called “firstNumber” Here is the general anatomy for instance declaration.
Objects: The object is the “variable” created from a certain class.
Classes: Classes are building blocks for object-oriented programming and can be thought of as custom data types. Classes provide the outline for instances, in other words, they list what variables and functions each instance has.
Public: The public keyword signals to the program that anything listed as “public” should be accessible throughout the program. Specifically, public data types are able to be referenced through reference to the class within any scope of the program.
Private: The private keyword is much alike to the public keyword in that it tells the compiler where the data listed under private should be able to be accessed. However, anything declared as private can only be accessed within that class itself.
Protected: Data listed under a protected scope can only be accessed by the class itself and its derived (child) subclasses.
Data Types: Data types can be thought of as variable types. Common data types you’ve used/will use are int (integers), float, double, string, char, etc.
In VEX, classes can be beneficial when organizing control structures for multiple similar subsystems. Later on, in this course, we will go over how lessons could be helpful when writing your program.
C++: Object-Oriented Programming
C++ is an object-oriented programming language, meaning it is a programming language that uses classes (blueprints of data types) to create instances of objects during program runtime. You have been utilizing classes within C++ in many of the earlier lessons and projects; all data types are a class of their own.
Every instance of an integer variable you have created and assigned instantiates an integer class instance. In this lesson, you will learn how to make your classes to create objects like an integer.
In C++, a class can be thought of as a custom data type. We are familiar with the traditional data types of int, float, double, etc. On the other hand, classes can do more than the “primitive data types” (1). A class can store any data type within itself. (2) Classes can contain functions.
Storing Data Types
To clarify what we mean by the phrase “classes can store data types,'' let's consider the following example. Let’s say we want a class that represents a person, and the course will contain the height of the person and their age. Here is how we would do that:
In other words, we are simply declaring the variables we want in the class after “public:”
Let’s consider another example. Let’s say we want to create a class that represents a student, and also want the Student class to contain information such as a student ID number (integer), whether or not the student has perfect attendance (bool), and the age of the student (integer). The following code will represent the “Student” class.
Once again, the variables we want in the class are declared after the “public:” keyword.
Additional Resource: C++ Video
Creating an Instance and Modifying Characteristics of a Class (Members)
Creating an instance of a class is very similar to creating an int/bool/double/etc. We would make an instance of the class through the following code:
<ClassName> <Object_Name>;
For example, let’s say we wanted to create a Person named “Ashwin”, a Person named “Andrés”, and a Student named “theRealJohn,” we would write the following:
Now let’s say we wanted to modify the height and age of Ashwin and Andrés, would use:
<Instance Name>.variable_name = n;
where n is the value. For example, if Ashwin was 35 years old and 70 inches tall and Andrés was 7 years old and 76 inches tall, we would do the following:
Now to modify the Student ID number of “theRealJohn”, the attendance, and the age, it would be the same as how we modified “Ashwin” and “Andrés.” We recommend modifying these variables for yourself as practice.
Functions in Classes
As of now, we have only dealt with storing variables inside classes. We can also have functions within classes. To do so, we would simply declare the function inside the class.
For example, let’s say we want a function that prints the height and age of a Person (referring to the Person class from before). This is how we would do it:
It’s almost identical to creating a regular function, but instead, the function is declared inside the class.
Static v. Dynamic Memory Allocation *Optional Advanced Reading
In C++, there are two places where memory is stored: the runtime stack and the heap. The stack is the main point of memory for the running program; it is where objects with a KNOWN SIZE are stored (ex: a statically allocated int has a constant size of memory allocated regardless of what int value is assigned). The heap is where objects that will have a size determined only during runtime or their size is changing during runtime.
Inheritance
Let’s say we have a class representing a person:
We can create a subclass, a child class, in order to provide for a more specific instance of a Person. In other words, the Baby subclass is just the Person subclass with more variables/functions for later use.
Thus, the Baby class uses the Person class as a reference for its “base” functionality and has access to all public and protected members within the Person class. For example, if we create an instance of the Baby class like so:
We can then make a legal call to the parent function getAge() since the Baby class is a type of Person class:
The function called on line 1 will print 12 to the terminal, but line 2 will fail with a compilation error. This is because the Person class’s age variable is being declared with a private scope. The private keyword is much alike to the public keyword in that it tells the compiler where the data listed under private should be able to be accessed. However, anything declared as private can only be accessed within that class itself.
There is one more keyword commonly used within C++, and it is the protected keyword. Data listed under a protected scope can only be accessed by the class itself and its derived (child) subclasses. For example, let’s say we kept the Baby class the same as before but instead defined the Person class as:
In this case, we can run the following code:
Unlike before, BOTH lines 2 and 3 will output 12 to the terminal since the Child class is now allowed to access Person’s age variable.
Public v. Private v. Protected
Designing Well-Structured Classes
Constructors
Constructors are called upon the initialization of an object to “set up” the class object being made.
It is a method that has a type of the class itself and returns nothing when called.
Constructors can take parameters, just like functions, to store or use inputted data.
Constructors can be overloaded just as how functions can be overloaded.
A common use of a constructor would be to set all the variables inside the class equal to some default value as a placeholder.
In this case, we could run the following program:
Line 1 would create a Person object, “Ashwin,” with initialized values of age = 0 and height_in_inches = 0 since we created an instance of the Person object while inputting no parameters. The program will use the constructor that takes no parameters (just as how calls to overloaded functions work).
Contrastingly, line 2 would create a Person object, “Andrés,” with initialized values of age = 13 and height_in_inches = 12. Thus, line 2 calls the overloaded Person constructor that takes in age and height parameters, creating a very short (and accurate) Andrés object.
Public vs Protected vs Private
Variables: Typically, programmers have all of the variables inside classes as private. Then, to modify variables, there are separate functions to modify the private/protected variables (mutator functions) and get the private variable values (accessor/getter).
Functions: Functions will almost always be public. We will only have private/protected functions if we only want the function to be used within the class itself. For example, if we do not want “calc_hypotenuse” to be used anywhere else other than in the “print_sides” function, this would be a good use of a private function.
Scope of Class and Functionality as it Pertains to VEX
Classes in VEX (and most other scenarios) typically model a single entity. For example, we have seen classes that can represent a Person, Student, and RightTriangle. In VEX, you will most likely have classes that represent a certain subsystem. For example, let’s consider a robot named “Big_J.”
We can see the subsystems include a Chassis, Intake, and Lift. With this, a standard program will have 3 Classes, each class being for a distinct subsystem. In other words, there would be a “Chassis” class, “Intake” class, and “Lift” class, which all will contain the appropriate functions/variables for their respective subsystems.