在阅读new 与delete相关的东西时,被各种命名空间全局作用域搞得有点乱,在此记录一下。
一、命名空间的定义
只要能出现在全局作用域中的东西就可以出现在命名空间中。通常这么定义命名空间:
namespace Name {
decl and define
} // 无须分号,像代码块,而不是类
命名空间有下面两个重要的属性:
1、命名空间可以是不连续的
2、命名空间是一个独立的作用域,所以同一个命名空间中不能出现两个一样的名字
所以一般我们处理自己的命名空间的方式是这样的:
1、在自己头文件中,先使用头文件保护,然后定义命名空间,在命名空间中做出声明但不定义
2、写一份CPP文件,在文件开头包含头文件,同时using namespace myNamespace;(也可以分别使用域作用符), 然后定义头文件中的声明
值得注意的是,不能把#include<string>这样的东西包含在自己的命名空间中,否则在自己的命名空间中定义嵌套std命名空间,就出错了,为什么,我也不知道为什么。
二、特殊的命名空间
c++中有两个隐式声明的命名空间,而且非常容易混淆,它们分别是全局命名空间,与未命名的命名空间,
1,全局命名空间
全局作用域中定义的名字(即在所有的类、函数以及命名空间之外定义的名字), 都会被隐式的添加到全局命名空间中。
2, 未命名的命名空间
未命名的命名空间是指关键字namespace之后紧跟花括号括起来的一系列声明语句。不同的文件都有自己独立的命名空间,未命名的命名空间可以在一个文件中不连续,但是不会扩展到其他文件,作用相当于C语言中全局static声明,学习C++的时候请忘掉C语言的语法。
使用未命名命名空间中的变量时,不需要添加任何域作用符,所以当全局作用域中的未命名命名空间与全局作用域中的名字一样时,会出现二义性。
未命名的命名空间中的名字与该命名空间的作用域一样,所以也就不难理解为什么全局作用域中的名字不能与全局未命名空间中的名字一样了。
三、命名空间嵌套与命名空间内联
1、 命名空间嵌套和其他的嵌套没什么不一样,内层屏蔽外层,在外层使用内层需要域作用符。
2、 命名空间内联
命名空间内联是c++ 11 的新特性,在命名空间第一次被声明的地方加上inline关键字,则这个命名空间中的名字可以被外层命名空间看见,如:
namespace ns {
inline newEdition {
xxx
}
oldEdition { xx}
}
这样用户在使用ns命名空间中的名字时,可以直接使用新版本的名字,如ns::newName,而非内联则需要ns::oldEdition::oldName;
通常实际的代码会这么写(再次声明#include只是简单的文本替换)
namespace ns {
inline namespace newEd { #include "newEd" }
namespace oldEd { #include "oldEd" }
}
使用命名空间中的名字:
三种方法,using 声明,using指示,使用域作用符。
特别值得注意的是,我们在使用std::move时有一条非常重要的规则,就是一定要使用std::move, 而不是using std::move;
这里讲原因:
1) using std::move;可以让std中的move变得可见,并且作用域同该using语句,这些都没有问题,但是在查找函数名字时,如果函数接受类类型的参数,那么编译器还会在该类所在的命名空间去找该函数的名字,如:
std::string s;
std::cin>>s;
对于函数operator>>,由于他接受std中的string类型,所以编译器还会在std中查找该函数,所以即使我们没有定义自己的>>,也没有使用using std::operator>>;编译器还是能够找到这个函数。
那么当我们使用using std::move;时,如果此时还有另外一个可用的move可见,如
int move(int i) { return 9; } //自定义的move using std::move; int main() { std::cout << move(1); //输出9 return 0; }
根据函数重载时的匹配规则,非模板函数优先级高于模板函数,so,会选择自定义的move函数。
因为这个原因,使用swap函数时,就不能使用std::swap,这样会让编译器去各种地方找swap函数的生命,以选择最好的匹配
int move(int i) { return 9; } //自定义的move int main() { using std::move; std::cout << move(1); //输出1 return 0; }