18.2命名空间
命名空间污染:应用程序用到多个独立开发的库(内含大量的全局名字,如类,函数模板)就会引发名字冲突
命名空间:命名空间分割全局命名空间,每个命名空间是一个作用域
18.2.1命名空间定义
namespace cplusplus_primer
{
class Sales_data{
};
Sales_data operator+(const Sales_data&, const Sales_data);
class Query{
};
class Query_base{
};
}//命名空间结束后无须分号。与块类似 不能定义在函数或类的内部
每个命名空间都是一个作用域
cplusplus_primer::Query q =cplusplus_primer::Query("hello");
//假设还有另一个命名空间的AddisonWesley也提供了Query类
AddisonWesley::Query q =AddisonWesley::Query("hello");
命名空间可以是不连续的
为什么怎么说,因为命名空间可以·定义在几个不同的部分
namespace nsp
{
//相关声明
}
上述代码可能是新定义一个命名空间,也可能是为了已存在的命名空间添加一些新成员
命名空间的定义可以不连续的特性可以将几个独立的接口和实现文件组成一个命名空间
命名空间的组织方式类似自定义类及函数的方式:
1命名空间的一部分成员的作用是定义类,以及声明类接口的函数及对象,应置与头文件
这些头文件将被包含在使用了这些成员的文件中
2命名空间成员的定义部分则置与另外的源文件中
定义多个类型不相关的命名空间应该使用单独的文件分别表示每个类型(或关联类型构成的集合)
定义本书的命名空间(上面说了那么多,其实就好像是分离式编译只不过很特殊)
//---Sales_data.h
// #include应该出现在打开命名空间之前
#include<string>
namespace cplusplus_primer
{
class Sales_data{
};
Sales_data operator+(const Sales_data&, const Sales_data);
//其他Sales_data的其他接口函数的声明
}
//---Sales.data.cc---
//确保#include出现在打开命名空间的操作之前
#include "Sales_data.h"
namespace cpluseplus_primer
{
//Sales_data成员及重载运算符的定义
}
在别的程序中使用上面定义的库,必须包含必要的头文件这些头文件的名字定义在命名空间cpluwplus_primer
//----user.cc
//Sales_data.h头文件的名字位于命名空间cplusplus_primer中
#include "Sales_data.h"
int main()
{
using cplusplus_primer::Sales_data;
Sales_data transl,trans2;
return 0;
}
不应该把#include放在命名空间内部,
隐含意思:把头文件所有名字定义成该命名空间的成员,
定义命名空间成员
{存在合适的声明语句,可以简写}
#include"Sales_data.h"
namespace cplusplus_primer{
//重新打开命名空间cplusplus_primer 命名空间定义的成员可以直接使用名字,此时无须前缀
std::istream& operator>>(std::istream&in,Sales_data&s){
/*...*/}
}
命名空间外部定义该命名空间的成员
//命名空间之外定义的成员必须使用含有前缀的名字
cplusplus_primer::Sales_data cplusplus_primer::operator+(const Sales_data lhs,const Sales_data& rhs)
{
Sales_data ret(rhs);
//...
}
提示:不要在一个不相关的作用域中定义这个运算符
模板特例化
//我们必须将模板特例化声明成std成员
namespace std
{
template<>struct hash<Sales_data>;
}
//在std中添加了模板特例化的声明后,就可以在命名空间std的外部定义它了
template<>struct std::hash<Sales_data>
{
size_t operator()(const Sales_data)const
{
return hash<string>(s.booNO)^
hash<unsingned>(s.units_sold)^
hash<double>(s.revenue)^
//其他成员与之前的版本一致
}
};
全局命名空间
它以隐式的方式声明,并且在所有程序中都存在,全局作用域中定义的名字被隐式地添加到全局命名空间
作用域运算符可以用于全局作用域的成员
::member_name
嵌套的命名空间
namespace cpluseplus_primer
{
//第一个嵌套的命名空间:定义了库的Query部分
namespace QueryLib
{
class Query {
};
Query operator&(const Query&, const Query&);
}
//第二个嵌套的命名空间:定义了库的Sales_data部分
namespace Bookstore
{
class Quote{
};
class Disc_quote :public Quote{
/*...*/ };
}
}
外层命名空间中的代码要想访问它必须在名字前添加限定符。
cplusplus_primer::QueryLib::Query
内联命名空间 c++新标准 可以被外层命名空间直接使用,无须在内联命名空间的名字前添加表达该命名空间的前缀
inline namespace FifthEd
{
//该命名空间表示本书第五版的代码
}
//后续打开的话就可以写inline也可以不写
namespace FifthEd //隐式内联
{
class Query_base{
/*...*/};
//其他与Query有关的声明
}
namespace FourthEd
{
class Item_base{
/*...*/};
class Query_base{
/*...*/};
//假设前面的FifthEd是最新的第五版表示的代码,而FourthEd是第四版用到的其他代码,inline的作用就出来的了
}
//假定每个命名空间都定义在同名的头文件中,则可以用命名空间cplusplus_primer定义如下形式
namespace cpusplus_primer
{
#include "FifthEd.h"
#include "FourthEd.h"
}
FifthEd是内联的,用cplusplus_primer::的代码可以直接获得FifhEd的成员,就是优先选择FifthEd的代码
如果想用更早期的代码得加上完整的外层命名空间名字:cplusplus_primer::FourthEd::Query_base
未命名的命名空间
namespace
{
//定义的变量拥有静态生命周期:第一次使用前创建,程序结束销毁
}
未命名的命名空间仅在特定的文件内部有效,其作用范围不会横跨多个不同的文件
int i; //i的全局声明
namespace
{
int i;
}
//二义性;i的定义即出现在全局作用域中,又出现在未嵌套的未命名的命名空间中
i =10;
嵌套在其他命名空间的未命名的命名空间
namespace local
{
namespace
{
int i;
}
//正确:定义在嵌套的未命名的命名空间中的i与全局作用域中的i不同
}
local::i =42; //未命名的命名空间成员通过了local访问到了
未命名的命名空间取代文件中的静态声明
作用:c语言用static声明使得其对于整个文件有效,c++用未命名的命名空间
18.2.1节练习 代码链接https://zhuanlan.zhihu.com/p/362485261
#pragma once
#include<iostream>
#include<fstream>
#include<string>
#include<sstream>
#include<map>
#include<set>
#include<vector>
namespace chapter10
{
using namespace std;
class QueryResult
{
friend ostream& print(ostream&, const QueryResult&);
public:
using line_no = vector<string>::size_type;
using line_iter = set<line_no>::iterator;
QueryResult(string s, shared_ptr<set<line_no>>l, shared_ptr<vector<string>>f) :sought(s), lines(l),file(f) {
}
line_iter begin()const {
return lines->cbegin(); }
line_iter end()const {
return lines->cend()