User-Defined Types in C++: Part II

The second mechanism used to create User-Defined Types is a class. A class is made up of data and function members. A function that belongs to a class is called a Method.

Syntax

A Class consists of the following syntax:

class User_Defined_Type_Name{

 private:

 public:

 };

The class keyword declares a class of type "User_Defined_Type_Name" and defines the data members within the class.

A class has a Constructor and Destructor method. The constructor method initializes the class to a safe state. The destructor cleans up after the class object is no longer needed. Furthermore, the constructor and destructor have the same name as the class.

For example, let's define a Student class with a Constructor and Destructor methods.

class Student{

 private:

 public:
    Student();  //1. Constructor

    ~Student(); //2. Destructor

};

Line 1 shows the constructor of the class. Line 2 illustrates the destructor of the class. Notice the tilde symbol "~" prefixed to the destructor's name.

In practice, you create a class object as shown in the snippet below (line 3). Upon creation of the object, the constructor method is called. When the class object is destroyed, the destructor method is called.

int main(){

    Student harold; //3. Create a class object

    return 0;
}

Access Specifiers

As mentioned, a class contains data members and methods. These members can be accessed through the dot operator "." However, their access is controlled by the class Access Specifier.

There are three kinds of Access Specifier: Public, Private or Protected. Members declared within the Private Access Specifier are not accessible outside the class. Whereas, members declared within the Public Access Specifier are accessible outside the class.

For example, line 1 shows the data member "age" defined within the private access specifier.

class Student{

 private:

    int age; //1. define data member

 public:
     Student();  //2. Constructor

     ~Student(); //3. Destructor

};

If you try to access this data member as shown below, you will get an error (line 2). Data members can only be accessed outside the class if it is defined within the public access specifier.

int main(){

    Student harold; //1. Create a class object

    harold.age=29; //2. ERROR. Age is a private data member
    return 0;
}

So, why not declare all members as Public?

C++ is an Object-Oriented Programming (OOP) language. One of the principles of OOP is to encapsulate data members as private and access them through the public access interface.

Instead of making "age" public, we are going to keep it as private but access it through a method declared in the public access specifier, also know as the Public Interface.

For example, in the snippet below, the "setAge" method is declared in the Public Interface (line 4). And will be responsible for setting the value of the data member "age."

class Student{

private:

    int age; //1. define data member

public:
    Student();  //2. Constructor

    ~Student(); //3. Destructor

    void setAge(int uAge); //4. Method to access the private "age" member
};

Now, let's define the "setAge()" method.

Defining Methods in C++

In C++, a method is defined with the following syntax:

return_type class_name::method_name(arguments){}

For example, the setAge() method is defined as follows

void Student::setAge(int uAge){}

The Constructor and Destructor methods are defined similarly but without a return type.

Student::Student(){} //Constructor
Student::~Student(){} //Destructor

The code snippet below shows the definition of the constructor, destructor and "setAge" methods (see lines 6,7 & 8).

Notice an additional method "getAge" defined in line 9. This method retrieves the value of the data member.

class Student{

private:

    int age; //1. define data member

public:
    Student();  //2. Constructor

    ~Student(); //3. Destructor

    void setAge(int uAge); //4. Method to set the value of private "age" member

    int getAge(); //5. Method to retrieve the value of private "age" member
};

Student::Student(){

    std::cout<<"Creating Class"<<std::endl; //6. Print "Creating Class"
}

Student::~Student(){

    std::cout<<"Removing Class"<<std::endl; //7. Print "Removing Class"
}

void Student::setAge(int uAge){

    age=uAge; //8. Set "age" value
}

int Student::getAge(){

    return age; //9. Return "age" value

}

Initializing Data Members

Data members must be initialized in the constructor using an Initialization List. The syntax is as follows:

class_name::constructor():data_member1(value1),data_member2(value2){}

In the example below, the "age" data member is initialized to zero. See line 1.

Student::Student():age(0){ //1. Initialization "age" to zero.

}

Using the Class

The final version of the Student Class is as follows:

class Student{

private:

    int age; //1. define data member

public:
    Student();  //2. Constructor

    ~Student(); //3. Destructor

    void setAge(int uAge); //4. Method to set the value of private "age" member

    int getAge(); //5. Method to retrieve the value of private "age" member
};

Student::Student():age(0.0){

    std::cout<<"Creating Class"<<std::endl; //6. Print "Creating Class"
}

Student::~Student(){

    std::cout<<"Removing Class"<<std::endl; //7. Print "Removing Class"
}

void Student::setAge(int uAge){

    age=uAge; //8. Set "age" value
}

int Student::getAge(){

    return age; //9. Return "age" value

}

In practice, you create a class object as shown in the snippet below (line 1).

