Dynamic Memory Allocation in C++

In a video game, when a new character shows up in a scene, memory is allocated for the character. The game developers made sure the game allocates enough memory for the character before you interacted with it. This allocation happens dynamically during runtime, and it is known as Dynamic Memory Allocation.

C++ provides two methods for allocating and deallocating memory: Low-level memory management and an object-oriented memory management. Low-level memory management uses the malloc and free functions to allocate and deallocate memory. Object-oriented memory management uses the new and delete operator instead.

Let's understand how malloc and free works before we learn about the new and delete operator.

Low-Level Memory Allocation

malloc function

The function malloc returns the address of a memory location of a particular size. The syntax is as follows:

my_pointer=(typecast)malloc(size_of_memory);

For example, let's say you need a chunk of memory one integer size long. To request this chunk of memory, you call the malloc function as follows (line 1):

int main(){

    int *p=(int*)malloc(sizeof(int)); //1. Allocating memory

    return 0;
}

Couple of things to note in the example above:

  1. "p" is defined as a pointer. Why? Because only Pointers can receive addresses. The malloc function returns an address location.
  1. malloc returns an address, but it does not know the data type the address will represent. Since the pointer "p" is a pointer to an "int" data type, you must typecast the address with "(int *)".

  2. The argument of malloc represents the memory size you want to allocate. Typically, you use the function "sizeOf()" in the malloc argument to represent a size. In the example above, malloc allocated the representation of an "int" data type.

After memory allocation, you can use the pointer as usual. For example, in the snippet below, the pointer is dereferenced and given the value of five (line 2).

int main(){

    int *p=(int*)malloc(sizeof(int)); //1. Allocating memory

    *p=5; //2. Assigning the value of 5 to p

    return 0;
}

free function

The free function deallocates (gives back) previously allocated memory back to the system. You must always deallocate all previously allocated memory. If you do not, it will lead to memory leaks in your application.

The free function syntax is as follows:

free(my_pointer);

The snippet below deallocates the section of memory allocated in our previous example:

[example 3]

int main(){

    int *p=(int*)malloc(sizeof(int)); //1. Allocating memory

    *p=5; //2. Assigning the value of 5 to p

    free(p); //3. deallocate the memory

    return 0;
}

Object-Oriented Memory Allocation

The functions malloc and free are often used in the C Programming Language. In C++, the operators new and delete are used instead. The new and delete operators perform the same operation as malloc and free, respectively. However, the new and delete operators are often used to allocate memory for user-defined types such as classes.

new Operator

The new operator does the following operations:

  1. It allocates memory for a User Defined Type object.
  2. It initializes the object. i.e., it calls the class constructor. The initialization ensures that the object is properly initialized before use.
  3. It returns an address to the object allocated. As opposed to malloc, you do not need to typecast the return value.

The syntax for the new operator is as follows:

my_pointer=new User_Defined_Type();

The snippet below shows the allocation of a class object using the "new" operator (line 3)

[example 4]

class Student{

private:

public:
    Student(){}; //1. Constructor

    ~Student(){}; //2. Destructor

};

int main(){

    Student *p=new Student(); //3. Allocating memory for the class

    return 0;
}

delete Operator

The delete operator behaves exactly like the free function with a minor difference. It calls the class destructor before deallocating the object.

The syntax of the delete operator is as follows:

delete my_pointer;

The snippet below shows the deallocation of the class object using the "delete" operator (line 4)

[example 5]

class Student{

 private:

 public:
     Student(){}; //1. Constructor

     ~Student(){}; //2. Destructor

 };


 int main(){

     Student *p=new Student(); //3. Allocating memory for the class

     delete p; //4. Deallocating class object memory

     return 0;

 }

Remember this crucial point, the new operator calls the constructor of the class, whereas the delete operator calls the destructor of the class.

Hope this helps

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