Hope you guys enjoyed this video! If you have any more C++ topics you'd like me to cover, let me know! ❤ Also don't forget that atm Brilliant has a 30 DAY FREE TRIAL, so visit brilliant.org/TheCherno and see everything they have to offer! The first 200 of you will get 20% off Brilliant’s annual premium subscription!
Dear Yan, as a 45 year old web developer, it's always a pleasure to watch you and your lectures. I watched your C++ series for elementary information to be able to develop games on Unreal Engine. I watch every video you post, even if I don't understand the content. Especially such concept videos keep our computer engineering knowledge alive, no matter what field you are programming. You are great! Good health to your work and your mind my dear teacher!
This is the kind of tutorials C++, and programming languages in general, lack and need! Project structure and technique is so important, but rarely ever taught.. Thanks cherno!
Please continue the C++ series, like advance concept introduced in C++20 and maybe it has upgraded further more, multithreading, design concept, UI using c++, Microservice using C++ or maybe webApp using C++. Thanks
“static” having a completely different meaning outside of classes is really confusing. Some people hate it so much they make an anonymous namespace around everything they want to stay internal. Since c++ 11 it does the exact same thing as ‘static’.
I started watching your C++ series when I was in college. Today I am a web developer and not written C++ from years. But I still like watching your videos and it teaches me how to do good programming. Your videos are fun. Keep it up Guy.
Okay i didn´t knew that if you specify a variable as "inline" inside a namespace, that it will only be created once. That is good know, because in the past, i always had trouble fighting with the "static" keyword in C++. So thanks for sharing! But nowadays i mostly use C99 and when i need global variables, i simply have them in one translation unit only, with the ability to make them "extern" if needed. All my libraries that i am writing are single-header-file style with full control how something gets compiled or linked, so by default it uses "extern" but can be overriden to "static" to make everything private in one translation unit only. Also i allow the creation and usage of libraries as well, so i always have dllexport and dllimport in mind as well.
Hey Cherno was browsing youtube a bit over lunch break and stumbled upon your channel. I was like damn why do i recognize this guys voice so much. Turns out i was following your videos of 3d game development religiously 11 years ago trying to get in to programming! Good to see you’re still going! Since then ive got a bachelors and a masters degree in software engineering! You played a big part of me getting into programming as a kid! Keep going❤
It's great how you explained that compiler error that jumps out when you forget the extern keyword or leave variable definition (not a declaration) in a header file. It was intimidating when I was starting the adventure ;) Now I'd say for a global variable I would either use extern, or... avoid it. Instead of using global variables in namespaces you can use static class members, then the class becomes the namespace for the variable. The syntax for the definitions is a little clunky, but I place all of those in a foldable region to get rid of them from my view. Another way to have some global variables is to put them into one struct, that then can reside either on the main stack frame or on the heap. I tested various approaches, like namespaces vs "static member only classes" and the classes won.
Classes that only contain static data members are really dumb and anti pattern in my opinion. That's exactly what a namespace is supposed to be used for. Use a namespace. Classes and structs and unions should only be used when we want them to contain at least a single non-static data member.
@@ohwow2074 I don't see a valid reason for it. My static classes work like singletons, on embedded. They have private functions and data (actually marked as protected to make it work). BTW, a class is a namespace too. I haven't found any conclusive and believable material claiming that it is an antipattern. I've seen some texts describing both approaches as preferred in certain scenarios. BTW, if it works, it's not dumb.
The inline and the extern keywords come at the same problem from two different directions. Just like Cherno says putting the definition in the header will cause multiple copies to be created (one in each OBJ), but the "inline" keyword tells the linker that the multiple copies should be considered as one and please throw out the extra definitions. The "extern" keywork on the other hand tells the compiler/linker "this is not a definition, just a notice that a definition is out there somewhere, we will find it at link time." Then you put the definition (the line without the "extern") in one CPP file. And you are done.
Have you done a video specifically on the different implications and use cases for inline? It's probably in your book of cpp stuff to cover lol Love your work, style and flow. Your videos are quick but not rushed, I really appreciate the pace. Thorough is best, luvs it.
I'm still immensely grateful to the author of the C teaching book I started out with. He introduce global variables pretty much at the end of the book, with ample warning on the downsides. As a result gloabls are still the last solution I will consider as a solution to a problem. I feel that in most cases my code is better for it.
I missed the part where he explains singleton or why you "shouldn't"* use global variables. ;) Because he ended on the inline solution it feels like it implies that's his recommendation. It is a solution for accessing a single instance/variable from multiple places, and not just a treatment of symptom to get rid of linker error like using static. But it has the problem that it gets rid of the safety rail of checking for name collisions. It's possible that you accidentally declare 2 global variables the same name that are meant to be separate but now they collide without any warning. Using extern declaration in the header and placing the definition into its own cpp file** seems like a better solution (but you won't have a header only library) There are also service locators and other ways to get around globals. *In embedded design static memory allocation and global variables are still very useful, but I'm sure they are not the best practice for large business software. **I wouldn't place them into a common global cpp, but into a Sprite.cpp... and any global Renderer objects into a Renderer.cpp... This way if both cpp have an int variable named 'count' that should be their own separate counter then the linker will yell at you. The inline will hide this collision. (It would complain if you had 2 different type variable have the same name though, like Sprite instance; and Renderer instance;.) Placing them in a common cpp is more error prone because you might not realize there should be more than 1 count (why is that there, who it belongs to)... for similar reasons you shouldn't have generic names for global variables, 'count' and similar should belong to the class, at least they should be in namespaces... most of the time the least globals you have the better.
Great video, I use the separate .cpp file with extern a lot. But I program a lot of embedded, so most objects I create need to be at a specific place in memory anyway. So it makes sense that an interface for example "owns" any registers that belong to it. Something like mathematical constants is placed in the main.cpp file anything module related would be placed in the header as you have shown in the video.
Another way to make sure that you have only one instance usin "extern" is like this: Have "extern Type var;" in header file, and define it in one cpp file using "Type var". The problem with this approach is that is mandatory to have a compilation unit with the definition at the moment you make a library or executable.
This is why we need modules. I can hardly wait, because that'll finally put C++ where a lot of other languages have been for decades and where it should have been from the start.
MSVC supports modules very well now. I recommend it. However don't rely on Intellisense much because while better than before, it still has some issues. I'm using them in a project I've been working on for about a year now and I love them. You can also make standard "headers" and there is an option in msvc that can import them like modules too. I haven't used it too much but that may help in cases of intellisense not working correctly.
Nah, that is why C++ should switch their improvement-model cause clearly the approach right now is just adding to the confusion. (and as if modules would help - nah, just look how they work together with macros - the same mess, if not worse)
@@ChrisCarlos64 Yeah, but MSVC is Windows only. I don't even use Windows. The last time I did I had access to VS6 and even then they had precompiled headers. A similar feature is available with gcc, but it's not particularly portable, if anyone cares about such things these days, but significantly more so than any other solution prior to modules. The reason I want modules is because they'd be a portable solution, I hope. I suppose portability is the only reason to want such a feature standardized anyway because most compiler suites have similar features now anyway.
If I want to use a global variable I always do that with the extern keyword. I declare the variable in a cpp file, and use the extern keyword in a header file. So I will reach that global variable where I include that header file.
One other way to use extern to achieve global variables, is to actually declare the variable "extern" in a header. And have a single CPP file define the variable (without extern). Similar to what you mentioned at the end, but slightly different. The unreal engine uses this pattern, I believe, for defining log categories and code-only-gameplay tags. But both of those are hidden behind a macro for declare/define (DECLARE_LOG_CATEGORY_EXTERN for headers, and DEFINE_LOG_CATEGORY for the cpp files)
hey cherno, just from a bit of experience, creating a lot of global structs independently might be less efficient than creating an extern struct array that you can then use enums with for lookups, the array thing works like a charm
Great videos. There are not very many C++ video developers out there, and your videos are direct and informative. Not like some of these other guys that put a bunch of flashy BS in their videos that just end up being distracting. We really appreciate your work. BTW, I would also recommend using const or constexpr when defining global variables that will never change.
The use of extern to reference a single entity outside of the other files that's how I've done it especially when the variable is coming from some other source like a library that needs to be visible to many functions.
I'm making an assumption here: First that your definition of various named sprites are intended to be const. In other words you don't want to allow changing one of your named sprites at run time. If this is the case, then something like constexpr may be a better choice. I generally advise to prefer not using global variables. If you do need something that is global then package it up in a class (call it an application class) and ask that class for the data and make accessing the data either for read or write a member function. I've probably wasted several years of time tracking down bugs due to indiscriminate use of global variables. Avoid them if you can, but make sure their use has a solid API so that they can't be abused. A good example of this is the use of std::cout. Put std::cout
I'm not a C++ programmer, but its inline keyword feels so confusing. It usually means the exact opposite thing - usually it copies(inlines) the given code into each file this code is called from.
It IS confusing, yeah. It should be a copy, and it does exactly that for functions, but not for variables. For variables, when the linker finds another instance of that variable it just skips it, because it was already linked. Whereas for functions it outright copies the entire function wherever it is used, so that it's faster (it doesn't have to jump to a new location in memory where the initial function is, then return where it was before, to run the rest of the code, it can just run the next code immediately. Better cache locality that way. Because instructions are also cached, not just variables.
inline is simply to avoid One Definition Rule (ODR) violations. It's no longer used to tell the compiler to inline functions (although it does changes the weights slightly).
In C++, inline also means that multiple definitions of the same function are permitted, so you can have a function defined in a header that's included in several sources. So it's in fact an alternative to extern, and useful for header-only libraries. In C++ 17 this functionality was added for variables, too.
thanks, i wanted to have global variables for simple things like window size since it is updated every frame (can stretch it and all) and didn't want to pass the pointer or value around. while i know people say to protect your pointer, i want to have a simple access to some variables globally.
To the defence of programmers using "static" (even if they may not know it), most of the time the compiler will just NOT store that variable in memory, but compile it embedded in the code as a constant. In fact the "static" keyword allows the compiler to do so. In your video, by printing its address or by adding side effects code in the constructor, you are essentially forcing the compiler to actually create instances in memory addresses. So basically your test is flawed for this simple reason. On the other hand, if you take some time to look at the actual assembly code generated by the compiler, and create a simpler version of a variable that does not create any side effect, you may find that in many cases, the use of static is actually not as bad or memory redundant as you think
Global vars in headers are just more work for the next programmer. Headers are for declaration. You can use sigleton or another translation unit that holds the vars and expose them with extern.
In my exp, it's pretty good to define a global variable in a single cpp file and never declare any global variable in header file. To get the global var in another cpp file, I used to create a global getter function.
I store certain things like filenames or resource accessors in arrays and then have an enum in the header file that gives the indices nice names. That works if theres a fixed number in the given category. I use unordered_map if I need to dynamically add or remove global objects of a certain type from memory.
Excelent points there. But I would disagree with your last point, where you basically hide a variable and access it somewhere else. This would make understanding what happens more complex than it needs to be. I personally like the class approach. Wherever you encounter it, its purpose is clear. It is a container of a category of items that are shared across. You can also add the const keyword to explicitly state in your header that those objects are not meant to be modified by others.
Yes and no. It prevents the content from being compiled more than once withing the same translation unit, but not across multiple translation units. As an example: x.h includes y.h and a.cpp includes both x.h and y.h. This is where the header guard protects the contents of y.h from being compiled twice within the same .cpp file. However, if you have a.cpp include y.h and b.cpp also include y.h, then of course both .cpp files will contain the header contents, and that's where the linker will later encounter duplicate declarations.
Hey, any updates for the raytracing series or is that officially over? Just wondering if it will continue or not :D Loving the content lately anyways tho! Keep at it
@@landrypierce9942 yeah I know, that's one of the reasons why I am so excited and eager for this series to continue. But it has been a while since last upload :(
You could as well declare the variable in the cpp file and declare the extern variable in the "Sprite" header, therefore all files which includes "Sprite.h" would have access to this global variable.
note that when writing really fast code you should propably avoid using globals and just pass around everything. if you are on clang or gcc you can enable __attribute__((const)) or __attribute__((pure)) to really put this to use, but MSVCs optimizer should also be able to take some use of not using globals.
"note that when writing really fast code you should propably avoid using globals" Nope. depending on the code, compiler and hardware they can be significantly faster even, but in general not slower (unless you are comparing to code that does not do the same work like initialising memory just in one case).
But what about the "#pragma once" instruction in the first line of your header file? Shouldn't that stop the same header from being included (and thereby its global variables declared/initialized) over and over again?
I had got a lot of linker errors when I had functions in the header file. I had to use inline to fix those. Though I didn't notice till I included my header only library into a program with more than one .cpp file heh.
I constantly learn new meanings of the keyword inline, when will this end??? 😂 I think I am at the 4th or 5th different use case now?? How can a keyword be so overloaded? 😂
This is causing me to lose my mind somewhat. When I think of inline, I expect almost the exact opposite behavior, like if I write an inline function, I would expect (basically) a new copy of that function's assembly to be made at every point where it's called, instead of having the assembly defined in one place with jumps at the call sites. But apparently for variables it makes sure it's only defined in one place? Why did they use that keyword? This is why I use rust, oh my god.
For functions it behaves like that, it creates a copy in place of wherever it is used. Specifically, a copy of the function's body, everything between the "{" and "}", using the two "{" and "}" as a scope guard for everything inside it, so that any potentially local variables inside those brackets die when the code execution hits the closing "}" bracket. But for variables it does not. Confusing, right? You can't just plop a copy of the same variable everywhere. If you wanted to do something like that, you would use a macro (a #define), which is basically just a dumb text editing copy-paste of the value of that "variable", done by the pre-processor ("processor" being the compiler), so that text editing replacement occurs before the compiler even begins going through it (processing it). The "inline" keyword for variables behaves differently. Even MORE differently for C++17 (and newer) when defining them in a struct/class. But at its core, it basically ignores any future use of the same variable definition and just uses the first one it finds. Not a copy AT ALL. And this is confusing.
For functions, inline also has the same effect of allowing the same function to be defined in multiple translation units (the definition must be the same, or it's undefined behaviour), but the linker will only pick one. In C++ "inline" and "extern inline" are equivalents, for both variables and functions.
can you please make a tutorial on what is make, cmake, makeLists ? and how to write code for that. this will clear the confusion what is happening behind the scenes. at work i was assigned a task, that was on Linux. i somehow did that, but don't know how i got my setup working. only confusion i have is, how to setup build system. how to link other projects against my project. how to add Libraries.
30 years ago C++ was not even standardized. It's a vastly different languages these days. You have to learn it from scratch. Even the hello world program has almost completely changed in C++23.
@@ohwow2074 I made windows programs in 3.12 it consisted of huge case statements to decode windows messages. Hello world typed in from scratch literally took a day. Great fun though and lots a blue screens.
For global variables, I would suggest one writer, multiple reader. The value must copied but not moved if we are talking about multiple readers. if we only use one writer and one reader, then the value moves. we must not have multple global variables, even with mutex locks, this will complicate stuffs.
use static variables inside a class instead. singleton has a very different use than using it as a global variables alternative, but yes its the same thing you can use singleton as an alternative to global variables
Dont use static member variables. They break when doing things like loading runtime modules, as each linked module will have its own instance. Instead, do this; define the variable in the cpp file, inside the local namespace for that file (you just do namespace { … } at the top and put your statics there. If you need them to be public, simply declare static getters in the class!
DO you mean inline static members? Because you can do static member and define it in a cpp.. i think the problem is define the static variable in a haeader file without inline.
@@Mateus.007 no i dont mean that. You dont want a header declared variable at all, because if you do, and you have multiple modules involved (especially runtime loaded dlls for example) then each module would have its own instance, since static variables rely on linktime operations. If you have a file local namespace instead it will just work, and only the module that compiled it will have the instance, but it can be shared with other modules fine via the getters and setters (even nonconst reference getters only!)
@@Mateus.007 I believe so. Its in the header, which is shared across modules, however for it to work across runtime modules only a single translation unit in a single module need to store the data. So I dont see how inline static would change that. Someone correct me if I'm wrong, because im not 100% on inline.
@@perkele1989 when you say modules you mean translation units, right? From what i understand inline makes the compiler to share the same variable across multiple translation units, as a substitute to the extern + definition in cpp file technique.
The same thing happened to me once, I started the DirectX API in a .cpp file and in the other I tried to access it, because it was static I didn't know at the time that these variables weren't shared and when I tried to debug it always gave an error when I tried to access, because the pointers were null. After a long time I found out that either I did everything in the same .cpp or put it inside classes. My question is how does this apply to the singleton pattern? About static and return self static instance
The singleton pattern is different because a static member variable doesn't belong to a particular instance of a class/struct. It's shared between all instances of the same class. So when you create a static Object* obj = nullptr; inside a class Object this obj member is available to all instances of Object (and it's just null at this time) That's why everytime you call a method to retrieve this single instance you assign it to "this" at the first time (the first time being when obj is still null) and every other instantiation receives the obj pointer directly
My question is even in case of making the static sprit, it won't take the memory space in any one of file, since static variable don't take any memory space, correct me if I am wrong, I am beginner , Thanks
Static variables do take memory space. And in fact if you put it in a header file and include it in say 5 cpp files, your program will actually have 5 distinct copies of that static variable at runtime so you'll essentially waste memory. That's why he used the inline keyword there.
Unlike what you say: 'inline' is *not* a magical keyword that does multiple things. That's what's wrong with 'static'. 'inline' does the same thing everywhere: it instructs the compiler and linker to merge all definitions of objects annotated with it if they have the same name. This is exactly the same when allowed to functions vs variables. It's just new to allow this for variables. But just like 'static' the name is poorly chosen because it doesn't describe what it does, just what it was first used for. This causes a lot of unfortunate and unnecessary confusion
Small request, could you make your face camera like twice as small? It doesn't need to take that much space and I'm sure most people come here for information and not to stare into your beutiful eyes Cherno! Thanks!
why cpp keep reusing the same keyword for different purpuse, like the static, inline (here). why NOT simply crete a new keyword such as GlobalForMultipleCpp in this case??
Now I am surprised... Because with the pragma once, even included in multiple files, in the end there should just be only one instance... But there's more... Why C(++) why?!
I would like to disagree with you that C is not a object oriented programming language, in the sense of classes and methods I would however agree with you. Also you can do OOP in C but its more manual and requires function pointers, it would look more like python where you pass the object to it self also.
It's not just wasteful to have multiple copies, it also wouldn't work as soon as you try to modify the global variable, because you are just modifying one copy of it that the other cpp files can't access.
Hope you guys enjoyed this video! If you have any more C++ topics you'd like me to cover, let me know! ❤
Also don't forget that atm Brilliant has a 30 DAY FREE TRIAL, so visit brilliant.org/TheCherno and see everything they have to offer! The first 200 of you will get 20% off Brilliant’s annual premium subscription!
At this rate, you should start a c++ dictionary series, explaining each keyword in all of its contexts and semantics in full detail ;)
Please get into the templates rabbit hole :D
can you do a video explaining in depth the inline keyword?
@@PANCHO7532 i think he already has one 🤔 im not completely sure but its worth checking out
Yes Please, PLEASE! Do a video on Extern as well thank you~ 🙏🙏
Dear Yan, as a 45 year old web developer, it's always a pleasure to watch you and your lectures. I watched your C++ series for elementary information to be able to develop games on Unreal Engine. I watch every video you post, even if I don't understand the content. Especially such concept videos keep our computer engineering knowledge alive, no matter what field you are programming. You are great! Good health to your work and your mind my dear teacher!
There is also one big problem with keyword "static". Since we have 2 or more copies, modifying one will not modify the other copies
This is the kind of tutorials C++, and programming languages in general, lack and need! Project structure and technique is so important, but rarely ever taught..
Thanks cherno!
Please continue the C++ series, like advance concept introduced in C++20 and maybe it has upgraded further more, multithreading, design concept, UI using c++, Microservice using C++ or maybe webApp using C++. Thanks
“static” having a completely different meaning outside of classes is really confusing. Some people hate it so much they make an anonymous namespace around everything they want to stay internal. Since c++ 11 it does the exact same thing as ‘static’.
Not just some people - many.
And i would say rightfully so. having the same keyword behaving wildly different in multiple ways is just bad.
I started watching your C++ series when I was in college. Today I am a web developer and not written C++ from years. But I still like watching your videos and it teaches me how to do good programming.
Your videos are fun.
Keep it up Guy.
Okay i didn´t knew that if you specify a variable as "inline" inside a namespace, that it will only be created once. That is good know, because in the past, i always had trouble fighting with the "static" keyword in C++. So thanks for sharing! But nowadays i mostly use C99 and when i need global variables, i simply have them in one translation unit only, with the ability to make them "extern" if needed. All my libraries that i am writing are single-header-file style with full control how something gets compiled or linked, so by default it uses "extern" but can be overriden to "static" to make everything private in one translation unit only. Also i allow the creation and usage of libraries as well, so i always have dllexport and dllimport in mind as well.
Hey Cherno was browsing youtube a bit over lunch break and stumbled upon your channel. I was like damn why do i recognize this guys voice so much. Turns out i was following your videos of 3d game development religiously 11 years ago trying to get in to programming! Good to see you’re still going! Since then ive got a bachelors and a masters degree in software engineering! You played a big part of me getting into programming as a kid! Keep going❤
Check out his game engine Hazel ;-)
Short and sweet, packed with 'Food for though', I love this guy! Great teacher!, thank you Cherno!
Definitely wanna see a video all about the inline keyword :D Or does that exist already?
It's great how you explained that compiler error that jumps out when you forget the extern keyword or leave variable definition (not a declaration) in a header file. It was intimidating when I was starting the adventure ;) Now I'd say for a global variable I would either use extern, or... avoid it. Instead of using global variables in namespaces you can use static class members, then the class becomes the namespace for the variable. The syntax for the definitions is a little clunky, but I place all of those in a foldable region to get rid of them from my view. Another way to have some global variables is to put them into one struct, that then can reside either on the main stack frame or on the heap. I tested various approaches, like namespaces vs "static member only classes" and the classes won.
Use extern or member and define it in a separate cpp file. Or just use inline keyword.
Classes that only contain static data members are really dumb and anti pattern in my opinion. That's exactly what a namespace is supposed to be used for. Use a namespace. Classes and structs and unions should only be used when we want them to contain at least a single non-static data member.
@@ohwow2074 I don't see a valid reason for it. My static classes work like singletons, on embedded. They have private functions and data (actually marked as protected to make it work). BTW, a class is a namespace too. I haven't found any conclusive and believable material claiming that it is an antipattern. I've seen some texts describing both approaches as preferred in certain scenarios. BTW, if it works, it's not dumb.
The inline and the extern keywords come at the same problem from two different directions. Just like Cherno says putting the definition in the header will cause multiple copies to be created (one in each OBJ), but the "inline" keyword tells the linker that the multiple copies should be considered as one and please throw out the extra definitions.
The "extern" keywork on the other hand tells the compiler/linker "this is not a definition, just a notice that a definition is out there somewhere, we will find it at link time." Then you put the definition (the line without the "extern") in one CPP file. And you are done.
New cherno video 🎉🎉
This is my favorite youtube channel. Thanks, Cherno, you're awesome!
Have you done a video specifically on the different implications and use cases for inline? It's probably in your book of cpp stuff to cover lol
Love your work, style and flow. Your videos are quick but not rushed, I really appreciate the pace. Thorough is best, luvs it.
I'm still immensely grateful to the author of the C teaching book I started out with. He introduce global variables pretty much at the end of the book, with ample warning on the downsides. As a result gloabls are still the last solution I will consider as a solution to a problem. I feel that in most cases my code is better for it.
I just love this channel and your teaching methods!
I didn't know about "inline" keyword for global vars. Thanks for new knowledge 😉
I missed the part where he explains singleton or why you "shouldn't"* use global variables. ;)
Because he ended on the inline solution it feels like it implies that's his recommendation.
It is a solution for accessing a single instance/variable from multiple places, and not just a treatment of symptom to get rid of linker error like using static.
But it has the problem that it gets rid of the safety rail of checking for name collisions. It's possible that you accidentally declare 2 global variables the same name that are meant to be separate but now they collide without any warning.
Using extern declaration in the header and placing the definition into its own cpp file** seems like a better solution (but you won't have a header only library)
There are also service locators and other ways to get around globals.
*In embedded design static memory allocation and global variables are still very useful, but I'm sure they are not the best practice for large business software.
**I wouldn't place them into a common global cpp, but into a Sprite.cpp... and any global Renderer objects into a Renderer.cpp...
This way if both cpp have an int variable named 'count' that should be their own separate counter then the linker will yell at you. The inline will hide this collision. (It would complain if you had 2 different type variable have the same name though, like Sprite instance; and Renderer instance;.) Placing them in a common cpp is more error prone because you might not realize there should be more than 1 count (why is that there, who it belongs to)... for similar reasons you shouldn't have generic names for global variables, 'count' and similar should belong to the class, at least they should be in namespaces... most of the time the least globals you have the better.
Great video. I'd really like to see the example where you use the "extern" keyword, as you mention in the end of the video.
Great video, I use the separate .cpp file with extern a lot. But I program a lot of embedded, so most objects I create need to be at a specific place in memory anyway. So it makes sense that an interface for example "owns" any registers that belong to it. Something like mathematical constants is placed in the main.cpp file anything module related would be placed in the header as you have shown in the video.
Another way to make sure that you have only one instance usin "extern" is like this: Have "extern Type var;" in header file, and define it in one cpp file using "Type var". The problem with this approach is that is mandatory to have a compilation unit with the definition at the moment you make a library or executable.
he mentioned this at the end of the video
Im very new to C++ and you are a godsend
This is why we need modules. I can hardly wait, because that'll finally put C++ where a lot of other languages have been for decades and where it should have been from the start.
MSVC supports modules very well now. I recommend it. However don't rely on Intellisense much because while better than before, it still has some issues.
I'm using them in a project I've been working on for about a year now and I love them. You can also make standard "headers" and there is an option in msvc that can import them like modules too. I haven't used it too much but that may help in cases of intellisense not working correctly.
Nah, that is why C++ should switch their improvement-model cause clearly the approach right now is just adding to the confusion.
(and as if modules would help - nah, just look how they work together with macros - the same mess, if not worse)
@@ChrisCarlos64 Yeah, but MSVC is Windows only. I don't even use Windows. The last time I did I had access to VS6 and even then they had precompiled headers. A similar feature is available with gcc, but it's not particularly portable, if anyone cares about such things these days, but significantly more so than any other solution prior to modules. The reason I want modules is because they'd be a portable solution, I hope. I suppose portability is the only reason to want such a feature standardized anyway because most compiler suites have similar features now anyway.
If I want to use a global variable I always do that with the extern keyword. I declare the variable in a cpp file, and use the extern keyword in a header file. So I will reach that global variable where I include that header file.
One other way to use extern to achieve global variables, is to actually declare the variable "extern" in a header. And have a single CPP file define the variable (without extern).
Similar to what you mentioned at the end, but slightly different.
The unreal engine uses this pattern, I believe, for defining log categories and code-only-gameplay tags. But both of those are hidden behind a macro for declare/define
(DECLARE_LOG_CATEGORY_EXTERN for headers, and DEFINE_LOG_CATEGORY for the cpp files)
It's good so you don't need to recompile everytime you change its default value.
hey cherno, just from a bit of experience, creating a lot of global structs independently might be less efficient than creating an extern struct array that you can then use enums with for lookups, the array thing works like a charm
good old cherno video, want more of these!!!
I love this kind of video. Can't explain how much I learn by watching your videos. Thanks alot, Cherno.
Great videos. There are not very many C++ video developers out there, and your videos are direct and informative. Not like some of these other guys that put a bunch of flashy BS in their videos that just end up being distracting. We really appreciate your work. BTW, I would also recommend using const or constexpr when defining global variables that will never change.
This was a beautiful video! Explained the topic, issues and solution very clear. Love it. Great job Cherno! 🦾🤖
So, video on inline? 😅
Thank you man! As always good material.
These are the type of videos i learn a lot from
great video, previously I managed my globals namespace by inlining variables.
Very insightful video!
Everything reminds me of the C++ series...
The use of extern to reference a single entity outside of the other files that's how I've done it especially when the variable is coming from some other source like a library that needs to be visible to many functions.
Very informative. Thank you ❤️❤️❤️❤️
Waiting for the handbook for all the errors
So inline is syntactic sugar for extern basically. It creates a magical empty translation unit that puts the variable there
Inside a class you still need to instantiate in a cpp file if you do not use inline, so it's the same thing as a regular global
I'm making an assumption here: First that your definition of various named sprites are intended to be const. In other words you don't want to allow changing one of your named sprites at run time. If this is the case, then something like constexpr may be a better choice. I generally advise to prefer not using global variables. If you do need something that is global then package it up in a class (call it an application class) and ask that class for the data and make accessing the data either for read or write a member function. I've probably wasted several years of time tracking down bugs due to indiscriminate use of global variables. Avoid them if you can, but make sure their use has a solid API so that they can't be abused. A good example of this is the use of std::cout. Put std::cout
Every time I watch an explanation video on C++, I am amazed about how spectacularly bad this language is.
I'm not a C++ programmer, but its inline keyword feels so confusing. It usually means the exact opposite thing - usually it copies(inlines) the given code into each file this code is called from.
It IS confusing, yeah. It should be a copy, and it does exactly that for functions, but not for variables.
For variables, when the linker finds another instance of that variable it just skips it, because it was already linked. Whereas for functions it outright copies the entire function wherever it is used, so that it's faster (it doesn't have to jump to a new location in memory where the initial function is, then return where it was before, to run the rest of the code, it can just run the next code immediately. Better cache locality that way. Because instructions are also cached, not just variables.
inline is simply to avoid One Definition Rule (ODR) violations. It's no longer used to tell the compiler to inline functions (although it does changes the weights slightly).
In C++, inline also means that multiple definitions of the same function are permitted, so you can have a function defined in a header that's included in several sources. So it's in fact an alternative to extern, and useful for header-only libraries. In C++ 17 this functionality was added for variables, too.
Many thanks for this.
Im nowhere near being a pro, I just started, but i’m happy that at least this whole video made sense to me. Getting there!
you are C++ expert so thanks for sharing knowledge
thanks, i wanted to have global variables for simple things like window size since it is updated every frame (can stretch it and all) and didn't want to pass the pointer or value around.
while i know people say to protect your pointer, i want to have a simple access to some variables globally.
Thanks for this. Subbed!
To the defence of programmers using "static" (even if they may not know it), most of the time the compiler will just NOT store that variable in memory, but compile it embedded in the code as a constant. In fact the "static" keyword allows the compiler to do so. In your video, by printing its address or by adding side effects code in the constructor, you are essentially forcing the compiler to actually create instances in memory addresses. So basically your test is flawed for this simple reason. On the other hand, if you take some time to look at the actual assembly code generated by the compiler, and create a simpler version of a variable that does not create any side effect, you may find that in many cases, the use of static is actually not as bad or memory redundant as you think
Can you tell us about the visual studio theme we want to apply? I really liked it
Global vars in headers are just more work for the next programmer. Headers are for declaration. You can use sigleton or another translation unit that holds the vars and expose them with extern.
A video for extern would be great!
In my exp, it's pretty good to define a global variable in a single cpp file and never declare any global variable in header file. To get the global var in another cpp file, I used to create a global getter function.
I store certain things like filenames or resource accessors in arrays and then have an enum in the header file that gives the indices nice names. That works if theres a fixed number in the given category. I use unordered_map if I need to dynamically add or remove global objects of a certain type from memory.
OF COURSE! It's static to the file just like it would be static to the class! It makes sense. C++ IS GREAT
Excelent points there. But I would disagree with your last point, where you basically hide a variable and access it somewhere else. This would make understanding what happens more complex than it needs to be. I personally like the class approach. Wherever you encounter it, its purpose is clear. It is a container of a category of items that are shared across. You can also add the const keyword to explicitly state in your header that those objects are not meant to be modified by others.
What about an extern declaration in the header, and the actual variable in the associated cpp?
I would love a video on the *extern* keyword. :)
it is better to declare with "extern" to make sure it works when it links with multiple libraries.
I'm still a bit confused, shouldn't pragma once or ifndef protect against copying the contents of a header file more than once?
Yes and no. It prevents the content from being compiled more than once withing the same translation unit, but not across multiple translation units. As an example: x.h includes y.h and a.cpp includes both x.h and y.h. This is where the header guard protects the contents of y.h from being compiled twice within the same .cpp file. However, if you have a.cpp include y.h and b.cpp also include y.h, then of course both .cpp files will contain the header contents, and that's where the linker will later encounter duplicate declarations.
Singletons using magic static are my global variables ^^
Hi brother I am new subscriber from India... 🇮🇳
Hey, any updates for the raytracing series or is that officially over? Just wondering if it will continue or not :D Loving the content lately anyways tho! Keep at it
He said in the past it would eventually be moved to Vulkan.
@@landrypierce9942 yeah I know, that's one of the reasons why I am so excited and eager for this series to continue. But it has been a while since last upload :(
Where you involved in the development of the Fifa series?
what about global constants?
You could as well declare the variable in the cpp file and declare the extern variable in the "Sprite" header, therefore all files which includes "Sprite.h" would have access to this global variable.
True. But this can become a really nasty issue if multiple threads access that variable simultaneously.
@@ohwow2074 Yeah that's true, you could define it as an atomic variable, but still having global variables isn't great.
@@caiotoledo30 or have a synchronization mechanism like a mutex or a semaphore that protects access to it.
I hope I become proficient in Java as much as you are in C++ one day. Thanks for your content, you're inspiration.
note that when writing really fast code you should propably avoid using globals and just pass around everything. if you are on clang or gcc you can enable __attribute__((const)) or __attribute__((pure)) to really put this to use, but MSVCs optimizer should also be able to take some use of not using globals.
"note that when writing really fast code you should propably avoid using globals"
Nope. depending on the code, compiler and hardware they can be significantly faster even, but in general not slower (unless you are comparing to code that does not do the same work like initialising memory just in one case).
Yo cherno when are you starting Data structure and Algorithm series
But what about the "#pragma once" instruction in the first line of your header file? Shouldn't that stop the same header from being included (and thereby its global variables declared/initialized) over and over again?
super good video
great vid!!!
I had got a lot of linker errors when I had functions in the header file. I had to use inline to fix those. Though I didn't notice till I included my header only library into a program with more than one .cpp file heh.
I constantly learn new meanings of the keyword inline, when will this end??? 😂 I think I am at the 4th or 5th different use case now?? How can a keyword be so overloaded? 😂
This is causing me to lose my mind somewhat. When I think of inline, I expect almost the exact opposite behavior, like if I write an inline function, I would expect (basically) a new copy of that function's assembly to be made at every point where it's called, instead of having the assembly defined in one place with jumps at the call sites. But apparently for variables it makes sure it's only defined in one place? Why did they use that keyword? This is why I use rust, oh my god.
Inline is the opposite of the regular external. External means it's not defined here. Inline means it's defined here.
For functions it behaves like that, it creates a copy in place of wherever it is used. Specifically, a copy of the function's body, everything between the "{" and "}", using the two "{" and "}" as a scope guard for everything inside it, so that any potentially local variables inside those brackets die when the code execution hits the closing "}" bracket.
But for variables it does not. Confusing, right? You can't just plop a copy of the same variable everywhere. If you wanted to do something like that, you would use a macro (a #define), which is basically just a dumb text editing copy-paste of the value of that "variable", done by the pre-processor ("processor" being the compiler), so that text editing replacement occurs before the compiler even begins going through it (processing it).
The "inline" keyword for variables behaves differently. Even MORE differently for C++17 (and newer) when defining them in a struct/class. But at its core, it basically ignores any future use of the same variable definition and just uses the first one it finds. Not a copy AT ALL. And this is confusing.
For functions, inline also has the same effect of allowing the same function to be defined in multiple translation units (the definition must be the same, or it's undefined behaviour), but the linker will only pick one. In C++ "inline" and "extern inline" are equivalents, for both variables and functions.
1:43 autogenerated text is dangerous 💀
Strange. When I try this example it won't let me use 'inline' saying it's only allowed on function declarations. It does let me use static though.
can you please make a tutorial on what is make, cmake, makeLists ? and how to write code for that. this will clear the confusion what is happening behind the scenes.
at work i was assigned a task, that was on Linux. i somehow did that, but don't know how i got my setup working. only confusion i have is, how to setup build system. how to link other projects against my project. how to add Libraries.
Makes me want to get back in to C++ programming after 30 years
30 years ago C++ was not even standardized. It's a vastly different languages these days. You have to learn it from scratch. Even the hello world program has almost completely changed in C++23.
@@ohwow2074 I made windows programs in 3.12 it consisted of huge case statements to decode windows messages. Hello world typed in from scratch literally took a day. Great fun though and lots a blue screens.
@@ohwow2074no it hasn't
For global variables, I would suggest one writer, multiple reader. The value must copied but not moved if we are talking about multiple readers.
if we only use one writer and one reader, then the value moves.
we must not have multple global variables, even with mutex locks, this will complicate stuffs.
👍Thanks.
Is it good to just use singleton class for global variables?
It is essentially the same thing. I avoid singleton pattern everywhere I can. It is also hard to test.
use static variables inside a class instead. singleton has a very different use than using it as a global variables alternative, but yes its the same thing you can use singleton as an alternative to global variables
Nice explanation, but you could also have mentioned anonymous namespaces.
Dont use static member variables. They break when doing things like loading runtime modules, as each linked module will have its own instance. Instead, do this; define the variable in the cpp file, inside the local namespace for that file (you just do namespace { … } at the top and put your statics there. If you need them to be public, simply declare static getters in the class!
DO you mean inline static members? Because you can do static member and define it in a cpp.. i think the problem is define the static variable in a haeader file without inline.
@@Mateus.007 no i dont mean that. You dont want a header declared variable at all, because if you do, and you have multiple modules involved (especially runtime loaded dlls for example) then each module would have its own instance, since static variables rely on linktime operations. If you have a file local namespace instead it will just work, and only the module that compiled it will have the instance, but it can be shared with other modules fine via the getters and setters (even nonconst reference getters only!)
@@perkele1989 Even with inline???
@@Mateus.007 I believe so. Its in the header, which is shared across modules, however for it to work across runtime modules only a single translation unit in a single module need to store the data. So I dont see how inline static would change that. Someone correct me if I'm wrong, because im not 100% on inline.
@@perkele1989 when you say modules you mean translation units, right? From what i understand inline makes the compiler to share the same variable across multiple translation units, as a substitute to the extern + definition in cpp file technique.
The same thing happened to me once, I started the DirectX API in a .cpp file and in the other I tried to access it, because it was static I didn't know at the time that these variables weren't shared and when I tried to debug it always gave an error when I tried to access, because the pointers were null. After a long time I found out that either I did everything in the same .cpp or put it inside classes. My question is how does this apply to the singleton pattern? About static and return self static instance
The singleton pattern is different because a static member variable doesn't belong to a particular instance of a class/struct. It's shared between all instances of the same class.
So when you create a static Object* obj = nullptr; inside a class Object this obj member is available to all instances of Object (and it's just null at this time)
That's why everytime you call a method to retrieve this single instance you assign it to "this" at the first time (the first time being when obj is still null) and every other instantiation receives the obj pointer directly
Singleton is more smart thing because of lazy instantiation for example and much more. It's modern OOP approach instead of "inline" C magic.
Omg thanks for answers. Every day I want to learn c++ more 😃
@@4dillusions it also comes with a lot of troubles (namely order of initialization/destruction errors) so they should be used wisely
My question is even in case of making the static sprit, it won't take the memory space in any one of file, since static variable don't take any memory space, correct me if I am wrong, I am beginner , Thanks
Static variables do take memory space. And in fact if you put it in a header file and include it in say 5 cpp files, your program will actually have 5 distinct copies of that static variable at runtime so you'll essentially waste memory. That's why he used the inline keyword there.
@@ohwow2074 Thanks for clarification
name of color theme?
Thanks Cherno, “Arrbgg” iously enjoyed this
Unlike what you say: 'inline' is *not* a magical keyword that does multiple things. That's what's wrong with 'static'. 'inline' does the same thing everywhere: it instructs the compiler and linker to merge all definitions of objects annotated with it if they have the same name. This is exactly the same when allowed to functions vs variables. It's just new to allow this for variables.
But just like 'static' the name is poorly chosen because it doesn't describe what it does, just what it was first used for. This causes a lot of unfortunate and unnecessary confusion
Global illumination in Hazel when?
Small request, could you make your face camera like twice as small? It doesn't need to take that much space and I'm sure most people come here for information and not to stare into your beutiful eyes Cherno! Thanks!
the ease of using C# singletons (one instance class) makes me feel so spoilt. lol
why cpp keep reusing the same keyword for different purpuse, like the static, inline (here). why NOT simply crete a new keyword such as GlobalForMultipleCpp in this case??
Now I am surprised... Because with the pragma once, even included in multiple files, in the end there should just be only one instance... But there's more... Why C(++) why?!
Static member in a class, I think that's what is most intuitive to people anyways
I would like to disagree with you that C is not a object oriented programming language,
in the sense of classes and methods I would however agree with you.
Also you can do OOP in C but its more manual and requires function pointers,
it would look more like python where you pass the object to it self also.
It's not just wasteful to have multiple copies, it also wouldn't work as soon as you try to modify the global variable, because you are just modifying one copy of it that the other cpp files can't access.
my guy
I love you