Line 2 shows how to set the value of "age" using the "setAge" method. Line 3 shows how to retrieve the value of "age" using the "getAge" method. Lastly, the age is printed in line 4.

int main(){

    Student harold; //1. Create a class object

    harold.setAge(29); //2. set age to 29

    int studentAge=harold.getAge(); //3. get the current age

    std::cout<<"Age: "<<studentAge<<std::endl; //4. print the age

    return 0;
}

Now that you know about classes, it's time to learn about the new and delete operators utilized in the creation and destruction of a class.

Hope this helps.

User-Defined Types in C++: Part 1

Data types such as int, float, char are known as built-in types. Although useful, built-in types do not provide the flexibility required to write complex applications. C++ circumvents this issue by providing a mechanism to create user-defined types. User-defined types provide a better representation of program objects used in complex applications.

User-defined types are made up of built-in types and the mechanism use to create them are:

  • Structures
  • Classes

In this article, I will talk you about Structures. In Part II of this article, I will talk about Classes.

Structures

A Data Structure provides a mechanism to organize several built-in types into a single entity. A Data Structure consist of the following syntax:

struct User_Defined_Type_Name{

built_in_type;
built_in_type;
built_in_type;

}

The struct keyword declares a structure of type "User_Defined_Type_Name" and defines the built-in types within the structure. Built-in data types within a structure are known as Data Members.

For example, the code snippet below declares a Structure of type Student with several data members.

struct Student{

    //Data members of the structure
    int age; 
    float GPA;
    char grade;
};

Student is an User-Defined Type. When you define a variable of type "Student," an object is created with a memory location and storage.

For example, in the snippet below, line 1 defines a structure, harold, of type Student. The definition allocates enough memory to hold all three built-in data types. In this instance: grade, GPA, and age.

int main(){

    Student harold; //1. Declaring a structure variable

    return 0;
}

Accessing data members

To access a data member in a structure, you must use the dot "." operator. For example, in the snippet below, the data member "age" is accessed using the dot operator (line 2).

int main(){

    Student harold; //1. Declaring a structure variable

    harold.age=29; //Access the structure data member "age"

    return 0;
}

The dot operator is used when a Structure is local to a function, has been declared globally or is "passed by value" to a function.

For example, in line 6, the structure is "passed by value" to the function "printAge." In line 2, the data member is accessed using the dot operator.

void printAge(Student uStudent); //1. Declaring function with "pass by value" argument

void printAge(Student uStudent){

    int age=uStudent.age; //2. Accessing the structure data member with "." operator

    std::cout<<"Print Age: "<<age<<std::endl; //3. Print student age

}

int main(){

    Student harold; //4. Declare a structure variable

    harold.age=29; //5. Set age of student

    printAge(harold); //6. call function and pass the structure by value

    return 0;
}

The "->" is also used to access the structure's data members. However, the "->" operator is used when a structure is "passed by reference" to a function.

For example, in line 5, the structure is "passed by reference" to the function "changeAge." In line 2, the data member is accessed and modified using the "->" operator.

void changeAge(Student *uStudent); //1. Declaring function with "pass by reference" argument

void changeAge(Student *uStudent){

    uStudent->age=30; //2. Accessing the structure data member with "->" operator

}

int main(){

    Student harold; //3. Declare a structure variable

    harold.age=29; //4. Set age of student

    changeAge(&harold); //5. call function and pass the structure by reference

    std::cout<<"Print age:"<<harold.age<<std::endl; //6. Print the new age of student

    return 0;
}

In Part II of this article, I will talk about the second mechanism used to create user-defined types. i.e., Classes.

Hope this helps.

Understanding References in C++

A C++ Pointer is a scary concept to beginners. The idea of a Pointer is simple, but its syntax is not. For example, the asterisk symbol "*" can be used to define or to dereference a pointer; the position of the asterisk determines if the Pointer is either being defined (see line 2 below) or is dereferenced (line 3).

int main() {

    int a=3; //1. Defining a variable

    int *myPointer=&a; //2. Defining a pointer

    int b=*myPointer; //3. Dereferencing a pointer

    return 0;
}

References are friendlier alternatives to pointers with nicer syntax. With References, the C++ compiler handles all the details of dereferencing a Pointer, so you don't have to.

Reference properties

A Reference is another name for a variable, i,e., an alias. A reference is not an object; It has no storage space and no memory location. In contrast, a variable and a pointer are objects. i.e., they have storage space and a memory location. This difference gives references unique requirements, such as:

  1. You must initialize a reference to an existing object.
  2. Once initialized, it can not refer to a different object.
  3. You can not manipulate a reference as an independent entity. They are simply a different name for an existing object.

Reference in action

When used in a declaration statement, the ampersand symbol "&" initializes a reference. For example, the code snippet below initializes the reference 'myReference' to the variable 'myVariable.' (see line 2)

