Placeholder Image

字幕列表 影片播放

  • there exists a place that you so secret that 1,000,000 thinking about it's guaranteed to upset somebody.

  • Embracing these ideas is so dangerous that simply suggesting you ever heard of them will cause immediate outrage on the Internet.

  • Joining me on a journey to this frightening location.

  • Join me as we wait.

  • Oh, hello.

  • I was just reading a story about processes from the eighties.

  • I don't think anybody is going to be interested in those.

  • If you like stories, I have a good story for you.

  • I've recently become the owner of a box, the boxes labeled Forbidden C++ on Within Lies the most heinous and evil forms of C plus plus code ever known.

  • Would you like to look inside?

  • Did my hand into the box and see what it yields?

  • Oh, yes, most evil.

  • What a good place to start.

  • Global variables.

  • I'm sure everybody watching this is quite familiar with variable.

  • There we go.

  • Variable.

  • A of type Interview on this variable exists within the scope of this main function.

  • The scope is defined by these curly brackets, and we should be familiar with the idea that nothing outside of this scope concede the variable a that have just created.

  • But what happens when we create a variable that isn't inside any scope?

  • Well, it becomes a global variable.

  • It is within all of the scopes within this file.

  • Now, for a simple one file programs such as this.

  • There's nothing wrong, a tall with that approach, and indeed it's quite convenient to operate this way.

  • And to all intents and purposes, it behaves like a regular horrible.

  • So here I am, out putting the value of that variable from the main function.

  • Global variables start to become more tricky when you've got multiple files in your project here.

  • I've added three very similar classes to my project on They all have a function called some method on that some method is going to print the global variable.

  • I'll include these at the top of my main program, but straightaway, we've a problem because these classes can't see.

  • My global variable on, the compiler warns, is a such So what happens if in the global name space here for all of the classes, I also declare my global variable.

  • Well, we still have a problem.

  • It's now saying there are multiple instances off this global variable and that's quite right.

  • So how do we bind them all together?

  • Instead, why don't we create a head of file called Global Header and declare our variable in that on include our global head of file in each class?

  • Well, that's not really changed anything.

  • There's still a redefinition on that makes sense, because hash include is effectively just couldn't paste the same bit of code into each class.

  • Head of file.

  • We've not really changed anything there, so I need to indicate to the compiler that I want this to be the same variable.

  • No matter where it's called on in my main program, I'm going to create an instance of each one of my classes and call the method.

  • Tell that to run it.

  • And here we see the result perhaps somewhat expected.

  • They all display 666 They've access that global variable under displaying its value, except that's incorrect because I didn't mention this.

  • But Class B changes the global variable value, so that's the first thing to worry about.

  • But secondly, even though we changed the global variables value when we output from Class C, its output, it's 666 again so clearly even though the compiler hasn't complained, the program isn't functioning in any way like I intended, so why don't we try throwing some of the buzzwords at it?

  • This is what people usually do when they're trying to make global variables work across files.

  • Now it's compiled it, saying It's got unresolved external.

  • So now I need to go and add on implementation for this variable.

  • Tell you what.

  • We may as well just do that here.

  • Okay, now it's just implementation.

  • Let's run it.

  • Oh, well, at least now it's behaving in a somewhat expected way, but this seems a little bit of a mess.

  • I don't like having to have this implementation somewhere in my code.

  • I wanted somewhere tied to where have declared it, because I could effectively put this anywhere, and I might put it in the wrong place.

  • Well, modern sea has a solution for that.

  • We can now declare this as being in line onset.

  • Its value compiles just fine.

  • Aunt behaves as expected, This in line keyword has sort of mutated over the last 20 years or so, But one way to think about it now is that when it sees a symbol like this is part of a compile unit.

  • Then it's the same across all of the Compile a ble units, so that's made implementing global variables a little easier.

  • But let's not just bypassed the big problem we have.

  • They're one of our classes, did something unexpected to the global variable, and that's affected the subsequent behavior of the program.

  • And that's the really big problem with global variables is nothing is protecting them, and nothing is guarding them on this.

  • Convicts squashing certain bugs very, very difficult indeed, particularly in large code basis, with lots of variables on many files.

  • As programmers, we should always aim to write functions where the end user can anticipate what the expected result is going to be.

  • On the whole, the c++ language allows us to do that very well because it has scope variable, so any variables inside a function somewhere can only really be changed by that function.

  • The fact that a function can change a global variable and therefore alter the state of the entire system without any warning or hint that it has done that to the user means that we get unpredictable behavior from call in that function so just be aware of these things when using global variables for simple applications where you're in full control.

  • Nothing wrong with them.

  • A tall but for multi file projects.

  • Firstly, there always a pain to get set up properly.

  • Secondly, they may not always behave the way you expect them to do.

  • And thirdly, they are accessible from absolutely anything and anywhere in your program.

  • Therefore, you have no guarantee what this state of the global variable will be after you've executed some code.

  • Well, let's not stop the shall we see what's next.

  • Oh, I hope you've got a strong stomach, Mac Rose.

  • Here is a deliberately simplified program.

  • It has a loop which it rates 20 times on outputs, the result of the max function between a random number drawn between zero and 10 on Fight.

  • So what we should expect to see is that the smallest number ever displayed is five.

  • Let's take a look.

  • Well, here are the results.

  • I've got five got a four and five and another four men and eight.

  • That's good.

  • 55550 And then a a one and then another five.

  • That's good too.

  • Okay, clearly not working, and this is because in a rather evil way, Max is not a function.

  • It's a macro that I've defined appear, and it's a fairly standard implementation off a maximum macro on just a little side point.

  • This is also considered really evil, for some reason, the turn every operator.

  • But hey, and so to all intents and purposes, it looks and feels like a function, and even in most cases it behaves correctly.

  • But the thing with Mac Rose is they are not executed at runtime there, purely a pre process er glorified cut copy and paste tool for the compiler, which undeniably can also be very, very powerful.

  • You just need to wield this power rather carefully.

  • So why was a result in correct macros are just a copy and paste tool.

  • So even though they can take in arguments in this case A and B, let's copy and paste this manually.

  • So what we see here?

  • Other two arguments that I supplied to the macro wherever we see the argument a we're going to copy and paste in the corresponding argument on will do the same for B.

  • Now we've inflated the macro.

  • Let's just tidy up this turner, a operator, so we can understand what decision it's making.

  • So this is the equivalent of saying if this is true, then returned.

  • Five ehlz return our first argument, and here we can see the problem.

  • The original test is fine, but one of the possible answers is to call the random function again when in reality, what that should be returning is the result of the argument we passed in in the first place.

  • Don't forget, this has happened at compile time.

  • So this code appear hasn't been executed at any point.

  • It's just a copy and paste tool.

  • So what can we do instead?

  • Well, my golden rule is that if you have something that behaves like a function, it should be a function.

  • In this case, I'm going to call the Standard Max Function house of the Standard Library.

  • I've access that they're the algorithm header.

  • This will now behave as we expect.

  • So let's take a look.

  • And here I've got 575595885 There no numbers less than five.

  • But don't feel you have to avoid macro is entirely.

  • Sometimes they offer quite interesting utility, so he was a macro that takes in a parameter s and then declares a variable called s on assigned it to the string value off s.

  • It turns out macros have quite a bit of syntax of their own.

  • And if you google it, you can find all of the different key words and symbols that too involved.

  • And it allows us to do rather obscure things like this.

  • And this might be another reason why people think they're evil is because he's starting to break all of the known conventions of the language on you're basically just making up your own here.

  • I've created a structure called test, but don't forget, this isn't executed at runtime.

  • It's executed at compile time.

  • So if I create an object my stroked t on.

  • I'm just going to go into the deep bugger here to look at what tea contains my highlight tea and expand on the window.

  • It's got four string type of variables, each initialized with the name of the variable.

  • Effectively Mac Rose.

  • Allow us to automate the construction of our own code.

  • They also allow you to play cool pranks on your coding buddies.

  • But be careful, not to abuse them, simply because it's easy to move the program away from what the programmer consensus ble expect on that will only lead to frustration on dhe difficulty debugging your programs.

  • I think we're getting to the point where we both want this to be over as soon as possible.

  • Let's get on to the next one.

  • Oh, that's just truly disgusting.

  • Go to I feel that poor old go to gets an unnecessarily hard time.

  • Its origins, lying programming languages that weren't a sophisticated is what we have today, and effectively, it's allows you to label parts of your program.

  • So here I've got part one and part two Onda.

  • At any point, I can jump to that part.

  • So in this case, if Y equals one, I'm going to jump to part one.

  • And if y equals two, I'm going to jump to part two.

  • So if I jumped apart to part one is never executed.

  • The primary problem would go to in C++ is that it will lead to spaghetti code.

  • Not it might.

  • It will on as your programs get more sophisticated.

  • If you start to lean on, go to too much, you're jumping backwards and forwards all over the place, and it can also lead to unexpected bugs and behavior to your program.

  • This label is invisible, so it doesnt partition any of the code within the region that is labeled as being distinct from any other code.

  • So in this case, if I jump to part one, I'll jump here on Execute X plus plus, but then I'll immediately execute X minus minus.

  • So to counteract that, I need to use yet another go to will have a part three to make sure that they skip Part two.

  • As you might expect, this example is deliberately simplified, but it does demonstrate a bit of an anti Patton where we're using go twos to implement subroutines.

  • We don't need to implement subroutines like this in a language which could natively handle subroutines in this code.

  • If we were to re factor it, toe have its intended purpose.

  • The obvious answer is to simply perform the operation we want when we need it and get rid of the goto is entirely the If construct provides enough functionality to make sure that we're not executing code, we shouldn't be executing, and it can be argued that if you're using, go to in your code.

  • You can always replace it with something a little bit more intelligent enhancing encapsulation, making the code clearer on reducing the possibility of bugs.

  • As I've just demonstrated, when you start using go twos, you tend to end up requiring Maur go twos.

  • And I believe it's probably safe to say that if you do find yourself using go twos, then you've some sort of design or logic problem with your algorithm.

  • That's not to say go twos.

  • Don't have some utility here.

  • I've added three nested four loops on.

  • There is an explicit condition inside the bottom of the nest that I want to capture on, then behave differently.

  • Perhaps I no longer need to carry on iterating through all of these.

  • For lips, this becomes a very big computational bottleneck in an algorithm.

  • Perhaps if I'm searching for something as soon as I found it within this three dimensional space, then I want to stop the search.

  • Now I could use brakes and continues to break out of the four loop in a more conventional way.

  • But in this instance, there's absolutely nothing wrong with using a go to to just break out of all of the loops at once and then carry on doing what I'm doing with the results.

  • And in my experience, it's situations like this where you do see the use of go to being used properly, particularly for error handling on in situations where you need to capture when all else fails, you can always go to a location in your algorithm, which can reset the algorithm to unknown state.

  • So don't use go to for subroutines.

  • But do you think about its use carefully when you need to capture exceptional circumstances?

  • The bravery you were showing is commendable.

  • Let's keep pressing forward.

  • Oh, well, I wasn't expecting this one.

  • Avoid stuff.

  • One of those standout features of the C++ language is it is a tight language.

  • Nothing exists in this system without having some sort of type associated with it.

  • We have into juice and floats, and we can define our own types in the forms of strokes and classes void.

  • Star is often found upon because fundamentally it removes the type from a pointer, and once you've removed that type, you can turn that data into anything you want.

  • So voiced our permits tremendous flexibility.

  • Let's consider this small example here.

  • I've got a struck to assume this is a very complicated strokes, but it's got some different types within it.

  • On.

  • I'm going to create a little utility function dump to file, which will simply dump the binary data in that stroked to a file.

  • So in my main program, I'm going to create an instance of my structure, and I'm going to call my daughter to file function notice here.

  • I'm passing in the address of the object, so I'm passing in a pointer.

  • I'm also passing in the size of the object.

  • We'll see why in a minute, very crudely, I'm going to create an output file stream in binary mode.

  • I'm going to invite to that stream, and then I'm going to close that street.

  • Notice that the argument is void star.

  • This means I really don't care what type A is.

  • All I care about is where it exists in memory.

  • And so as soon as I entered this function, A is just a number.

  • It is a memory address.

  • I know nothing about what it is pointing to.

  • So if I want to write it to the file.

  • I also have to provide the size of the object in bites because I don't know when to stop writing to the file.

  • I don't know what a is.

  • All I know is it's a starting location.

  • In order to make a compatible with the right function, I need to cast it to type chum.

  • And so this is quite a powerful thing, because I can use this one function to dump any kind of object in my program to a file.

  • If I did want to retrieve my original object back, I'd have to cast the void star pointer back into something sensible.

  • So here I'm performing the cast on Dhe because B isn't a pointer.

  • Actually need the value at the location after that cast, and there's my object.

  • Be so I've reconstructed my test structure from the Void star pointer that was passed into the function.

  • But I could quite legally also cast it to absolutely anything else, and it will be interpreted as such.

  • There be no sort of translation or clever manipulation of the memory.

  • Whatever memory exists at that void, Star location will now be read as a double on.

  • This is quite a useful technique when you're dealing with lower level programs where you don't necessarily need to care about the type of the information.

  • All you care about is where it starts in memory.

  • However, C++ is a typed language on it's good practice to try and keep things in their original type.

  • After all, this line here makes absolutely no sense at all, and I'll also add, fundamentally avoid star on its own.

  • Other than being a memory address is quite useless.

  • You'll always need to cast it to something else.

  • And as a programmer, you need to take care that you're casting it to something sensible or indeed the right thing.

  • So, as I mentioned before, quite useful for low level.

  • I owned things such as writing to a file, cause all I really care about the bites.

  • I don't care about the type, and so I can use this one function to handle any object in my sister.

  • A very powerful thing, but it's only a CZ good as the documentation on the programmer that's using it.

  • The compiler will see nothing wrong with this line, yet it's complete gobbledygook and could potentially lead to very difficult to track down bugs on That's really were the problem with Void Star begins.

  • It is really the case that we ever want to removed the type of an object in a program.

  • So let's look at an alternative method, which allow similar flexibility but does things in a slightly more modern way going to include the any library from the standard library and rewrite my function to support the any type coming in.

  • So any is effectively a modern c++.