 Hey, everybody, it's Brian, and in this video, we're going to talk about QList. Now before we really dive in, I suspect some of you out there have worked with QList before because we are talking about version six of Qt. Guess what? There are some changes. There's always changes, but these are good changes. I promise I can summarize this really, really great article into one major heading, which is QVector and QList are now unified. There's a lot of other changes that we're going to cover, but basically what we're talking about here is there used to be two classes, QVector and QList. Now QVector is simply an alias for QList. That's right. There's only one class we need to cover. This used to be super, super confusing. If you don't believe me, just go into Qt Creator, look up QVector, and it says QVector is an alias for QList, and that's the extent of the documentation. There's nothing else to see here. Instead, go over to QList and then voila, there's all the docs you need. So it really simplifies the conversation. We used to have one video for QVector and one video for QList, and then the differences between the two and then different use cases, and we don't have to talk about that anymore because it's all the same thing. So rewinding a little bit. What is QList? It's a templated container, templated meaning you can put pretty much anything in it. QList itself is not a Q object. So there's no signals and slots, and you can copy QList all day long and have no problems. QList and QVector, as I've said, are the same thing. There's no more two gigabyte limit. This is amazing if you've ever hit that two gig limit and tried to figure out why. This is just absolutely outstanding. Everybody stand up, clap, throw your chair across the room, do whatever you need to do, rejoice. And of course QList will auto resize itself and it does this unapologetically. So if you've ever run into memory constraints, especially if you're starting to get up here, be aware that it will automatically manage its memory in the background. You don't have to worry about it. Speaking of memory, Q objects and Q pointers need to be cleaned up. Notice I differentiated between the two. A Q object cannot be copied. So as we've demonstrated in previous videos, putting a Q object through a template or a templated container usually ends up with you can't do it because you're trying to make a copy. So you have to work with a pointer. Pointers of course need to be cleaned up and we can do that either manually or using the Qt parent child relationship or using smart pointers. Let's dive in and take a look. So let's start at the beginning. We want to create a list and add some data. To do this, I want to demonstrate a few things. But the first thing we're going to do is demonstrate that you can create a list, put data in it and actually copy it. So I'm going to say Q list and this is a templated class. So we have to give it some sort of data type that we're going to be working with. And we're just going to say get numbers. Then of course we want to create a list and let's just simply call it list. Let's go ahead and make sure that because this function is going to return a list that we actually do return it. Just want to make sure. What we're demonstrating here is that we can do this copy and there's zero penalties here. Let's go ahead and work with this now. So how do we actually get the data into the list? Well, we can use the operator, for example, we can just say list and then we can just shift in 300, 400 and let's go ahead and put 500 in there. Makes it super convenient to put a bunch of values in rapid succession. Or if we wanted something a little bit more complex, let's just make this overly complex. I'm going to say int max equals. And you notice I have this queue random generator in our includes very, very simple class. We're just going to grab the global instance and I'm going to say bounded. And what this is going to do is give me the opportunity to do a low to a high. So I'm going to say between one, I almost said zero between one and 10. We want a random maximum number, which illustrates the other point. This is not an array. There's no hard upper limit. We can just keep plopping items in there until it won't go anymore. So we don't actually know how many items we're going to put in. We're going to let this just figured out through our random number generator. And then I'm going to say for int i equals zero, i is less than whatever random maximum we have. And then let's go ahead and increment i. And then we're going to append some say list dot append. And you see there's a bunch of different ways to do this. But we're just going to use this last little guy right here. It's going to figure it out for us automatically, of course, but I'm going to say let's go ahead and just shove a random number in there bounded and I'm going to do one to 100. So really, all that's going to do is just generate a list between one and 10 items and each item is going to be between one and 100. Really cool that we got that, but let's make a function that actually uses us now. So we're going to say void test numbers. And I'm going to try and keep this example extremely simple just because I'm trying not to mire the complexity of this video with the complexity that is Q list. Go ahead and copy that. So I'm going to say get numbers. So we're just calling that function and getting a copy of that list. Let's go ahead and print this out. So I'm going to say Q info. Before we get too much further, let's actually put this down here and demonstrate as we go. So, so far, all we're doing is getting the numbers and then printing it out. So there is our list and you can see we've got 300, 400, 500, and then we've got two random numbers in there. And if I kill this and rerun it, you see we've got more random numbers, 52, 99, and then more random numbers. And you can go on and on and on. It'll be random every single time. Now in case there's some math nerds out there, no, this is not, I believe it's not cryptographically random. So it's just a generalized random number generator. All right. Now what I want to do is demonstrate and I'm going to do a little bit of copy and paste action just to save a few microseconds here. There's a lot of things that exist in Qt for historical reason. For example, length, count, and size. And these used to be three completely different things. But if you highlight one and go length, same as size and count, well, then why did they exist? Because they used to do different things. So for example, size returns the number of items in the list. Is there, well, and it was introduced in 5.2 to overcome some hurdles they were currently having. Count is a little bit different because count you can actually say, hey, I want count with no parameters and it's going to give you the same as length and size or I want count with a specific number of items. Which will of course tell you how many instances, did I say that right, instances of an item there are in the actual list. So if you ever get super confused, just pick one and go with it. I tend to stick with length. But of course, size returns the number of items in the list. Length is the same thing as size and count. Which one should you use? It really doesn't matter. Honestly, most people are going to use size or length or one or the other depending on what you're used to and what your specific use case is. But under the hood, they're all pretty much doing the same thing. Now I'm going to say four and I equals zero. And then I want I is going to be less than the list dot and I'm just going to pick one. I'm going to say length and I'm going to say I plus plus. So from here, all we're going to do, I got that menu helping me out here. So I'm going to copy and paste Q info I our current index equals and then list at or you can do it kind of the C++ way just like access second array. So what's really the difference between at or doing at the old school way is at is a constant. Meaning that's read only, you're not going to be able to modify that. And we're going to demonstrate that later. Doesn't really affect us now because we're not trying to modify it. But if you want to read at will give you a constant and list will give you something you can actually modify. We're going to do this again, just for giggles here. I'm going to say Q info, I'm just going to break this up a little bit just to show what we can do. There's often a holy war about this and that's why I wanted to slow down and really, really demonstrate this. I'm going to do a for each and some people are automatically going to shout at their monitor and say, why are you doing a for each? This makes no sense. But this is the magic of a container. You can just say for each item in the container do something. And then you can just Q info it out in this example. Now which way is right, which way is wrong? Should you use for or for each? Well, it really doesn't matter. Let me just run and demonstrate. There's a lot of pros and cons to using each one, but what you can see here is the for gives you the actual index or the for each will not, you just get the item. So that's where you got to be a little bit careful. And yes, for is faster than for each, but on most modern computers, that speed is really not something you're going to notice unless you're doing something really, really intensive. So if you need the index to do the for, you don't need the index. You can always use the for each or the for. It doesn't really matter. Okay, prepare to be confused because if you're used to just arrays and not containers, this gets very, very confusing. I have a test function I made just for this little section. I've commented out the previous one. All we're doing is we're getting a Q list of integers from our get numbers function, which of course is just going to create a list and copy it for us. So just to avoid any confusion, this is basically what it's going to look like when we get it. Some randomized list of numbers, that's all it is. Let's go ahead and kill that. Now let's look at how we would want to modify this. So we can do what's called a direct mod or direct modification, which is saying list and then we need a position. I'm going to do the zero, which is the first item. And I'm going to set this to, let's go with 1000. So what we're really doing here is say, take that first one and modify it. If you try to do something like this, and I get a lot of emails saying, why won't this work at, well, notice it's a const reference. So this is not going to allow us to modify it. So if you try to do the zero equals 3000, you're going to get this ugly little message right here. Cannot assign a return value because the function at returns a const. Whenever you see const, it just simply means read only. That exists purely for speed. You see how we're using at in our for loop, which means it's faster because we don't have that modify. Now, when I say faster, it's not super much faster. It's still going to work. You're probably not going to notice a speed difference. But what we're able to do is directly modify the list at will not let you modify it. Just for giggles, run it one more time. You see the first one is now 1000. Pay special a note to that because we're going to move that around this list on accident. Now, when I say on accident, what I tend to see is developers will say, oh, well, the first one's a thousand and that'll never change. So I'm just going to say, always use the first one whenever they want the number 1000. Well, appending does not break that rule, but it does modify the list. So we're going to say list dot append and let's go ahead and append 99. And there's another way, which we've already covered, which you can just use the operator 2000 and 3000. Now, this is where new developers start to get a false sense of security because guess what? You're modifying this list. But the first item is still 1000 because append is just going to slap those on the very end here. Okay. That makes sense. Now comes the problem. When we start talking about inserting, inserting is, well, almost comical. Like if you've ever seen an episode of the Three Stooges when they, I think it's Mo or Larry, probably curly knowing my luck, but one of them goes in and just slams something on the table and everything else shifts around. So we're going to do just that. We're going to say list dot insert and we're going to insert at the zero position and we're going to insert the number 55. So what are we doing here? We're modifying our list, but we're putting it at the very beginning and we can specify the position. If we save and run, this is what's happening under the hood. You notice how 1000 is no longer at the zero position instead our 55 is. That's why you never, never, never hard code the index of what you're looking for. You're always going to do a search, which we're going to demonstrate later on in this video. And just to take this to an extreme, we can grab this and we can do something like one position. Let's put 22 and then in case you're wondering, yes, you can reuse the positions. This is going to keep modifying that list. I'm going to say 255 and just to take this to an extreme. Yes, you can do something like that. Why would you want to do that? I have no idea, but you could do it. And this is what it does in memory. It just simply keeps inserting and modifying our list. So our humble little list that was maxed at 10 items is now 20 items long and our 1000, which used to be at the zero index is now at position nine. So that gets a little bit scary. Now let's take a look at removing. And removing is not for the faint of the heart because this is where some memory management comes into play and Qt will do things unapologetically. So I'm going to do the copy paste. I'm going to print that link out. I'm going to say list dot. And if you say remove, you see there's a bunch of them. And this gets super scary. So I'm going to jump down to remove at. And I want to remove at a specific position. So we're going to just remove that first one. Let's grab this and copy paste. And we're just going to show the length of this shrink over time. Now, instead of remove at, I want to remove one. So what remove one does is it'll look at your list and go down till it finds the first occurrence of it. So I want to remove that first occurrence of 255. Bang, right here. Go ahead and modify this. Give it the good old copy paste here. And now I want to remove all. That's what I love about these. They're very intuitive. You can tell exactly what's going to happen before you even run it. I mean, we haven't run this thing yet, but you can tell exactly what's going on. Remove all is going to go through and remove all occurrences of whatever value you give it. Let's save and run and demonstrate before we go too much further. So sure enough, our list is shrinking. And let's go ahead and put that for loop at the very bottom. Just so we can see what's going to happen. So we can validate those 255s are no longer there. Sure enough, wham, they're gone. All right, let's say we want to get rid of 2000 here. This is a common mistake. I see people do this all the time, and it's super frustrating. So we're going to go ahead and we're going to say, and we're just going to say we didn't even bother to read the documentation. So we're going to try and remove, right? I want to point out before I run this, I'm even going to give it a build. There are no warnings. There are no error messages. It's just going to work. Or so you think. Notice how you get this assert failure. And then I or someone else who's out in the internet who knows how to use Q is going to get the, wait a minute, what is this index out of range mean? Why didn't it remove it? Why am I getting an error? Always highlight it, hit F1, you'll see what's going on. So this removes N elements from the list starting at position I. This is why this is so stinking confusing. Removes N, but N is the second one. N number of elements from the list, not the actual item starting at the position. Okay, so this wants, where are we starting and how many things are you going to wipe out of here? Not an actual value. So I want to start at let's say five and I want to wipe out, let's say up to a hundred. There isn't a hundred things in this list, but watch, bang, no compiler error, no warning, no nothing. It tries to run and then it just dies with index out of range. That's frustrating. So let's scope that down a little bit to seven. So from five to seven, we're going to wipe out some items. And now magically it works. Did you catch what's going on here? Look at the screen. Remove five to seven, zero, one, two, three, four, five, six. So it went to the fifth position, remove them and then modified the list around. But we didn't actually remove our 2000 like we tried to in the very beginning. To do that, we have to find the index of or search for it, which we're going to cover in the next section. But I wanted to highlight that remove will betray you. Time and time again, be very mindful of what that does. It's looking for a starting index and number. When in doubt, I lighten up one. So removes and elements starting at position. All right, as we've demonstrated, the list is modified, which means the index of the individual elements change. And to really hit that home, I've made a new function called test search, which has a Q list from our get numbers function. And we've got a special item called 999. And we're going to insert it at the first position and then append it. Now remember with append, we don't know what the index of the last item is. So let's just go ahead and print that list out and see what this looks like. So there it is at the first position, but then it's at the 10th position. And remember, this is a variable or random length from our function. So every time we run this, that different position, it was a 10. Now it's at 11. Now it's at 13 and so on and so on. So we can't really predict where that last one's going to be at. This is where searching comes into play. So let's go ahead and find the first occurrence of it. So I'm going to say int fpose equals, and I want our list dot index of. An index of is going to say, okay, what are you looking for? And I'm looking for our item. You can also of course say from where, which is a position. So you can start at a different position if you wanted to. We'll talk about that in a minute, but right now let's just find the first occurrence. And then of course, I want to actually print this out on the screen. So I'm just going to say qmfo, our first is that fpose. Now, if that wasn't easy enough, how do we get that last position? Remember we're appending, we don't know where that is. I'm going to call that lpose. This is last and is the last index of. So this is going to keep going through your list and look for the last occurrence of that item. Let's go ahead and print that out. Let's say run. And let's see what this looks like here. So first zero, last zero. Uh-oh, we have a problem in our code. Let's find out what's going on here. Ah, yes, very simple. I was the helpless victim of copying paste. Let's try that again. All right. So first zero, last five. Sure enough, it's right there. Let's run this a few more times. Real quick, just to prove that it is actually working. First zero, last at 11, it works. Now, that's great and all. But let's say we have multiple occurrences of this. For example, let's say I just go in here and I say insert at the, I'm just going to take a wild guess that this list is going to be big enough. That may cause problems if the random number generator really shafts us. But we're going to insert at the third position. So let's run this again real quick. Notice what's going on though. We have 999, 999, and of course 999 at the very end. But we only got the first and last. How do we get that middle guy here? How do we find him? There's a little trick I'd like to show you and it's actually not unique to me. This isn't something I invented, but let's find them all. So I'm going to say int pose for position equals. And we're going to do the list.index of. We're going to find the first occurrence here. We want to find the first occurrence of our item. If my keyboard would help me out here. You ever get that where like your keyboard is like slightly off from where you're expecting it and then your typing just gets all jacked up. That's what's going on there. So say Q info and let's go ahead and we want to know where it is at. Now remember we're already have done an index of so we should have some sort of position. Now I'm going to get the next position. And I'm going to say pose equals list.index of. And we want of course that item. And now we want to start searching at the position plus one. Now a do loop is just very dangerous. So we have to give it some sort of terminator here. So we're going to say while and we want to make sure that position is greater than negative one. So if you're a C++ newbie what's happening here is we're saying get the position display it out. Try to get the next position. But of course if it's negative one this is just not going to work. We could have done this differently and done a while loop and say while you know pose is greater than negative one. But I wanted to print this out at least one time. That's a confusing bit about programming is there's a billion different ways to do that. So let's go ahead and save and let's find every occurrence of that position or I should say that item. So we're at zero at three and at six. So zero three and six. And if we were to save run of course we are at zero three and 14. So zero three and 14. So this is working as expected. This is of course pretty fast but the more items you have the longer that's going to take. Now when we say longer we're talking usually milliseconds. If you start really grinding into multiple seconds then you've got some sort of issue that you could probably solve using a different model of some kind. Some say Q info. And what we're going to do here is see if this actually contains an instance. And I want to know if the list that actually contains the item. I already know that's going to return true but of course this is a bull just to demonstrate. Of course it's in there because I know we already added it but if you ever need it programmatically there it is. Just simply getting a bull whether it's in there or not. Now we can do something called guess it. We're going to slice this. Get a slice man that sounds great. I could go for a pizza right now. So cute list. So you're going to start hearing my stomach grumble if I don't stop talking about food. And I'm going to say items. And think of this well exactly like I don't know why I put another equal sign in there. Think of this exactly like a giant pizza in front of you. You want to get a slice of that. So I'm going to say list dot sliced. Now this is a little bit confusing here. What we need to do is look at this and we want the position and the number. So I'm going to get from the first position and I want two of them. And I'm going to say Q info just to kind of demonstrate here. And let's just print out our list so we can see the entire thing. And then let's go Q info. And I want the items or the slice that we just took here. So our Q list is 99. And all these values you see we got the first two items. So let's modify this a little bit here. And let's say I want to go from the first one. And I want to get up to four of them. Save run and see what that slice looks like. So sure enough it's 300 400 999 and 500. So that is how you get a slice or you guessed it a part of that list. So in case you wanted just a little subsection of it for a function or something like that. I'm going to admit it so far we've taken the easy route and we've been working with integers. And I'm not a big fan of doing things the easy way because the real world is often much more challenging. So let's talk about memory considerations. I've got this class here which of course is a Q object called test. And in this class we're just seeing the full object lifecycle of the constructor and the deconstructor. I just want to know when this is created and when it's destroyed because in C++ memory is like a real thing and we have to deal with that. So I'm going to demonstrate two different ways of managing memory. So I'm going to say void and let's call this test delete all. And I try super hard to make sure my keyboard's like right at that sweet spot where my spelling is just on point. But of course I can see I already missed something. So what we're going to do here is we're going to say test. And because we cannot copy a Q object we need to work with pointers. And let's call that list. So what do we have here? We have a Q list which has got a bunch of pointers to test classes. Okay. So to recap those are not the actual instances but pointers to the instances. All right so let's go ahead and say four int i equals zero to i is less than, I'm just going to pick a magic number five. What are we doing here? We're going to put some information in here. So I'm going to say list.append. And this is where you'd get into trouble if you were trying to copy. So if I just set something like test it's not going to work for multiple reasons. First it's not going to work because we're trying to copy a Q object but also because we haven't matched our template. So we would have multiple issues here if we tried to do that. Even though the ID is saying there's no issue we know better. So let's go ahead and put that pointer back in there. Now we have just created another issue. Memory management, there is no parent. So we've learned in Qt that when you're working with Qt object it has a parent child relationship tree which basically is Qt's really awesome way of doing a memory management for us but we're not using that. So now we have a list of pointers we now have to manage. Oh, that's not good, right? So you may be thinking I'm going to do some obnoxious for loop and we're going to call delete. No, there's a macro called QDeleteAll that does all of that for us. So you just give it a container and voila, it does it. The problem is with this it doesn't remove the items. So for example, if we call this function as is notice I'm asking for a length after we've done a delete all you're going to see that list is not zero. See, we have constructed, we call cleanup, we've deconstructed and we still have five items in that list. I'm maybe not a computer expert but I believe that's called a dangling pointer and if we try to access one of those things it's going to crash our application. That's not good. So instead we need to actually clear it out ourselves. From there, not only have we deleted all the memory but we've also cleaned all the items out of the list so we don't have those dangling pointers. See, zero works as expected. Now that is not a bad way of doing it but it's probably not the best way of doing it. So there are many, many billions of ways of doing things but we're going to work with smart pointers. So we're going to get rid of that, put that in there. So when I say smart pointers we are talking about well things that we've already covered in previous videos but we're going to talk about the Q shared pointer. So I'm going to say Q list and I'm going to actually type this out rather than copying pasting because I want you to understand the logic. I'm going to say Q shared pointer. Now, yes, before we continue, you can 100% use the standard library pointers but we're making a Q tutorial so I'm going to use the Q smart pointer class. But definitely if you want to use the standard library, use the standard library. That's the beauty of it, but Qt, they don't really force you into one thing or the other. Now notice the double arrows. This confuses people but basically we're using a templated class inside of a templated class so you have to have those double pointers. It's super confusing but if you're C++ newbie, just understand what you're doing. Template within a template. So our standard pointer list, there we go, not standard pointer, shared pointer list. You see what I mean by this, it's confusing fast. So we've got a Q list of shared pointers which is going to automatically manage that memory for us. Okay, let's go ahead and do a for loop here. And then let's go ahead, that almost got bad. I equals zero, I is less than five and then I increment. Now you may be asking yourself, what's the difference between using like a Q share pointer versus a standard library smart pointer? Really not much, to be brutally honest, for practicality reasons. There's a lot of little nuances under the hood, but if you're in Qt, you should probably use the standard library in Qt, you should probably prefer the Qt way if you're going to interrupt between Qt and other C++ libraries, use the standard library way. That's kind of my motto anyways. So we're going to make a Q shared pointer. Again, feel free to use whatever smart pointer you want and we're just going to make a new test. Now remember, no parent. So what are we doing here? There's no parent child relationship. We are going to use a shared pointer. So this is auto memory management, meaning we're trusting the computer to figure out what it's supposed to be doing. And then we're just going to append this and we're just going to append that item. Now, I want to do things a little bit different just to show you this is actually going to work. So we're going to remove the first item. So I'm going to say list.removefirst. And then I'm going to just go ahead and clear it. So I'm going to say Q info, which will just wipe out the rest of them. And then just for giggles here, I want to run out that length. Say run and let's see this in action. All right, so we've constructed a bunch of stuff. We've called remove first and you see that first one. You can tell by the memory was destroyed. Then we cleared the list and everything else got cleared as well. It just works. I love it. It's so simple. It's so beautiful. I hope you enjoyed this video. You can find the source code out on github.com. If you need additional help, myself and thousands of other developers are hanging out in the Voidrealms Facebook group. This is a large group with lots of developers and we talk about everything technology related, not just the technology that you just watched. And if you want official training, I do develop courses out on udemy.com. This is official classroom style training. If you go out there and the course you're looking for is just simply not there, drop me a note. I'm either working on it or I will actually develop it. I will put a link down below for all three of those. And as always, help me help you. Smash that like and subscribe button. The more popular these videos become, the more I'll create and publish out on YouTube. Thank you for watching.