 So first a few comments about objects and pointers and then we will spend the most of today's lecture on file system reading and writing files. So first of all you know we already looked at bunch of methods in objects but just to clarify things and set it down very clearly. So you can define methods as part of a class and each object of that class will thereby have a method of that same name except that those different objects will have different member values and in the method you will have this invisible, you know these methods are just like function, they have access to this invisible variable called this okay and you do not always have to say this arrow x or this arrow y. If x is not otherwise defined in the current scope then x by default will mean the x variables value in the current object. Now what goes for members also goes for method can access this arrow method arg as just method arg. So if method 1 calls method 2 without this qualifier it means invoke that method 2 in the same object okay. So this is always an implicit scoping mechanism that exists over and beyond local scope inside the function. Now calling method with arg on the current object versus calling it on that which is some other object they are totally different. Although it is the same method it is being invoked on two different object instances even if those instances belong to the same class or you know that p arrow method those are different things. And the general syntax is always the same. So if you have object type star object pointer then obviously star object pointer is something of type object type therefore you can invoke dot method on it like this with arguments that is syntactically equivalent to object pointer arrow method with arguments. That is the basic syntax requirements of object oriented program. So that is how members and methods work yeah no so that is assumed to be like an object whereas that p is the object pointer making of the convention whereas this is a pointer this is itself a pointer. So if you are upset with the similarity between this and that we can change it to other. The other is an object other p is an object pointer. So last time we talked about how we can build a binary search tree. We saw that you can insert these nodes with keys and the basic logic was that if you have to insert a new key into a tree or a subtree you compare the new key with the roots key. If the new key is smaller it goes into the left subtree if the new key is larger it goes to the right subtree. And just in case you are not there in the previous lecture so you the code so tree node is one node of the tree it has you know there is a global variable defined inside this class which defines what null is we already seen that. Every tree node has a key which happens to be a const it also has a left and right child pointer which are themselves tree nodes. The constructor takes a key and sets key to that value except it sets left and right to null as well. Insertion as we discussed you find the proper child either the left child or the right child depending on the comparison of the new key to be inserted with the key at this node. Sorry if we do not declare the static. Can the other members of the same class as to sit or not. So in this case it does not really matter what I am trying to do is to save space. So I do not want to store null in every instance. If you declared a static only one copy of null is stored and shared across all the objects but here they can access it. We will just make copies of null everywhere. So we find the correct child and then we if the child is null then child is changed to a new tree node with the new key otherwise you insert new key into the child recursively. So this is a recursive call. So observe that the recursion happens by changing the base object from myself to the proper child. That is a little different from calling factorial with a different argument here the implicit argument is on what object you are calling the method. So control is first transferred to root.insert and root.insert will then transfer it to its children.insert and so on that is how the recursion works. So you could write the code more simply by going like this. If the key to be inserted is less than this node's key then you do it on the left otherwise you do it on the right this entire code will be exactly replicated. In other applications you will be replicating a whole bunch of code. So it is just a small trick I wanted to show where instead of writing the code twice over once with left and once with right we write the code exactly once with child. What is child? Child has to be a reference to a tree node pointer. So it is not just a pointer it is not just a reference it is a reference to a point. And then how do you print the keys in order? We already said that the data structure has been defined to be recursive with ordering of keys. The root key is always larger than any key in the left subtree. The root key is strictly smaller than any key in the right subtree. So using that we can easily write a recursive routine for printing the whole tree. If the left is not null you have to print the left then print myself then if the right is not null print the right. So in the order left me right and by definition because this property of smaller than root larger than root holds at every node if this recursive routine will necessarily print the keys in increasing order by induction. So in main you basically say if root is not equal to null then print the root and then print a new line. So let's look at sample piece of code which will illustrate this nicely. So here is the insert code with that child trick and then print is exactly as I said. So first let me print out the keys just in increasing order. So in main what I will do is I will keep reading keys from the keyboard. I will assume the user will only enter distinct keys and then each key will be inserted into the root after the first one. In the first case if root is null then it will be changed to that one tree node having the key otherwise we will insert key into the root and after every insertion I will print the entire tree. So search tree goes into an infinite loop looking for new keys from the keyboard. So suppose I insert 10 so it means that there is only one number root in the tree which is 10. Next suppose I insert 5 which is 510. Next suppose I insert 15 so it is 510, 15 so I insert 20. So this is interesting because after every insertion I can give you the entire sorted order and not only that you can use this data structure to take the place of map in your event simulation. So given a tree rooted at the variable root how do you find the smallest key in the tree? Just keep walking down the left spine of the tree until you cannot go left anymore that is the smallest key. So you can therefore use this data structure to run your event simulation. Now I will also change the print out. I will use my small indent code from earlier so that instead of printing the keys just in order I will in fact for every node I will print the key with an indentation depending on how deep it is in the tree and I will also print a new line. So this will be interesting because effectively when I print this tree think of turning this tree on the side and reflecting it, subtree 1 will be printed first with indentation then the root will be printed to the left most point on one line then subtree 3 will be printed with an indentation. So that will show us the structure of the tree more clearly let me compile this again and then I will run this and provide the same key. So I give 10, 10 becomes the root. Now suppose I give 5, 5 becomes the left child of 10 because I am printing left first it comes upper so it is not just turned it is also reflected. Now if I give you 15 that becomes the right subtree of 10. Now the nesting can be seen. Now suppose I insert say 7 that will become the right subtree of 5. If I insert 2 that becomes the left subtree of 5. If I insert 11 that becomes the left subtree of 15. If I insert 20 that becomes the right subtree of 15. So this now in this particular case forms a balanced binary tree and printing with indentation shows you the tree structure root with 2 children 5 and 15, 5 with children 2 and 7, 15 with children 11 and 20. But because of in order traversal you also see the keys being printed in increasing order line after line. So this is one nice tree which is balanced suppose I told you to search for a particular key in this tree that is easy because you just like binary search you compare with the roots key if it matches you have found it otherwise depending on the ordering go down the left subtree or go down the right subtree. Now if the tree is balanced it means that every level you are doubling the number of leaves and therefore if the tree totally has n nodes then it depth is about log n and so within log n comparisons you will either find or not find your query. But there could be other bad trees such as I can insert 1, 2, 3, 4. If I insert in order then that leads to a tree which has degenerated into a single link list basically. One of the links is not being used left and right only one of them is being used. So in this case search will take linear time. So it is important that under any key insertion sequence whether you can keep the tree balanced that is a difficult problem that is studied in detail in data structures courses. Here is a easier problem but it is still not immediate some of you may see an answer but some of you may not. I am inserting keys here I may want to delete it how do I delete it? If a key is a leaf node then it is easy but what if I want to delete an internal node like a root node of the tree think about it how do I just delete an internal node while maintaining the property that left keys are less than root less than right keys that is no longer trivial. So that is a binary search tree demo and as I was saying last time perhaps you might want to add a parent pointer so far because we have left and right child pointers it is very easy to start high up in the tree and then walk down. But for some purposes it may be handy to walk up from the child to a parent as well for example you may want to find your sibling nodes. So we can introduce trivially a new field called up which is another tree pointer and I can initialize it while creating the tree node I know where the tree node is going to be inserted so I am going to add the parent pointer as an argument while creating the tree node. So it is very easy to extend the earlier code so that you get an up pointer and you can move up and down and roam around the tree as you wish. So two more comments about pointers one is what is a constant pointer versus pointer to a constant. Suppose you want the up pointer to be a constant because the tree is never changed once it is formed then you can declare this as tree node star const up. So observe the ordering of the const token. The const token comes closest to the variable name declaration of up. That means that the pointer cannot be changed the data pointed 2 by the pointer can be changed. Whereas if you want to have a pointer called up which points to a const tree node where the components of tree node can be changed then const has to appear bound to tree node so left of tree node that is const tree node star up these are not the same. But you could also be ultra conservative and say that both of them have to be const. So if you want to have a pointer itself nor can you change the contents of the pointer. So there will be two constants in that case. Okay so that is the next slide which is also coming back to the earlier question of what is the pointer and what is the reference. So suppose I have a equal to 3 b equal to 5. If I declared an in pointer which is in star px I cannot assign in star px equal to a because a is an integer px is an integer pointer. I can take an address of a by saying and a and then I can assign into px. At this point px is itself a variable whose contents is an address. So in the very next line I am perfectly free to say px equal to ampersand b. That changes the value of px from the address of a to the address of b. Observe that up to the third line the contents of the cells called a and b do not change at all. They continue to be 3 and 5. Only the address stored in px is changing from the address of a to the address of b. Now consider the fourth line int ampersand rx equal to a. This time rx is a reference to the variable earlier called a. So rx and a are now aliases. Rx is not a variable by itself. Rx has no independent storage for itself. Rx has no storage. So in the next line when you say rx equal to b that is not a counter part of px equal to ampersand b. In the last line when you say rx equal to b what happens? The cell called a the value 3 from there is overwritten with the value 5. That is the difference between pointers and references. Reference has no corporeal existence. Reference is a name aliases. A pointer is itself a variable which holds an address. You can change the address. Is that clear? It is very important to understand this. What happens when the recursion happens? I mean child points to insert of n key. Child points to insert of n key, right? So eventually as you walk down you will end up with a node whose correct child is null. Yes but observe that at the very first level if I am being able to call insert on a node then that node exists. That node itself is not null. So that node therefore has a key. And it has a left pointer and right pointer but the left pointer and right pointer may be null. If this node is near the bottom of the tree or at the bottom of the tree then one or more of left and right could be null. So when I have just started out with insert n key on this node then I can do the comparison. I can find whether the child should be left or child should be right. If the child is null then all I have to do is to create a new tree node which is a leaf and stitch it into the current node. So I say child equal to new tree node n key. If the child is not null then I have to walk further down. And therefore I can invoke child arrow insert n key. Where does the recursion end? The recursion ends when I ditch a child which is null, there I directly stitch it and quit. I do not call child.insert. I only call it if the child already exists. Another pointer and another pointer points to another pointer. Each pointer points to a tree node which has in it two more pointers. A tree node has a left and right pointer. The left pointer itself points to a tree node, the right pointer points to a tree node. But then also be null. So just like in a linear list in a queue element, a queue element contains one key or value and then it had a pointer to another key element. Here a tree node has a key and it has two pointers to the left and right subframe. Any questions about pointers? So quick summary of pointers before we move on. So you get a pointer as an address when you call new inside the systems library. That allocates to you a chunk of contiguous bytes from the heap. And it returns to you the starting address of that chunk. Now it's up to you how to manage that chunk. Your advice to walk outside that allocated area. You'll be basically trashing other parts of the heap which either you don't own or you'll be clobbering other parts of your data which you won't know. Just stay inside that chunk. Do whatever you want inside the chunk. This chunk is allocated on the heap. Whether you close scope or not and how the stack is behaving doesn't affect this allocation. This allocation is with you until you call a delete on that starting address of the chunk. Yeah, so what else could you have done? What we want is we actually want to see in the first case we want to change child. Look at the earlier code. This is clear. We need some anchor points. So I'm trying to insert the node at this node. If the key to be inserted is less, I go down the left sub tree. How do I go down? If the sub tree is already non-existent, then I just stitch in the new node there. That modifies left. So the problem is that here, what else could I do? If I didn't want to use left and right as different branches and repeat the same code, then one possibility you might think of is, I don't know, what else could you do? I mean, you can't have tree node start child. Why not? Then there will be a copy of the left or right. So when you change child, that would not change left or right. Whereas this code clearly says that I want to change left. This left equal to is the clue. This left is okay. I'm only accessing the value of left to go into its method. But that left is being overdue. If I didn't, if I just said tree node start child, that would be wrong because I would just make a copy of left or right. One of the addresses in the variable called child. And then I would be setting child, but I would not be setting left or right. That would be a bug. What does it identify in left or right or which is it going to change? Oh, by comparing the keys again. Here, I was basically saying if the new key is less than key, then I'd do the instruction on the left. We've given a condition and then it's left or right. What does it identify that left belongs to left or right? Oh, because as I told earlier. You always have, every object has this implicit variable called this. Any method is like a function except that they have access to this invariable variable called this. And you don't have to say this arrow x or this arrow y or this arrow left, this arrow right. Any member can be mentioned implicitly without this. Yeah. That's right. If you're not working on the current object, but you're working on some other object, yes, you need an explicit pointer in that. In our particular example, we're always working on the current node and therefore this is enough. So to give an example, I can write a piece of code to add up all the keys for all the nodes. So one way to write that would be outside the object itself. Suppose I say int sum, sum keys. And here I say tree node star ptn. If I want to write it in this format, then this is not a method. This is actually a function which explicitly takes an argument which is the tree node under which I want to find the sum. So how do I do this? I say if ptn is equal to null, then return 0. There is no tree. So the sum is 0. Else, what do I return? I return sum keys ptn arrow left plus ptn arrow key plus sum keys ptn arrow left plus ptn arrow right. Let me explain line by line. So the arrow basically says take the pointer called ptn, access that pointer to get a tree node and then access the left field of the tree node. So if you want, you can write this in the following syntax. You can say return sum key star ptn which is a tree node dot left plus ptn arrow key plus etc. So start ptn dot left is the same as ptn arrow left. Some keys. I want to sum up the keys, not one. I want the sum of all keys under this sub tree. Key would be any number. In case of the event simulation, key would be a time. So this is a case where there is no this because I am explicitly handing you a tree node pointer and ptn has to be the basis of everything you do. If you have to take the left pointer, you still have to say ptn arrow left. If you have to take the right pointer, you have to say ptn arrow right or you could declare it as a method. So just like print was a recursive method, you could define a sum without any arguments because now sum is being invoked on the current object. So here you would say return, so here there is no question of null because you cannot even invoke some on null. So here you would just say return sum left plus e plus sum right or rather it is a left arrow sum. So to find the sum at this node, find the sum of the at the left node, add it to my key and add that to right sum. So this is the object version of the same routine. So observe that there is less clutter, you do not have to keep around explicit this pointer. So what is going on here is implicitly there is a this being passed to you inside sum and everything is this. Actually everything is this arrow but you do not have to write it down. So whenever your recursion actually tracks the recursive structure of the data structure it is very easy to code this. This sum looks much cleaner than this one, it is much shorter, somewhat shorter. So here you have to actually test for null. Here you cannot even invoke some on a null pointer because you have to have a concrete object to be able to invoke some on it. So now you might have to say, so that is a good point, you cannot directly say this because left could be null. And if you try to invoke some on a null pointer you will get an exception. So you have to say something like if left is not equal to null then this otherwise zero and that kind of thing. It will become as long as it is. It will become as long as it is. Yeah, I agree. But there are two styles of doing it. One is the explicit pointers as arguments, the other is with this as the pointer. Okay, so that is pretty much all I have to say about pointer.