7 作用域、生存期、链接
用户自定义命名空间和范围解析运算符
- 为了避免命名冲突,在尽可能小的作用域内定义标识符
- 一个命名空间要么在全局作用域内定义,要么在另一个命名空间内定义
- 使用范围解析运算符
::
可以告诉编译器去指定命名空间查找指定标识符(如果::
前没有命名空间则表示全局命名空间) - 如果使用标识符时没有带范围解析运算符,则编译器首先在使用该标识符的命名空间内查找是否有匹配的声明;如果没有则往外层的命名空间继续查找;最后查找全局命名空间
// 以下代码输出Foo; 如果注释掉Foo里的print()则输出Out;如果再注释掉Out里的print()则输出global
#include "iostream"
void print() {
std::cout << "global";
}
namespace Out {
void print() {
std::cout << "Out";
}
namespace Foo {
void print() {
std::cout << "Foo";
}
void printHelloThere() {
print();
}
}
}
int main() {
Out::Foo::printHelloThere();
return 0;
}
- 对于一个命名空间里的标识符,它们的前向声明也应该放在该命名空间里
// add.h
namespace BasicMath{
int add(int x,int y);// 在BasicMath命名空间里声明add()
}
// add.cpp
namespace BasicMath{
int add(int x,int y){ // 在BasicMath命名空间里定义add()
return x+y;
}
}
// main.cpp
#include "add.h" // 为了引入BasicMath::add()
#include <iostream>
int main(){
std::cout<<BasicMath::add(3,4)<<'\n';
return 0;
}
如果add()前向声明没有放在BasicMath命名空间里,则add()会在全局作用域中声明,则编译器会报错找不到BasicMath::add()的声明;
如果add()的定义没有放在BasicMath命名空间里,编译不会报错,但是链接器会报错BasicMath::add()未定义
- 可以在多个位置(跨多个文件或一个文件的多个位置)声明命名空间块
标准库大量使用了这个特性,因为每个标准库头文件都包含了
std
命名空间的声明,否则整个标准库都得在一个头文件中定义
这意味着可以将自己的代码添加到std
命名空间中,但这可能会导致未定义的行为,因为std
命名空间不允许用户扩展
- 嵌套命名空间有两种形式:
namespace a{namespace b{}}
或者namespace a::b{}
(自从c++17支持) - 命名空间别名:
namespace c = a::b;