CHAPTER 2
Consider the following code:
Sample: GlobalNamespaceSample\GlobalNamespaceSample.cpp
#include <iostream> #include <ostream> #include "../pchar.h" int g_x = 10; int AddTwoNumbers(int a, int b) { return a + b; } int _pmain(int /*argc*/, _pchar* /*argv*/[]) { int y = 20; std::wcout << L"The result of AddTwoNumbers(g_x, y) where g_x = " << g_x << L" and y = " << y << L" is " << AddTwoNumbers(g_x, y) << L"." << std::endl; if (true == 1) { std::wcout << L"true == 1!" << std::endl; } return 0; } |
About functions |
In the previous sample, we define two functions, AddTwoNumbers and wmain. These two functions are both in the global namespace. The global namespace is the base level in which everything else within the program exists. C++, owing to its C heritage, allows you to define anything within the global namespace (so you can define namespaces, classes, structures, variables, functions, enums, and templates).
C# also has the concept of a global namespace, but it does not allow you to define anything within it other than namespaces and types. In the previous example, we have the statement int g_x = 10; which defines an integer named g_x within the global namespace and assigns it a value of 10. This is what is commonly referred to as a global variable. In C# global variables are illegal.
As a brief aside, every programming language I have ever worked with has had its share of bad features—things the language allows, but things that usually lead to issues. These issues include hard-to-debug problems, subtle errors that go unnoticed for a long time, maintainability problems, readability problems, and all the other frustrating things that add many hours to development time without any benefit. C++ is no different. When we come across something that fits this description, I will do my best to call it out. This is one of those times.
Global variables are bad. Avoid them whenever possible. There is a common convention when using them in C++, which is to prefix the variable name with g_, as in the previous example. While this helps to alert you and other programmers to the fact that this is a global variable, it does not change the fact that it is a global variable, having all the problems I described. This isn’t a book on bad programming practices, so I’m not going to spend time explaining why global variables are bad. All you need to know is this feature exists in C++, but you should avoid using it whenever possible.
In C++, :: is the scope resolution operator. It is used for separating nested namespaces, for separating types from their namespace, and for separating member functions and variables from their type.
Note that it is only used in the last situation when performing the following:
In other instances, you use either the . operator or the -> operator, depending on whether you are accessing the member directly or via a pointer.
This can seem complicated since C# just uses the . operator for all of the purposes that ::, ., and -> are used for in C++.
Note: We'll discuss the . and -> operators later. For now, you just need to know that they are used for accessing instance member variables and non-static member functions (which you use depending on whether or not you are working with a pointer).
For the most part, you'll be fine. The only place you’re likely to trip up is if you try to access a base class member by using the . operator rather than the :: operator, or if you try to specify an enum member using something other than ::. If you ever compile your program and receive a syntax error complaining about "missing ';' before '.' ", it's a good bet you used a . where you should've used a :: instead.
A namespace is defined in much the same way as it is in C#. Here is an example:
Sample: NamespacesSample\NamespacesSample.cpp
#include <iostream> #include <ostream> #include <string> #include "../pchar.h" using namespace std; namespace Places { namespace Cities { struct City { City(const wchar_t* name) { Name = wstring(name); } wstring Name; }; } } int _pmain(int /*argc*/, _pchar* /*argv*/[]) { auto nyc = Places::Cities::City(L"New York City"); wcout << L"City name is " << nyc.Name.c_str() << L"." << endl;
return 0; } |
As you can see, we can nest namespaces and in turn access those namespaces and the types contained within them using the scope resolution operator. You’ll also notice that we used the auto keyword. This is the C++ equivalent of C#’s var keyword.
Looking at the NamespacesSample, notice that we’ve included the line using namespace std;. This line looks very similar to the using directive in C#, and it is; however, in C# the using directive automatically brings into scope any types in that namespace which are in any of the referenced assemblies. By contrast, in C++ a using directive only brings into scope the types in the included header files, not all of the types in the libraries that will be linked to your program or library. What are header files you ask? That question provides a good segue into the next chapter.
Note: Never include a using directive in a header file. If you do that, you don’t just import the types and namespaces in that namespace into the header file, you also import them into any source or header file that includes the header file. This causes really nasty namespace pollution issues. We’ll be discussing header files next, so anything that’s unclear about this should make sense then. Just remember that having a using namespace directive in a header file is a bad idea; only use them in your source code files.