 So, now let us so we will go back and forth between objects and pointers because they really go hand in hand it is impossible to teach one for long without the other. So, we will look at memory management. Now, again we will start with the basic types then go to native arrays and then get into objects. Now, so far the variable storage story has been like this. We have never used global variables, we do not even want to think about it, let us drop it from site. All variables we have used so far were allocated inside functions and they are passed either by reference or by value to other functions. Now, in case of non-collection variables like int, double, card the storage for those variables would always be taken from the stack. In case of collection variables like vector or list or string storage was taken partly from the stack and we will see the details of this gradually, but storage was mainly taken from the heap. So, to be technically correct here storage was taken from the stack and the heap and we will see exactly how, but we did not need to understand how or interfere with the process. Now, ok so life was good why do we need to mess it up by trying to understand memory management. So, the reason is that you may sometimes want a variable to outlive its scope, remember I gave this dangerous function which took a reference to an integer and passed it out. Now, that is dangerous because we did not learn how to do memory management or allocation yet, but it is a perfectly legitimate thing to want to have a variable outlive its scope. Because it exits the scope of a function and goes to another function it is put in data structures, ok. For example, the same customer can have a checking account and a fixed deposit in a bank. We do not want to make multiple copies of the customer record because if the customer's address of phone number changes in one place, we would have to go and change everywhere in all account types that the customer has an account in. So, that is bad what we like to store in the account databases is more like a pointer to a customer record. So, that from the fixed deposit table from the checking account table from the savings account table from the mutual fund table if it is the same customer it is the same pointer. And we should be able to get to the same customer record only one copy of it and change the phone number or the address or even the name whatever, right. So, therefore, if we wanted to write a function called makeCustomer, we might not want to return a customer from it, we may actually want to return a customer star or a customer pointer. Now, this pointer becomes some sort of a handle by which you are holding the customer and the handle gets moved around, the handle can be copied, but once you access the handle you get to exactly the same memory spots. So, for simplicity let us forget about customers and just start with int variables. So, how do you allocate an int that does not come from the stack? So, you do that by calling this new keyword called new, make me a new integer, what does that return? It does not return an integer, it returns your pointer to an integer and it is conventional in C++ to name pointer variables starting with p. So, p i points to an integer which you might think of as i, this new method call if you will. It is a C++ keyword, you cannot have a variable of that name. This tells the C++ runtime system to allocate 4 bytes from the heap segment to be used to store i or whatever you call as pi i, you know p i. This block of memory is not on your stack, it is in your heap and even if the scope ends this does not go away. So, now we are declaring an address and then declare a variable for it, I mean before we should we used to declare a variable and find out its address and now we are declaring an address and then. Yes, so new allocates an address for you with an extent of 4 bytes in this case and you give it a name called p i. So, again to make the type system very precise, p i is an address, star p i is an integer, p i is an address of an integer, star p i is that integer itself. So, after that the value of that cell whether you are writing it or reading it is called star p i. So, you can say star p i equal to 5 or you can say print p i as an address itself and print the value of p i plus 1 and finally, delete p i delete is again a new keyword in the language. You have to say delete p i not star p i this time, you are actually releasing the address that returns the 4 allocated bytes to the system. And the all important thing in coding with pointers and doing memory management is that if you have allocated the in some space you have to release it exactly once. There is this old multipython joke about how to lob a granade. So, granade explodes typically of 5 seconds after the pin is pulled. So, and the statement was you know if you throw it too early then it is possible that the enemy will pick up the granade and lob it back to you. So, you should typically count to 3 and then lob it at the enemy. And so, the dialogue goes you shall count up to 3, you will not count up to 2 neither shall you count up to 4, you will only count up to 3. So, similarly when deleting space you have deleted exactly once. If you if you delete it 0 times it means you have a memory leakage, you are continuing to allocate space from the system, but not giving it back. If you do that some very small number of times then that is probably fine the system will tolerate it. Eventually your process will end and the space will go back to the system anyway. But if you have a long running job and in some loop you have a small amount of leakage that will eventually skyrocket eventually you will occupy the entire memory without realizing where the storage is going and the system cannot allocate more legitimate requests for RAM. So, leakage is well you will be allocate or delete 0 times. If you try to delete more than once some C++ runtime systems are clever enough to catch it, some are not. In case you the system is not smart enough and you delete some storage twice you will typically destroy the logical structure of the heap and damage your processes memory and get either buggy results or crash your computer or at least your process. So, playing with memory management is a dangerous game, disciplined structured coding using constructors and destructors in the exact recommended way is the only way you can avoid memory management bugs. The problem with memory management bugs is that they are very hard to catch in most real world applications. Because you may be trashing memory at a certain point of execution and that may be detected long after the fact. And the heap is one noodle where all classes and all programmers code are interactive. So you may think that the bug is in one particular method, but the actual method which resulted in mangling the heap structure may be someone else's. So debugging heap code is very very difficult and there is a number of very expensive commercial tools which have been built to debug heap bugs. Even in the open source or free software regime there are multiple libraries I will post some of the names on model later which can help you debug memory allocation bugs, but nothing is a perfect fix. Defensive coding and avoiding bugs is the best way to deal with memory management. That's another reason why using constructors and destructors is so nice because you limit memory management operations only to those methods and nothing else. Typically you should not have memory management operations like new and delete outside constructors and destructors. So let's look at new and delete in action. So here's a very simple piece of code, same code as I showed, new int, I'm allocating only one int, I'm filling it with five, I'm printing out both the address itself and the contents of the address and then deleting it. So compile it, run it, no surprises. This is the particular address which was allocated to me and I fill it with five. If you don't fill it with five you get garbage, usual rules, fine. Now let's do something interesting which is, so I said that I want to outlive the scope of the variable, we're not really doing that here. So what do I mean by outliving the scope of the variable? So instead I'll do something like int star make int int val, this is an int maker. This will allocate an integer and fill it with the value val and return it. So this is how it will go, I will actually create the integer here, I'll fill it with val there and I'll return pi. So look at the type specifications carefully, make int does not return an integer, it returns an int pointer. So therefore because pi is also an int pointer, the compiler will be perfectly happy to return pi. In fact you shouldn't try to return star pi, that's wrong. Pi is what you want to return and the contents of pi was set to this val. Now inside here I'd say int star pa equals make int 78 and now I'll say pa and star pa, delete pa and again here maybe I'll print out the same thing here from the perspective of the function just before returning and from main I'll print out this message. So this time functionally the same thing should happen, make int when it calls new allocates this particular cell which is different from the previous one. It has value 78, main gets this same address as a return obviously and it also reads 78. Now observe that the value itself 78 survives the closure of that scope because the storage for pi is no longer in the stack, okay, pi is being allocated from the heap and I'm not sure that a small run with two or three variables will prove the point but you can try to write multiple allocations and see that they're not necessarily coming out in order or being deallocated in order. In fact if you don't do this delete pa the system is perfectly happy, it will not deallocate the storage until the process dies. So it's your job to delete pa exactly once. If I do a delete pa again immediately I don't think the sky will fall on our head but you'll have corrupted memory anyway, okay. You might after that start getting into weird problems that you don't understand. So let's try this out. A lot of things, bad things happen, backtrace, memory map, okay, so aborted, G plus plus are awfully unhappy, the runtime system. We understood that in this case, okay, double free or corruption because it was a very simple pattern and the double free happened quickly one time after the other. The runtime system understood what you were trying to do wrongly but this is not guaranteed by any means. If between the two doubles you have done a bunch of other memory operations it may very well be that G plus plus will not detect the problem and you will have corrupted your memory pool, yeah. Delete two times, once we did it pa is not defined anymore. That's a good point. What's happening is that pa is pointing into a bunch of memory. The state of that may have been updated to reflect that it has been freed back into the system. That's precisely how it detected that you are trying to do a double free. But see pa itself is an integer pointer that you pass, think of this as a function called delete on pa, pa is being passed by value, so the callers pa will not change. There's no way delete can change your pa, yeah. When we are doing it the second time, it isn't equivalent to doing a delete suppose x, y, z, something that's, when we are doing delete the second time, we are trying to delete something that doesn't exist at all. Well it always exists, so as a picture it's like this, right. So here is this amorphous expanse of RAM. What happened is the memory allocator allocated four bytes of it and pa was pointing to the first byte. Now I don't want to get into the technicalities but for every block that's allocated to you, there has to be some bookkeeping or management space, which tells the system how many bytes were allocated to you, etc, etc. That typically goes either at the beginning or the end, okay. So you have pa, let's say here is some management area that the system has allocated to keep track of the fact that you own that area, okay. When you say free, or sorry, delete pa, okay. Think of it as a function call with pa as argument passed by value. So now the system gets this call, it looks at pa, okay. Some of it figures out from this management space, all right. Actually what happens is it's before. Of course, the system doesn't know what the length is without reading some kind of a header information. So during new, when you say new int, what happens is the system somehow finds four consecutive bytes that are free. In fact more, this is its management area. It stamps the fact that you allocated four bytes after this and then it returns to you this, not that. This is a fixed size, fixed bookkeeping area. So when you say delete pa, it looks at pa, it walks back that fixed size. Now it has a management information handy. It knows how many bytes have been freed up and now it marks this as free. The next time you call delete pa, it can see that it's already free and that's how it's saying double free. But if in between you have done a whole bunch of memory operation, this space may have been reused by some other guy. You are free to hang on to pa, no one can change that. No, so there are sophisticated algorithms to say that if you allocated five integers one after the other, then you freed them up all. The system will join them back together into a big block. Four bytes space is freed up. Yes, and from time to time, if it finds a bunch of consecutive chunks like that, it joins it up and says there's a single free area of 200 bytes. At that time, do the space below that gets freed up? To the here, here. So typically when you say delete, it only marks the place as deleted. And once in a while it does an audit of whatever is free in the system and joins up all kinds of small chunks to make bigger chunks. It's always good to keep your free memory in big chunks, because then you can allocate big objects, yeah. A pointer which does not exist. Yeah. Is it a pilot-run error or will it be a runtime error? It depends. So the compiler doesn't have that. For example, I can easily do the following. So 0 is the address 0. So you can use an arbitrary address here. I can always say 0x, I don't know, 4, 5, 6, 7, 8. That's an arbitrary address. I can now delete it. And to add insert to integer, you can delete it twice without ever allocating it. The compiler should not really complicate. So it says you gave it an int and you're trying to convert the integer in point. But hey, you can cast it. Another compiler is perfectly healthy. So now we're taking an address into an arbitrary area of RAM. You have no idea what is there, and you're deleting it. Which is not initialized at all. A pointer which is not initialized. Suppose in the last line, delete a Px. Just that. Not even defined. Just like this. Then PA is undefined. Yeah, so the compiler will. Now the compiler will complain, because it doesn't know what PA is. As you know, the definition of PA, it will work fine. Let's see, even without initializer, it works. Oh, wow. So that's the danger with memory management in C++ and C-like language. So look, I have no idea what the value of PA will be here. And in spite of that, I am free to go in and delete it. This will go into the delete system. And now, so typically, when you buy or download one of these packages that help you debug memory problems, what the runtime system will also do is, apart from the necessary bookkeeping information in the header, it will put some standard patterns here, and it will also put some standard padding patterns at the end, so that you can identify the boundaries of an allocated object relatively easily. But hey, it could always have that pattern accidentally because of your other legitimate actions in the code. So it's not a foolproof way. The longer you make the padding patterns, the more random you make it, the less the chances that your code's bit patterns will exactly match it. But the chances never zero. So that's the summary of memory management that you have to be very, very careful. You have to somehow ensure, pretty much by design, that the allocations happen matched with allocations exactly once. And you should be careful not to pick up arbitrary addresses that were obtained without a new operation. You should only try to delete things that you obtained through new. And any bugs in the code may upset that also. Any questions so far? On the basics of new and delete. So of course, what applies to int here applies to doubles or anything else? Questions? Yeah, you can. The problem is once you release the object, then any other data structure, any other variable is free to be allocated over that. And? The only loss of declaration is close of scope for a variable. The variable name will be known as long as the scope is in effect. So the next building block we need is, instead of deleting, newing and deleting one item at a time, can we allocate arrays on the heap? Thus far, when you allocated arrays inside main, even that is supposed to come from the stack. But of course, very large arrays should come from the heap and not the stack. When vector allocates storage for itself, it allocates it on the heap. So here's an example, although here the size is very small. So some size of the array nn is equal to 5. Now we say the declaration, the type remains the same. It's int star pi. But the only new thing that happens is when you say new int, you give it the number of integers that are to be allocated in brackets. So this allocates in heap enough space for nn integers. So 4 times nn bytes. And now things feel sort of like an array. So don't look at the second construct here, only look at the top one. So that looks like array access. Now we can print out all the garbage values. No one has initialized them in this code yet. So those values are all garbage. But you can read them as pi box ix. Yes. While we declare vectors, vectors are initialized from heap, right? Vectors initialized from? Means vectors use the space from heap. Yes. But native arrays is inside a function dot. Yeah. That's why we get segmentation fault for vectors regularly. If we access outside vector. No, why do you get segmentation fault? When do you do that? Segmentation fault. When we access outside the bond of a vector. Wait, there are two things. Native arrays versus STL or standard C++ vectors. Which one do you mean? Boost vectors. Huh? Boost matrix and vector. So boost as well as the other vector, the standard template library STL vector. Those will probably do a check on the index to see if they are within bounds. And they may forcibly do, have you do a segmentation violation if the, it's outside bounds? Here, what we are doing is allocating array in the heap. On the heap, yes. Instead of that, we can declare vector also. It's the same function also. No, we'll come back to our own implementation of vector. You will then understand the connection between this and vector. We are not discussing vector. All we are doing is we are declaring native arrays, but instead of using just native array, we are saying new here. Therefore, it will come from the heap, not from the stack. So, we still say new, but we give an additional argument to say how many. And once you do that, it feels like a native array. And then finally, you have to return the space back by again doing a delete. But this time you have to signal to the system that you actually are deleting an array. And the monster to do that is to put a box bracket before the delete. So, the heap actually remembers the size. It's unfortunate that the heap will not tell you. So, again, here you have to hang on to that nn in doing whatever you are trying to do. So, in particular, once this new int nn happens, it's a very bad idea to change nn, because you'll be running all over the place. Again, it's only legal to access p i of 0 through nn minus 1. You go outside that, you go outside the region allocated for your array. This is particularly dangerous and insidious, because very often you get this index of by one bugs. And suppose you wanted to access p i of 0, but because of index calculation mistake, you get p i of minus 1. Well, you saw that picture. You'll be exactly overwriting the memory management information just before your allocated chunk, and that will trash the memory. Similarly, if you're off by one at the high end of the index, instead of going up to nn minus 1, if by accident you go up to nn or nn plus 1, you might be walking into the management data for some other memory chunk. So, heap management has been an area of research for many years. It's now pretty much stable. People sort of understand the best ways of doing it, but heap management is a complicated art. There are small objects, there are large objects, there are single variables, there are long arrays. So, before you know, your memory is all sliced and diced into tiny pieces, and although there is a total of 5 megabytes of memory available, you can't allocate even 1 megabyte of it, because it comes in very tiny chunks. So, a lot of interesting algorithms go into your runtime system to periodically fix that problem, try to coalesce multiple contiguous free regions into large regions and so on. The problem is with cnc++ like languages, the pointers are revealed to you as native memory addresses, and the memory manager has very little leeway to do a lot of intelligent memory compaction. A language like Java or C-Sharp that can do much better memory management, because you cannot depend on native memory addresses. There is a separate thing called a garbage collector and compactor, which runs alongside your program, but that's for other courses to discuss. So, the important thing in this code to understand is a few things. So, first of all, allocation has a size parameter, deallocation has to be given a box, so it understands it's not one element, but many elements, and the last thing is how you address. Now, when you say pi ix, internally, that's actually immediately translated into this internal form, start pi i plus ix. So, what this means is the contents of the cell, which is ix integers after the address pi, pi. So, pi is an integer pointer. When you say pi plus 3, ok, we have seen that already in the native array days, it is 3 times 4 bytes after pi. And start is an operator, which accesses the contents of memory. If pi is an integer address, then so is pi plus ix. You look at that address, and you inspect the cell address by that address. That is start pi plus ix. These two have exactly the same effect. So, that's how you allocate native arrays, but on the heap. So, now armed with all this, we can start to write our own vector class. Ok, now it's no big deal to write vector as provided. So, here is a specification. This is what we'd like to provide to our customers. Class vector, and let's say these are the public things you want to expose to the customer. The customer can create an empty vector. The customer can copy construct a vector from another vector. The customer can destroy a vector. The customer can insert at position p and integer v. And the meaning of that, we have to decide what is allowed and what is not. The customer can remove the integer, the value at position p. Let me actually say float, because then we can distinguish between the index type and the value type. And let's say I can get the floating value at position p without changing anything. Now generally it's nice to also be able to get the size of the vector currently. So, this is what we'd like to provide as our own vector class. Now pushback is a special case of insert. So, we won't bother with it. The only thing to discuss before freezing the design is what does insert mean? Suppose the current size of the vector is 10 and you try to insert at 20, what should happen? And there's a couple of possibilities. You can just say this is not allowed. Or you could say that between 10 and 19 will fill in some default values. It's one of the two. So, I'm sorry. Yes, vector will allocate memory from the heap. So, let's start writing this, then we'll see how it goes. Size is initially initialized for the vector. Size is initially 0. Say. So, we can keep on moving and insert the things even if it exceeds that. Even? I mean you said that if vector is initialized to size 10. Yes. Yeah, the understanding is that there is no a priori bound to the number of elements I can insert. Just like the vector classes we have used so far, vector of t. We want to support dynamic growing and shrinking of the vector. Okay. So, that's the next thing we'll write in this class. Now, typically what would happen is this specification would go into a header file. And that will implement it somewhere else. But for simplicity today, let me just implement it all in just a C++ file. So, this is the entire code, but we'll see. It doesn't do anything interesting. It just allocates five integers and we can use PI of IX both as LHS and RHS. Same deal. Okay. So, nothing particularly interesting. So, here is the vector class that we'll create. And we'll probably do the whole implementation inside this here itself because it's just a small example. Okay. Now, the vector also has some private fields and methods perhaps. What we need to implement the vector is a native array like float star PA. That's what stores the values. Okay. We also need to remember the capacity of the array as allocated and the size which is the logical number of items that the user has put in the vector. The invariant that we need to support is that capacity should always be greater than or equal to size. Otherwise, we're in trouble. So, those are the three things we need as internal state which the outside customer should not even see. And that's why we call them private. Now, what does the constructor vector do? Depending on what you feel like, you could either start off PA with a default small size or you could even start off PA equal to zero. Zero is called a null pointer because the address zero is usually not writable or readable legally. But if you don't feel like doing that, that's fine. We'll just say cap equal to size equal to zero. So, nothing has been allocated. No element has been inserted into the vector. So, both the capacity and the size are zeros. Now, what if I'm copying the vector from somewhere else? One approach would be to allocate PA as new float what? Let's say I don't want any extra storage, then I'll just say other dot size. I need to store that many floats in the system. So, that's what I store as new. Cap equal size equal to other dot size. The buffer of float size create has size cap, same as size. And now I say for int ix equals zero ix less than cap plus plus ix. PA ix, the ith element in my native buffer from the heap is equal to other dot get ix. So, there's already a get method provided in the other guy. So, how many of you are comfortable with the default constructor which initializes an empty vector? That's pretty easy. If I want to initialize my vector from another vector, I have to allocate space for as many floating point numbers as the size of the other guy. And then I have to fill it in. And I record both capacity and size as that other guy's size. Now, what do I do in the destructor? If capacity is greater than zero, if I have allocated something, then I do delete PA. You don't really do anything else. And this is where an important explanation is due about the interaction between the heap and the stack in creating objects. When inside main like this, if I say vector vec, ok, that vec will have private fields called cap and size and PA itself. Those will be on stack, but what PA points to will be on heap. Is that clear? Similarly, suppose vec has now, we have done a bunch of things on vec and you say vector vec2 equals vec. When you do that, the copy constructor will be invoked. The storage for vec2 will again be divided between stack and heap. Vec2's PA cap and size variables will again be on stack. But this time, vec2's PA will point to a different area of RAM compared to vec's PA, ok. So, look at this part of the code a little carefully. I'll pull this up so you can see the copy constructor first. And I'll need to scribble on the board because I need both of them on screen simultaneously. So, let's say here is vec1. Then you do a bunch of stuff on vec1 so it fills out and then you do a copy construction on vec2, ok. So, now what will happen is that, so here is the stack. When main starts up, I'm looking at the bottom of the stack. No call has been done yet. Vec1 has the three fields PA cap and size. Those will occupy some space on the stack and here is my heap which is best not drawn in a linear fashion. PA is basically pointing to a contiguous chunk of things of integers like this. When you now say vec2 equals vec, so this is what's known as vec. Another struct if you will will be allocated on the stack. This is the heap. This is the stack. Vec2 will have its own PA cap and size. These will be copied directly. If you see the copy constructor, cap equals size is other size, ok. Actually not cap. Cap and size will both be the other guy's size. Vec dot cap may well be bigger. So, size will be copied from the other guy's size. Cap will also be copied from other guy's size. But now this PA will actually go to a different area of RAM. So, vec2 dot PA will not be the same as vec dot PA because the copy constructor also does a new. So, whenever you have an object like this which has some variables inside the struct of the object itself, but it's also doing news, the new parts will come from heap and the other variables will remain on the stack. So, that's the copy constructor and we already done the default constructor. Now, the structure just looks at the capacity. If the allocated buffer has positive capacity, it's deleted. And if you want, just in case after that, you know, you'd like to probably set cap equal to size equal to 0. That's safe, but it doesn't really matter. Legally people destroying a vector shouldn't access it anymore. Now, how do we do these things? Size is of course pretty easy. We just return size, that's all. Now, the only things that are of interest, so even gate is very simple. Now, you might do some bound checking here. So, you could say if p is less than 0 or p is greater than equal to size, then you don't want to do something bad. Typically, what you do is you would say add a index p out of bounds or yeah, out of bounds. And now we can print 0 and size. And I will do something drastic like say exit minus 1. In case of bad memory accesses, it's much better to fail very quickly. Immediately at the point of mistake than wait long after. So, that's a get. But if the index is perfectly fine, then what do I do? I return p of p. P is already this buffer of floats. P is a legal index, so I can now return p of p. Now, all that remains is inserting and removing. So, suppose I again have to check that if p is less than 0 or p is greater than equal to what? So, to do push backs, you may well have p equal to size. So, now I have to say that it's only illegal if this is greater than size. Say, you may well have a 5 element array and you want to make the 5th or position 6th element. So, I will again print out a error message and quit if something is wrong. Otherwise, what do I do? This is the interesting situation. Now, the size of the array will increase by 1. But generally, it might be too wasteful to just allocate a array of size 1 more. So, very often what is done is you will grow the array by some fixed block size like 10 elements or something depending on the application statistics. But in this case, let's just play it cheap and say that we will just allocate an array of 1 more size. So, if it turns out that if size plus 1 is still less than equal to capacity, then I have space. Otherwise, I have to allocate. If size plus 1 is less than equal to capacity, then what do I do? I am inserting something at position p. Therefore, for int move equals p plus 1 move less than size plus plus move. This is not really correct. Move equal to size move greater than equal to greater than p minus minus move pA move equals pA move minus 1. Why is that? So, here is my array. Remember size points to 1 past the last element. And I am trying to stick in the new value into position p. So, move comes down from size to p plus 1 each time. And remember there is space here because I know capacity is large. So, there is storage here. It's just garbage value. So, what do I do? I copy pA of m equal to pA of m minus 1. I have to come down from the high end otherwise it will be wrong. Once I have done that then this variable is garbage. I copy that until I copy the pth variable into the p plus first variable. Thus creating a hole here. And now I set pA of p to be equal to the value v. What else do I have to do? I have to increase size by 1. But the invariant between size and cap is still satisfied. So, I do not have to allocate any new arrays. Otherwise what do I have to do? The old array is too small. I have to allocate a new array. So, let us say I say something like int new cap equals I do not know cap plus 10. Int new size equals size plus 1. Float star new pA equals if now really allocate a new one new cap. Now, what will you have to do? From 0 through p minus 1 we will have to copy the old values. From for copy equals 0 copy less than p copy or plus plus copy new pA copy equals pA copy. Now, we have new pA p equals value v that was given to me. And now we have for int c equals p c less than size plus plus c. How does this go? So, pA c plus 1 because I have already spent p storing v equals pA. So, it is somewhat important for you to understand the few cases here. I am inserting the value v at position p. If p is not a legal offset or index then I complain and quit. If I find that I have one more empty slot in my allocated buffer. So, what I have to do is to move elements from p plus from p through size minus 1 to p plus 1 through size. And then I push in the value v at position p and I increase size by 1. If on the other hand I do not have enough capacity then I have to allocate a new capacity array on the heap. I need to copy the initial prefix of it. So, insert v at the pth position then I have to copy the suffix. Copy prefix copy insert the pth element then copy the suffix. And finally, what do I have to do after all this cap? Well, so I have to free up the old space right. So, I say delete pA and then I do pA equals new pA. So, observe that I am making sure that before I lose the address pA I actually deleted exactly once. But, so this is already a buggy piece of code why is that? So, again you do that if cap is greater than 0 otherwise you do not. Otherwise you did not have anything allocated initially to start with right. Remember in the beginning I did not actually have pA being anything it is not legal ok. So, if cap equal to 0 then delete pA and then assign pA to the newly allocated address. And then do cap equals new cap size equals new size. So, let me draw some pictures to illustrate what this slightly spaghetti like code is doing. So, before trying the insert the story was this. I had a buffer called pA whose capacity was this much and which was filled up to size the remaining was junk. For some reason I found that size and capacity were the same. So, there is no place to stick in a new element. So, in fact my starting story was that this was pA and both size and capacity was 1 beyond the last element. So, there is no space. So, what I did is I allocated a larger buffer whose starting address was new pA with capacity called new cap in which I transferred this ditto up to that point called this the new size sorry this is also size. But actually what I did is suppose this is p ok here is the prefix here is the suffix. I copied it up to the prefix p minus 1 then I inserted the pth value v then I copied the suffix. So, it went 1 above. So, that is size plus 1 which is new size and then this is junk this is unused ok. So, that is the new state of the world after which I finished off by overwriting. First of all I have to free up this buffer and then I link up pA to point here. I say size is the new size and cap is the new cap. So, that is how I grow the storage required after I release the old storage and copy its value into some new block of memory that I have allocated ok. Now it is not too difficult to write out remove. So, you can do that. So, one thing you could do is if you remove you just reduce size, but do not release the memory at all. Or you could have a strategy like if size drops below 10 percent of capacity then actually allocate a smaller array and free up the bigger array. So, you can do kind of tricks like that ok. So, this is an example of then a simple vector implementation which is almost as good as the professional one. So, next time we will continue with more objects and pointers similar exercise.