int main(){

    int myVariable=3; //1. Defining a variable

    int &myReference=myVariable; //2. Declearing a reference and initializing it to myVariable

    return 0;
}

If you modify the value of 'myVariable,' the reference's value also changes. Remember, a reference is simply another name for a variable. The code snippet below shows this property.

In the snippet below, we modify the 'myVariable' value to 5 (line 3).

Lines 4 and 5 prints the value of 'myVariable' and 'myReference.' In this example, the project prints the value of 5 for the variable and reference.

int main(){

    int myVariable=3; //1. Defining a variable

    int &myReference=myVariable; //2. Declaring a reference and initializing it to myVariable

    myVariable=5; //3. Modify the variable

    std::cout<<myVariable<<std::endl; //4. Print the value of the variable

    std::cout<<myReference<<std::endl; //5. Print the value of the reference

 return 0;

 }

In the same manner, if 'myReference' changes value, 'myVariable' also changes value.

So, if a reference is simply another name for an existing object, why go through the trouble of using them?

The answer is that references provide a simpler syntax than pointers. When using pointers, you need to deal with passing an address and dereferencing a pointer. With references, the compiler takes care of these details for you.

For example, the following code snippet uses pointers. The function's arguments is a Pointer (line 1). Inside the function's definition, you must dereference the pointer to retrieve its value (line 2). And if you want to change the value, you must dereference the pointer (line 3).

int add(int *a); //1. argument is a pointer

int add(int *a){

    int b=*a+1; //2. dereference the pointer and increment the value by 1

    *a=5; //3. modify the value of the variable

    return b; //4. return the value of b
}

In practice, the function call requires the address of the variable. See line 2 in the snippet below:

int main() {

    int myVariable=2; //1. define a variable

    int myAnswer=add(&myVariable); //2. pass the address of the variable

    return 0;

}

The same function becomes a lot cleaner with references. Take a look at the code snippet below:

int add(int &a); //1. Function arguement requires a reference

int add(int &a){

    int b=a+1; //2. No need to dereference the variable being passed

    a=5; //3. No need to use dereference when you modify the variable

    return b;

}

The function's argument is a reference instead of a pointer (line 1). Inside the function's definition, there is no need to dereference the variable being passed to the function (lines 2 & 3). With references, you treat the variable like any other variable inside the function.

In practice, the function call does not require special syntax, such as passing its address. See line 2 in the snippet below:

int main(){

    int myVariable=2; //1. Define a variable

    int myAnswer=add(myVariable); //2. pass the variable without the need to pass its address

    return 0;
}

In summary, references were created to alleviate the pain of working with Pointers. However, references are NOT pointers. They are simply aliases for existing variables or pointers.

Hope this helps

Passing by value vs Passing by reference in C++

The concept of passing a variable to a function by value or by reference is somewhat confusing to beginners. It was difficult for me to comprehend until I learned the essential elements of a variable.

A variable is a named memory location that stores a value. However, there is more to this. A variable is composed of a rvalue (right value) and a lvalue (left value). The rvalue represents the value stored in the memory location. Whereas, the lvalue designates the address of the memory location.

When you pass a variable to a function, you either pass the variable's rvalue or lvalue. If you pass the rvalue to a function's argument, you are passing the variable by value. However, if you pass the variable's lvalue, you are passing the variable by reference. Passing a variable by reference equates to saying "passing its address to the function."

So what's the point of all of this? The point is this: Passing the address of a variable is the only way to have a function modify the content of a variable.

Let's talk about the differences between each method.

Difference in function declaration

Functions that receive a value through its argument are declared as:

int add(int a);

In practice, the function is use as follows:

int main(){

int myVariable=2; //1. define a variable
int myAnswer=add(myVariable); //2. pass the variable by value

return 0;
}

In line 1, you define a variable named 'myVariable.' In line 2, the variable is "passed by value" to the function 'add.'

In contrast, functions that receive an address through its argument are declared as:

int add(int *a);

The argument is declared as a pointer since pointers can receive addresses.

In practice, the function is use as follows:

int main(){

int myVariable=2; //1. define a variable
int myAnswer=add(&myVariable); //2. pass the variable by reference

return 0;
}

In line 1, you define a variable named 'myVariable.' However, in line 2, the variable is "passed by reference" to the function 'add.' The symbol '&' means "The address of."

Difference in function definition

There are differences in the function definition for each method. In a "pass by value" method, a function is defined as:

int add(int a){

int b=a+1; //1. increment the incoming value by 1

return b; //2. return the value of b
}

In the code snippet above, the function increments the value of 'a' by 1 (see line 1). Line 2, returns the value of b.

In a "pass by reference" method, a function is defined as:

