tags:
- Cpp
Namespaces in C++ (ENG)
People have family names to help distinguish individuals who have the exact same first name. This is a worldwide practice, no matter where you live. In C++, we do the same thing. We create specific namespaces to avoid name conflicts.
To the most of us, the very first encounter with namespace
in C++ would be likely using namespace std;
as shown in the example below:
#include <iostream>
using namespace std;
int main(){
cout << "Hello world!" << endl;
return 0;
}
Using using namespace std;
globally allows us to avoid repeatedly typing the std::
prefix. However, this practice can be harmful when handling large projects. Why?
As we just discussed, namespaces in C++ help us avoid name conflicts. Imagine this scenario: you're using multiple libraries that define functions with the same name. When you call these functions, how can the compiler (or linker) know which function to use? Just like if there are two people named Henry in your class, you don't just call out "Henry" because both might respond.
Since this would cause a loading error, we must have some mechanism to solve this problem. In C++, this mechanism is the namespace
. It's like giving a function a name suffix. Let me give an easy example:
int sum(int a, int b) {
return a + b;
}
namespace congzhi{ // namespace definition
int sum(int a, int b) {
// Do something else...
return a + b;
}
}
int main() {
sum(1, 2);
{
using namespace congzhi;
sum(1,2); // call congzhi::sum(1,2);
}
// congzhi::sum(1,2); // Same
return 0;
}
Let's say we use a math library that has a sum()
function, but we would like our own sum()
function to do something else inside the function scope. What we do is add a suffix name to the function using a namespace.
After compiling, you would see the difference: the congzhi
namespace is actually added before the function name, resulting in _ZN7congzhi3sumEii
, exactly like a suffix name or family name if you will.
_Z3sumii:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %edx
movl -8(%rbp), %eax
addl %edx, %eax
popq %rbp
ret
_ZN7congzhi3sumEii:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %edx
movl -8(%rbp), %eax
addl %edx, %eax
popq %rbp
ret
main:
pushq %rbp
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $2, %esi
movl $1, %edi
call _Z3sumii
movl $2, %esi
movl $1, %edi
call _ZN7congzhi3sumEii
movl $0, %eax
popq %rbp
ret
By the way, ::
is called the scope resolution operator in C++, and it is used to specify which namespace or class a particular function or variable belongs to. The ::
operator is also used to refer to the global namespace. As in:
int func(){ // global function
}
int var; // global variable
int main(){
::var = 10; // equals to var = 10;
::func(); // equals to func();
}
Just like namespaces, classes can also play a similar role in C++. By defining member functions within a class, you can ensure that functions with the same name do not conflict with each other.
class MathLib {
public:
int sum(int a, int b) {
return a + b;
}
inline static int i = 0;
};
int main() {
MathLib math;
int result1 = math.sum(1, 2); // Calls MathLib::sum
MathLib::i = 5; // Accesses static variable i
return 0;
}
Differently, unlike namespaces, classes provide encapsulation for member variables and member functions. This is something you cannot achieve with namespaces. That's why in most large projects, we use this pattern:
namespace congzhi{
class MathLib{};
class GraphLib{};
class IOLib{};
}
Thus we can group those related functionalities and avoid name conflicts.
Nested namespaces are not recommended in good design in most cases. We use namespaces primarily to distinguish different libraries. For example, std
is used for the C++ standard library, and sdl
is used for the SDL library. However, if you wish, you can add suffixes to function names as much as you like.
You simply use nested namespaces like this:
// nested namespaces
namespace A{ // Equals to: namespace A::B::C{ }
namespace B{
namespace C{
exampleFunc(){
// ...
}
// Some functions here...
}
}
}
int main(){
{ // using derectives
using namespace A::B::C;
exampleFunc();
} // A using namespace scope with life time
A::B::C::exampleFunc(); // Same
return 0;
}
using namespace
as Sparingly as PossibleNo denying, using the using namespace
directive can be convenient. However, it can also bring issues that are not easy to identify, especially as the project grows larger.
// lib.hpp
namespace A{
int sum(int a, int b){ // A::sum()
return a + b;
}
}
namespace B{
int sum(int a, int b){ // B::sum()
return a + b;
}
}
// main.cpp
#include "lib.hpp"
using namespace A; // gloabl namespace
using namespace B; // global namespace in another file
int main(){
sum(1, 2); // which sum() would it call?
}
In this simple example, try to guess which function it would call. You might think, "Well, I could just use the scope resolution operator B::
in this case, right?" But in a large project built by a team of people, this sum()
call becomes ambiguous. Which one does it call? Even the compiler won't know, leading to compilation errors and potential big problems in the future.
NEVER EVER WRITE using namespace
AT GLOABL SCOPE IN A HEADER FILE
So a good practice is as follows:
// main.cpp
#include "lib.hpp"
// No global namespace directives are allowed.
int function_using_lib_a(int a, int b) {
using namespace A;
return sum(a, b); // Calls A::sum
}
int main() {
function_using_lib_a(1, 2);
{ // Samll scope
using namespace B;
sum(1, 2); // Calls B::sum
}
// Or like this:
B::sum(1, 2); // Calls B::sum, even cleaner...
return 0;
}
Make the scope of using namespace
as small as possible, or try not to use it at all.
Remember we had a example look like this? Every time we wanted to use exampleFunc()
, we had to add the long suffix A::B::C::
, this can be annoying. So, we can use namespace alias to define a alternate name for a namespace.
// nested namespaces
namespace A{ // Equals to: namespace A::B::C{ }
namespace B{
namespace C{
exampleFunc(){
// ...
}
// Some functions here...
}
}
}
namespace ABC = A::B::C; // namespace alias
int main(){
A::B::C::exampleFunc();
ABC::exampleFunc(); // Same
return 0;
}