Now that we know how to use pointers to point to objects,
the rest of the module will be about using pointers inside
of objects as data members.
We'll begin this journey by imagining that we are genetic
engineers. And just a disclaimer, I'm not very good at
biology and you don't have to be either.
This is just hypothetical.
I created a simple DNA class, which holds the genetic
code inside of this really large array.
I used digit separator syntax to make this more readable.
But the code array holds 60 million characters,
which is equal to 60 MB of memory.
The real DNA sequence would be much larger, but the reason for
creating this big array is to make it impossible for us to use
this DNA object on the stack.
I will try to declare one just to prove that this is not possible.
If I try to compile this program,
I will get a segmentation fault because this is too big for stack memory.
So let's instead allocate this DNA object on the heap.
Instead of reading the genetic code from a file and making things more
complicated, let's agree that the first four elements of the code array
represent the whole sequence. And I want to initialize these four
elements with a simple string literal.
Of course, we need to define this constructor.
We learned in one of the previous lessons that we can pass the string literal
to a function by using the constant pointer to a char value.
It has to be constant because the string literal is
in a read only part of memory.
I will simply rip out all of the characters from this literal and store
them at the start of the code array in the same order.
This is one of those situations where we actually use the
array of characters as an array of characters.
I don't want to use this code array as a string.
It is literally just a sequence of characters and each one of these
characters should be one of the four possible genetic bases,
C, G, T, or A.
I also want a way to print out this code, so I will create another
function for that. We will simply loop over the first four
characters and print them out to the terminal.
We could have used the null character at the end of this
sequence, but I still want to pretend that all of the
other elements are initialized.
And now this constructor should work.
Let's test it by using the printGeneticCode function.
Don't forget to use the arrow operator because sample is a pointer.
The code is presented correctly, so it seems that
we are done with the DNA class.
Since we are now scientists,
we also need to have subjects for the experiments. I
will create a simple Subject class.
Every subject has a unique integer ID and also a DNA sample
which represents its genetic material.
Since DNA class is really large,
this kind of implementation would prevent us from
instantiating subjects on the stack.
So instead of having the sample inside of the subject itself,
we can turn it into a pointer to a DNA object.
Now, let's define the constructor. It will accept the ID of the
subject and also the string literal for the DNA sample,
I want to pass this string literal pointer to the DNA object, but the object
doesn't exist yet, we only have an empty sample pointer.
So inside of this constructor,
we actually need to allocate space on the heap for this DNA object and
store its location inside of the sample pointer. I will pass this
pointer to a string literal down to the DNA constructor and this is
where we need to start thinking about all of the side effects from
using pointers as data members.
Since we allocated this DNA resource inside of the constructor,
we need to deallocate it inside of the destructor.
So let's do this. I hope that you remember the concept of RAII
from the first module because this is it.
All of the resources that we allocate inside of the
constructor need to be released inside of the destructor;
otherwise, we are risking a memory leak.
I also want to create a default constructor, which will just
instantiate an empty subject with the ID of 0.
The genetic placeholder code will also be a string of 0s
because I want to prepare this place on the heap for
potential future modifications,
we will need this for some of the following lessons.
Finally,
let me also declare the printSubjectData function so that we can easily see
all that we need to know about it on the terminal screen.
I will first print out the subject's ID and then invoke the printGeneticCode
function to print the genetic sequence from the sample.
Now we can try this out.
Let's remove all of this code from the main function and create a
sheep subject with some random ID and genetic code.
And let's also print the information about this subject.
Okay, I'm going to compile this, and it seems that everything works as expected.
Hopefully, I thought of a good enough example to
make you see why RAII is important.
This also applies to using pointers for any kind of resource,
not just objects on the heap.
Maybe you need to access a file through the pointer, and you open a
connection to it in the constructor, but remember to always release
resources inside of the destructor.
Now that we defined this subject class,
we can continue expanding it in the following lessons.
And as you will see,
managing resources with pointers from inside of the
class is not as simple as it seems.