用于大型程序的工具
--命名空间
引言:
在一个给定作用域中定义的每一个名字在该作用域中必须是唯一的,对庞大、复杂的应用程序而言,这个要求可能难以满足。这样的应用程序的全局作用域中一般有很多名字定义。由独立开发的库构成的复杂程序更有可能遇到名字冲突 —— 相同的名字既可能在我们自己的代码中使用,也可能(更常见地)在独立供应商提供的代码中使用。
库倾向于定义很多全局名字 —— 主要是模板名、类型名或函数名。在使用来自多个供应商的库编写应用程序的时候,这些名字中有一些差点儿不可避免地会发生冲突,这样的名字冲突问题称为命名空间污染问题。
传统上,程序猿通过将全局实体的名字设得非常长来避免命名空间污染,常常常使用特定字符序列作为程序中名字的前缀:
class cplusplus_primer_Query { //... }; ifstream & cplusplus_primer_open_file(ifstream &,const string &);
程序猿编写和阅读使用这样的长名字的程序非常麻烦。
命名空间为防止名字冲突提供了更加可控的机制,命名空间能够划分全局名字空间,这样使用独立开发的库就更加easy了。一个命名空间是一个作用域,通过在命名空间内部定义库中的名字,库的作者(以及用户)能够避免全局名字固有的限制。
一、命名空间的定义
以keywordnamespace開始,后接命名空间的名字:
namespace cplusplus_primer { class Sales_item { //... }; Sales_item operator+(const Sales_item &, const Sales_item &); class Query { public: Query(const std::string &); std::ostream &display(std::ostream &) const; //... }; class Query_base { //... }; }
定义了cplusplus_primer的命名空间,它有四个成员:两个类,一个重载的+操作符,一个函数。
像其它名字一样,命名空间的名字在定义该命名空间的作用域中必须是唯一的。命名空间能够在全局作用域或其它作用域内部定义,但不能在函数或类内部定义。
命名空间名字后面接着由花括号括住的一块声明和定义,能够在命名空间中放入能够出如今全局作用域的随意声明:类、变量(以及它们的初始化)、函数(以及它们的定义)、模板以及其它命名空间。
命名空间作用域不能以分号结束。
1、每一个命名空间是一个作用域
定义在命名空间中的实体称为命名空间成员。当中的每一个名字必须引用该命名空间中的唯一实体。不同命名空间能够具有同名成员。
在命名空间中定义的名字能够被命名空间中的其它成员直接訪问,命名空间外部的代码必须指出名字定义在哪个命名空间中:
cplusplus_primer::Query q = cplusplus_primer::Query("hello"); q.display(cout);
假设还有一命名空间(如AddisonWesley)也提供TextQuery类,并且我们想要使用那个类取代cplusplus_primer中定义的TextQuery,能够通过这样改动代码而实现:
AddisonWesley::Query q = AddisonWesley::Query("hello"); q.display(cout);
2、从命名空间外部使用命名空间成员
能够使用using声明来获得对我们知道将常常使用的名字的直接訪问:
using cplusplus_primer::Query; Query q = Query("world"); q.display(cout);
3、命名空间能够是不连续的
与其它作用域不同,命名空间能够在几个部分中定义。命名空间由它的分离定义部分的总和构成,命名空间是累积的。一个命名空间的分离部分能够分散在多个文件里,在不同文本文件里的命名空间定义也是累积的。当然,名字仅仅在声明名字的文件里可见,这一常规限制继续应用,所以,假设命名空间的一个部分须要定义在还有一文件里的名字,仍然必须声明该名字。
编写命名空间定义:
namespace namespace_name { //... }
既能够定义新的命名空间,也能够加入�到现存命名空间中。
假设名字namespace_name不是引用前面定义的命名空间,则用该名字创建新的命名空间,否则,这个定义打开一个已存在的命名空间,并将这些新声明加到那个命名空间。
4、接口和实现的分离
能够用分离的接口文件和实现文件构成命名空间,因此,能够用与管理自己的类和函数定义相同的方法来组织命名空间:
1.定义类的命名空间成员,以及作为类接口的一部分的函数声明与对象声明,能够放在头文件里,使用命名空间成员的文件能够包括这些头文件。
2.命名空间成员的定义能够放在单独的源文件中。
这个要求相同适用于命名空间中定义的名字。通过将接口和实现分离,能够保证函数和其它我们须要的名字仅仅定义一次,但相同的声明能够在不论什么使用该实体的地方见到。
【最佳实践】
定义多个不相关类型的命名空间应该使用分离的文件,表示该命名空间定义的每一个类型。
5、定义本书的命名空间
使用将接口和实现分离的策略:
//1 ---Sales_item.h--- #ifndef SALES_ITEM_H_INCLUDED #define SALES_ITEM_H_INCLUDED namespace cplusplus_primer { class Sales_item { //... }; Sales_item operator+(const Sales_item &,const Sales_item &); } #endif // SALES_ITEM_H_INCLUDED
//2 ---Query.h--- #ifndef QUERY_H_INCLUDED #define QUERY_H_INCLUDED #include <fstream> #include <string> namespace cplusplus_primer { class Query { public: Query(const std::string &); std::ostream &display(std::ostream &) const; }; class Query_base { //... }; } #endif // QUERY_H_INCLUDED
//3 ---Sales_item.cpp--- #include "Sales_item.h" namespace cplusplus_primer { //定义 }
//4 ---Query.cpp--- #include "Query.h" namespace cplusplus_primer { //定义 }
这样的程序组织给予开发人员和库用户必要的模块性。每一个类仍组织在自己的接口和实现文件里,一个类的用户不必编译与其它类相关的名字。假设同意 Sales_item.cpp和 main.cpp文件编译和链接到一个程序而不会导致编译时错误和执行时错误,就能够对用户隐藏实现。库的开发人员能够独立工作于每一个类型的实现。
使用我们的库的程序能够包括须要的头文件,那些头文件里的名字定义在命名空间 cplusplus_primer内部:
// --- main.cpp --- #include "Sales_item.h" int main() { cplusplus_primer::Sales_item trans1,trans2; //... }
6、定义命名空间成员
在命名空间内部定义的函数能够使用同一命名空间中定义的名字的简写形式:
namespace cplusplus_primer { std::istream &operator>>(std::istream &in,Sales_item &) { //... return in; } }
在命名空间的外部定义命名空间成员:名字的命名空间声明必须在作用域中,并且定义必须指定该名字所属的命名空间:
cplusplus_primer::Sales_item cplusplus_primer::operator+(const Sales_item &lhs, const Sales_item &rhs) { Sales_item ret(lhs); //... return ret; }
定义看起来相似于定义在类外部的类成员函数,返回类型和函数名由命名空间名字限定。一旦看到全然限定的函数名,就处于命名空间的作用域中。因此,形參表和函数体中的命名空间成员引用能够使用非限定名引用Sales_item。
7、不能在不相关的命名空间中定义成员
尽管能够在命名空间定义的外部定义命名空间成员,对这个定义能够出现的地方仍有些限制:仅仅有包围成员声明的命名空间能够包括成员的定义。比如,operator+ 既能够定义在命名空间cplusplus_primer中,也能够定义在全局作用域中,但它不能定义在不相关的命名空间中。
8、全局命名空间
定义在全局作用域的名字(在随意类、函数或命名空间外部声明的名字)是定义在全局命名空间中的。全局命名空间是隐式声明的,存在于每一个程序中。在全局作用域定义实体的每一个文件将那些名字加到全局命名空间。
能够用作用域操作符引用全局命名空间的成员。由于全局命名空间是隐含的,它没有名字,所以记号
::member_name;
引用全局命名空间的成员。
//P603 习题17.13/14/15 //--- Bookstore.h --- #ifndef BOOKSTORE_H_INCLUDED #define BOOKSTORE_H_INCLUDED #include <stdexcept> #include <string> #include <iostream> namespace Bookstore { class out_of_stock : public std::runtime_error { public: out_of_stock(const std::string &s): runtime_error(s) {} }; class isbn_mismatch : public std::logic_error { public: isbn_mismatch(const std::string &s):logic_error(s) {} isbn_mismatch(const std::string &s, const std::string &lhs, const std::string &rhs): logic_error(s),left(lhs),right(rhs) {} std::string left,right; virtual ~isbn_mismatch() throw () {} }; class Sales_item { friend Sales_item operator+(const Sales_item &,const Sales_item &); friend std::ostream &operator<<(std::ostream &,const Sales_item &); friend std::istream &operator>>(std::istream &,Sales_item &); public: Sales_item(const std::string &s = ""): isbn(s),units_sold(0),revenue(0) {} Sales_item(std::istream &in) { in >> *this; } std::string book() const { return isbn; } bool same_isbn(const Sales_item &rhs) const { return isbn == rhs.isbn; } void setVal() { std::cin >> isbn >> units_sold >> revenue; } Sales_item &operator+=(const Sales_item &rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } private: std::string isbn; unsigned units_sold; double revenue; }; } #endif // BOOKSTORE_H_INCLUDED
// --- Bookstore.cpp --- #include "Bookstore.h" namespace Bookstore { Sales_item operator+(const Sales_item &lhs,const Sales_item &rhs) { if (!lhs.same_isbn(rhs)) { throw isbn_mismatch("isbn mismatch ",lhs.book(),rhs.book()); } Sales_item ret(lhs); ret += rhs; return ret; } std::ostream &operator<<(std::ostream &os,const Sales_item &rhs) { os << rhs.book() << " " << rhs.units_sold << " " << rhs.revenue; return os; } std::istream &operator>>(std::istream &in,Sales_item &rhs) { double price; in >> rhs.isbn >> rhs.units_sold >> price; if (in) { rhs.revenue = price * rhs.units_sold; } else { rhs = Sales_item(); } return in; } }
//--- MyApp.h --- #ifndef MYAPP_H_INCLUDED #define MYAPP_H_INCLUDED #include "Bookstore.h" namespace MyApp { void processTrans() { Bookstore::Sales_item item1,item2,sum; while (std::cin >> item1 >> item2) { try { sum = item1 + item2; } catch (Bookstore::isbn_mismatch &e) { std::cerr << e.what() << ": left isbn(" << e.left << "); right isbn(" << e.right << ")" << std::endl; continue; } std::cout << sum << std::endl; } } } #endif // MYAPP_H_INCLUDED