int add(int *a){

int b=*a+1; //1. dereference the pointer and increment the value by 1

*a=5; //2. modify the value of the variable

return b; //3. return the value of b
}

Line 1 dereferences the pointer 'a.'; it accesses the value the pointer 'a' points. The value is then incremented by 1.

Line 2 modifies the value the pointer 'a' points to and sets it equal to 5. Line 3 returns the value of b.

Examples of each method

Let's go through a complete example of each method:

"Pass by value" method

In the code snippet below, lines 1 and 2 declare and define the function 'add.'

Line 5 shows the start of the 'main()' function. Line 6 defines a variable 'myVariable.' This variable is "passed by value" to the function 'add' in line 7.

Lines 8 and 9 prints the value of 'myAnswer' and 'myVariable.'

int add(int a); //1. declare a pass by value function

//2. define a pass by value function
int add(int a){

    int b=a+1; //3. increment the incoming value by 1

    return b; //4. return the value of b
}

//5. main function
int main() {

    int myVariable=2; //6. define a variable

    int myAnswer=add(myVariable); //7. pass the variable by value

    std::cout<<myAnswer<<std::endl; //8. print the value of myAnswer

    std::cout<<myVariable<<std::endl; //9. print the value of myVariable

    return 0;
}

When you run this program, the output of 'myAnswer' is 3, and 'myVariable' is 2. As expected, the function did not change the value of 'myVariable.'

"Pass by reference" method

In the code snippet below, lines 1 and 2 declare and define the function 'add.'

Line 6 shows the start of the 'main()' function. Line 7 defines a variable 'myVariable.' This variable is "passed by reference" to the function 'add' in line 8.

Lines 9 and 10 prints the value of 'myAnswer' and 'myVariable.'

int add(int *a); //1. declare a pass by reference function

//2. define a pass by reference function
int add(int *a){

    int b=*a+1; //3. dereference the pointer and increment the value by 1

    *a=5; //4. modify the value of the variable

    return b; //5. return the value of b
}

//6. main function
int main() {

    int myVariable=2; //7. define a variable

    int myAnswer=add(&myVariable); //8. pass the variable by reference

    std::cout<<myAnswer<<std::endl; //9. print the value of myAnswer

    std::cout<<myVariable<<std::endl; //10. print the value of myVariable

    return 0;
}

When you execute the program above, the output of 'myAnswer' is 3. However, 'myVariable' was modified and now equals 5.

The concept of "pass by value" vs. "pass by reference" may be difficult to grasp. In doubt always remember: "passing by reference" is the only way to have a function modify the content of a variable.

Hope this helps

Reference: C++ Pointers and Dynamic Memory Management

On this Mac, I developed a Game Engine

Two weeks ago, my Mac stopped working. I took it to the Apple store so they can diagnose the problem and unfortunately, the hard drive was no longer usable.

I lost all of my data and ended up buying a new Mac. Yes, I could have bought a new hard drive, but I've had this Mac for over six years, and it was time to get a new one.

My Mac with a blinking question mark after a reboot

My Mac with a blinking question mark after a reboot

When the Apple technicians told me that my Mac was no longer usable, I got sad. Not because I lost my data, but because I've had built a relationship with my laptop.

No, I am not weird. Let me explain.

Developing a Game Engine is hard. It takes a lot of dedication, persistence, and motivation. It took me over three years to develop a game engine. I had worked on it before heading to work, during lunch, after work and during the weekends. I can say that I worked over 15,330 hours on it.

Game Engine Development is not that hard technically-wise. It is hard psychologically-wise. You can spend three or four months of work only to implement a simple feature. You can spend two to three months trying to implement an algorithm and fail. I spent almost a whole year trying to get the Collision Detection System to work.

To be honest, I wanted to give up every single day of development. I was mentally tired. The enthusiasm that I had when I started development was gone. There was no desire in my heart to finish the game engine. I was spiritually tired.

And throughout all this time, my Mac was with me. It witness all the hours I put on it. It witnesses me dancing when I successfully implemented the first beta version of the engine:

 
 

It heard me swear in Spanish when the engine would crash. It saw me stared at it for hours pondering the idea of giving up. And it saw me when I screamed and jumped when I implemented the basic framework of the engine (Beta version v0.0.2):

 
 

It saw the game engine first demo:

 
 

The night before it died, I was preparing a demo to showcase version v0.0.3 of the engine. It was going to showcase the final piece of the engine framework. But my Mac never saw this demo working:

 
 

The next morning when I tried to turn on the computer, a folder with a blinking question mark showed up on my screen. I tried rebooting the computer, but it was useless.

I got sad when I realized my Mac was no longer usable. It may sound weird; I agree with you. But I am not sad because it no longer works. I am sad because, through this Mac, I learned to persist.

Thanks for reading.