When working with bigger projects, a problem can arise when many different identifiers exist in the same project. For example, two variables, functions or classes that have the same name.
The way this problem is solved is by the introduction of namespaces
.
We can wrap sections of our code inside a namespace
as shown below.
namespace Math {
// your code here
}
We can popualte namespaces with any of the constructs we've seen before. This can include variables, functions and even complete classes:
namespace Math {
int Add(int x, int y) {
return x + y;
}
float Pi { 3.14 };
float Circumference (float Diameter) {
// Because this function is in the same namespace as
// the pi variable, we can access Pi as normal here
return Diameter * Pi;
}
class Square {
float SideLength { 5.0 };
float Area() { return SideLength * SideLength };
}
}
How can we create a namespace called Utilities
?
In the earlier example, we saw that members of a namespace can access other members of their namespace as normal:
namespace Math {
float Pi { 3.14 };
float Circumference(float Diameter) {
// We can access Pi here as we're in the same namespace
return Diameter * Pi;
}
}
To access namespace members from outside the namespace, we need the Scope Resolution Operator, ::
For example, the following statement accesses the Add
function and the Pi
variable, which are both inside the Math
namespace:
Math::Add(10, Math::Pi);
How can we call the Square
function within the Utilities
namespace?
namespace Utilities {
void Square() {};
};
Namespaces can be nested inside other namespaces in C++
namespace Math {
int Add(int x, int y) {
return x + y;
}
namespace Geometry {
float Pi { 3.14f };
}
}
To access these nested identifiers, we can can use the ::
operator multiple times:
Math::Add(10, Math::Geometry::Pi)
using
DeclarationsStarting from the very first lesson, we've had a line of code in our files that we've left unexplained thus far:
using namespace std;
Predictably, this line is related to namespaces.
The effect of this using
declaration was an instruction that asks the compiler: "if an identifier in this scope is not found, try searching for it in the std
namespace".
This using
statement has been necessary every time we used cout
and endl
. These identifiers were actually in the std
namespace.
Without the using
declaration, we would needed to use the scope resolution operator. Instead of this:
using namespace std;
cout << "Hello world!" << endl;
We'd have needed to do this:
std::cout << "Hello world!" << std::endl;
We can use using
declarations with any namespace - not just std
:
namespace Maths {
float Pi { 3.14f };
}
using namespace Maths;
using namespace std;
cout << Pi << endl;
What statement can we add on line 5 below that will make line 7 work?
1namespace Utilities {
2 void Square() {};
3};
4
5// ?
6
7Square();
8
using namespace
Statements?Probably not. Namespaces exist to help us organise code - a using namespace
declaration effectively subverts that organisation.
We will continue to use them in our lessons. This is only for two reasons:
But, in real projects, we should be cautious about adopting using
declarations.
Remember too how #include
directives work - if we have a using
declaration that is scoped to an entire file, that would also effect any file that includes it.
This can cause things to get out of hand quite quickly so, if using
declarations are going to be adopted, be particularly cautious when using them on files that are likely to be included in other files.
The Unreal coding standard asks for using
declarations to never be used in the global scope:
Do not put using declarations in the global scope, even in a .cpp file. It's okay to put using declarations within another namespace, or within a function body.
And the Google C++ standard asks that using
declarations never be used, anywhere:
Do not use using-directives (e.g., using namespace foo
).
If we want to use using
statements, there are two ways we can get their benefits, whilst minimising their drawbacks. The first is to use them only in the scope they're necessary. The second is to restrict them to only using the specific identifiers we need.
using
DeclarationsUsing declarations follow the normal block scoping rules. A using
declaration outside of any block has "global" scope, and will apply to the entire file.
This can be problematic, especially if that file goes on to be added to a load of other files, via the #include
directive.
As an alternative to global using
declarations, we can place them inside blocks - usually function bodies or other namespaces. This wil limit their effect to just the scope of that block.
void Hello() {
using namespace std;
cout << "Hello ";
}
void World() {
// Error! The using declaration only applies to the block where it is used
cout << "World"
}
int main() {
Hello();
World();
}
using
Declarations to Specific IdentifiersRather than having a using
statement that covers the entire std
namespace, we can restrict it just to specific identifiers.
For example, to use it with just std::cout
and std::endl
, we could write this:
#include <iostream>
using std::cout, std::endl;
int main() {
cout << "Hello world!" << endl;
}
Become a software engineer with C++. Starting from the basics, we guide you step by step along the way