The course is part of this learning path
In this course, we will explore the basic concepts and fundamentals of pointers in C++ and you'll learn how to use them to point to dynamically allocated memory.
- Learn the fundamentals of pointers
- Learn how to allocate memory dynamically and also how to return it
- Explore rectangle and circle classes and create instances of them dynamically
- Beginner coders, new to C++
- Developers looking to upskill by adding C++ to their CV
- College students and anyone studying C++
To get the most out of this course, you should have a basic understanding of the fundamentals of C++.
In the previous lecture, we learned how to work with dynamic memory when it comes to objects and arrays. We learned how to use the new operator to allocate objects on the heap and also how to return the memory that was allocated for those objects. In this lecture, because you might come across it or even use it. Especially in large or older code. It is important to understand the topic C++ of Const Correctness. That is how to use the const keyword correctly in different circumstances. Also, these are mostly important when dealing with passing pointers to functions. There are four scenarios that we will discuss: non-constant pointer to non-constant data, constant pointer to non-constant data, non-constant pointer to constant data, and finally, constant pointer to constant data. That seems like a lot. But it's really not that bad.
Let's create a project called ConstCorrectness and we'll take a look at all four scenarios. We'll discuss them as we go and see what happens. So, let's create a new project, empty project CostCorrectness. It creates. All right. So, we will create a main.cpp file, and let's give it the skeleton. Let's create the versions here of different functions that we will use. So the letters here, there's one with no constants at all. And then we have the cp2ncd and then ncp2cd, cp2cd. So, these are all different versions of the usages of const. So, the first one is the non-constant pointer to non-constant data. This one right here cp2ncd is constant pointer to non-constant data and so on. So and the others non-constant pointer to constant data. Constant pointer to constant data. Let's see what we got here. All right. And in this case, we're not passing anything as a parameter to a function but this could affect parameters as well. All right.
So, what are we going to do here? So, I guess from main I'm actually going to fill up main first because it's the least really super important as far as being memorable. So, we'll just call each of them. So basically, we've got the noConst and then some extra spacing. Then cp2ncd and see how some extra spacing. And then we're going to do the same thing. Non-constant pointer to constant data and some extra spacing and constant pointer to constant data. Extra spacing. And that's all main is. So, all the work is going to be done in all these nice little functions underneath. So, for the noConst this is going to be neither is a constant. So, that means both can be changed. So, little bit of note their comment. So, we'll say In noConst. All right. And we'll make a pointer here intPtr = new int(50) and then \t. And we'll say orig value. It looks like \torig value but it's \t
that means tab. We don't need an extra space between those two because it'll put the space occupied by a tab. So, there we go. If I change this pointer right here, we'll say changed data or change data. So pointer, oops, endl. And we of course have to delete the intPtr. Delete current dynamic int. And we're gonna let's say reuse the pointer down here = new int(125) and then we print it out new integer entirely. And of course, when we're done with that, we want to delete that as well. Remember we're not actually deleting the pointer. We're deleting the dynamic memory that's allocated or returning it. Delete may or may not be a good word for that. But that's what they chose. So, we'll say delete intPtr. Good. All right. So, that's good for that one. And the next one we've got constant pointer to non-constant data.
So, this one says the pointer is constant. That means cannot be changed. The data is not constant. So, that can be changed. You're so used to doing semicolons. All right. cp2ncd. int* const. Okay, talk about that in just a moment. All right. So, the syntax right here says int* const intPtr = new int(100). Now, the reason the const is after int* is because we want the pointer to be constant. Not the data. Now, that might be a little confusing. The way I do, it is after the identity before the identifiable if you go from right to left and read it that way. I don't mean like this towards left I don't mean like that right to left. But if you read the words right to left, you have const pointer to int is the way you can think of it as. So, it's a constant pointer to an integer and the integer says nothing about being constant. So, const pointer to an integral. All right.
So for this one, we'll say orig value *intPtr. Now, we can change the data. Can we change it? Yep, because the data is not constant. So, we're dereferencing the pointer and saying let me change the data, let me manipulate the data and we're allowed to do that. new integer value *intPtr endl. Good. So, we're allowed to change the data. Everything looks good to us. Now, what happens if we try to change the pointer? Normally, you would delete before you do this so don't get actually let's do that delete, and let's say we wanted to reuse it. So, let's try this. intPtr = new int (222). Let's see if that works. Something's wrong because it's underlining. It says the expression must be a modifiable lvalue. So, what in the world does that mean? Well, this is an lvalue. It's the left side of an equation.
So, we're setting or an assignment in this case the lvalue on the left side of the equality operator. The assignment operator rather. So, we are saying that intPtr is going to be a new integer which is 222. We're getting an error because the pointer itself is constant because it's a const pointer. That means we cannot change what the pointer points to as far as what memory address. If we do this and directly try to assign the pointer and we're getting a new memory address and we cannot do that because the pointer is constant. We can change the data it points to because the data is not constant. It's just a pointer to an integer. So this we will comment out that would be an ERROR. The pointer is constant. Now, the next one non-constant pointer to constant data. In this case, the pointer is not constant.
So it can be changed, but the data is constant. So, it cannot be changed. Let's see. So, we'll say In ncp2cd. All right. And then we'll say const int* intPtr = new int(500). Look how the syntax is different. So, instead of the const being after the asterisk, it's before the int and again you read from right to left. It's a pointer to an integer constant. See, makes sense? So, that's what we look at it as is just a pointer to an integer constant. So, it would be a constant integer. So, the data is what is constant. Awesome. So, now we'll say \torig value intPtr and then we want to delete intPtr. Now, what happens if we say inPtr = new int(100). That's actually okay. Because the pointer can actually be changed. The pointer can be changed because it's just a pointer. The data is an integer constant. We're
reading from right to left, so this is actually okay. Now, what happens if I try this though? What happens if I try to change the data? How do you change data through a pointer? Well, you dereference it. So, let's try that. That's interesting. It's not underlying the whole thing. It's underlying the asterisk and it gives us the same error as it did before, just a completely different cause it says 'expression must be a modifiable lvalue'. I can't change this data right here. I cannot change what it points to. So, I'm going to comment that out because that's not allowed, 'error data is constant' in that case. All right, so right here this is pretty restrictive, the pointer is constant and the data is constant. It's going to be interesting. So, what does that look like? Well, we know that the data is constant because it's an integer constant. But it's a pointer to an integer constant. But what kind of pointer is it? It's a const pointer. So yes, you do use it twice. It allows you to initialize it so, you don't have trouble with initialization. It's just you can't change it at all. So, inside cp2cd, orig value, we can get the data out of that we just can't modify it. So, no changes allowed. So, we should be getting some problems right down here.
Does that work? Nope, can't change the data because it says 'expression must be modifiable lvalue'. We can't change the data because it's an integer constant. So, that's a no-go. That's error, data is constant. Now, what about changing the pointer? Can I just point to a new int(1212)? The answer is no because again we get 'expression must be a modifiable lvalue' and the reason it's not modifiable is because the pointer is a const pointer. So, the whole thing const pointer to integer constant that means we cannot modify the pointer through this pointer and we cannot modify the data through the pointer. So, this is an error as well 'ERROR - the pointer is constant'. Now, that of course, we have to be able to do is return the memory. All right, the only thing we could really do with constant pointer to constant data is print out the stuff. We can't do any kind of modifications whatsoever.
So, let's run it just to see everything in action. Make sure we don't have any errors still and see what happens. So, in noConst original value is 50, changed the data to a 100, and then we get a new integer entirely, looks pretty good to me at the 125. And then in the cp2ncd original value was 100, when we printed that out, changed it to 250, and we're allowed to do that because even though the pointer can't be changed because it's a constant pointer, the data is non-constant constant. So, the data is just regular old data, you can change it. Non-constant pointer to constant data, we have the original value and we could have printed out the completely new pointer but in this case, we didn't. It's totally okay though but we do know that we didn't get an error here. So, that's good. Now, another thing we need to do here, there's something I forgot to do 'delete intPtr'. See, our program had a memory leak in it, we don't want that. So, we fix it.
Let's run it again, get the same situation since it's at the very end of the functions you'll notice, I'm not setting int pointer to null pointer because there's no way to use it after we're done here, we're not using it after. But, if you want to do that as practice, that's totally fine. Now, constant pointer to constant data, all we're doing is printing out original value. It never actually changes so we don't print out anything different. All right, pretty good. Pretty awesome.
Now, you can see that the least restrictive is clearly the non-constant pointer to constant data and the most restrictive is clearly the constant pointer to constant data. You can't change either the data or the pointer in that case. And of course, we have the situations in between where either the pointer or the data is constant.
Good work everyone. Before we move on I have a little challenge for you. I'd like you to continue with this project and write a function called noChange, not no const but noChange that takes a double parameter that doesn't allow either the value or the pointer to be changed. The function should do nothing but print out the value of the data pointed to by the parameter. Pass it in a double pointer from main and test it out. Make sure you delete the double before the end of main. So, pause the video and try to complete this challenge. Come back when you're done or if you want to see how I do it.
How'd that go for you? Were you able to write the function? Let's do it together. I'm going to separate it a little bit from these other ones just so we can see what's going on. We were told we needed a double but it needed to be so that the data is constant and also the pointer. So, we'll just say some value. I will copy this just to save myself some time. Go all the way down here. And let's see here, no change. And right here we're going to just print it out so some value and of course, we need to test this from main. So, inside main we can call or I guess we're going to create a double, don't we? So, let's create a double-pointer rather double* myDubzPtr= new double (5.25). You thought I was going to use 3.14 again, didn't you?
All right, so no change myDubzPtr and then we can delete myDubzPtr and if we want to just to be safe we can set it equal to null pointer. All right, so let's see here, we'll run it again just to make sure it works fine and we get 5.25. No exciting fanfare, like we did with the other ones, we just get a nice 5.25 at the end and everything's good, awesome. So, awesome job everyone. Hopefully, that does exactly what you thought it should. You might have noticed the variable I declared is not a constant in any way but the parameter is. So how does that work? I could change the variable in main with no problem. I could change its data or what it's pointing to. But inside the function, since the parameter is a constant pointer to a double constant, you cannot change either the pointer or the value inside the function. In the next lecture, you'll work on your first project for this section where you'll dynamically create rectangle objects. Let's get going.
John has a Ph.D. in Computer Science and is a professional software engineer and consultant, as well as a computer science university professor and department chair.