C++PrimerPlus 学习笔记 | 第九章 内存模型和命名空间|2.10 名称空间

名称空间

在C++中名称可以是变量函数结构美剧类和类的结构和成员,随着项目的增大,名称相互冲突的可能性也可能上升,当使用多个厂商的类库,可能导致名称冲突。C++标准提供了名称空间工具,以便更好的控制名称的作用域.

传统的C++名称空间

  1. 声明区域是指可以在其中进行声明的区域。例如函数外面声明的全局变量,对于这种变量声明区域为其所在文件,对于函数中声明的变量,其声明区域为其所在代码块。
  2. 潜在作用域变量的潜在作用域为从变量声明点开始到其声明区域结束,因此潜在作用域比声明区域小,这是由于变量必须定义后才能使用。
  3. 然而变量并非是在其潜在作用域一直可用的,他可能被另一个嵌套声明区域的同名变量覆盖,变量对于程序而言可见的范围称为作用域

其区分的关键点,声明区域用于表示这一个代码块存在这一个名称,而潜在作用域表示该变量物理上存在的区域,作用域表示变量可见的区域

C++关于全局变量和局部变量的规则定义了一种名称空间层次,每个声明区域都可以声明名称,这些名称独立于其他名称中声明的名称,在一个函数中声明的局部变量不会与另外一个函数中声明的局部变量冲突。

新的名称空间特性

C++新增了一个功能,即通过定义一种新的声明区域来创建命名的名称空间,这样做的目的之一是提供一个声明名称的区域,一个名称空间的名称不会与另一个命名空间的相同名称发生冲突 来看一下实例

namespace Jack // 名称空间 Jack
{
  double pail;
  void fetch();
  int pal;
  struct well;
}
namespace Jill // 名称空间 Jill
{
  double bucket(double n);
  double fetch;
  int pal;
  struct Hill;
}

名称空间可以是全局的也可以位于另一个名称空间中,但不能位于代码块中,因此在默认情况下名称空间声明的名称的链接性是外部的。

namespace A { // 全局名称空间
    namespace A_sub { // 局部名称空间
        
    }
}

除了用户定义的名称空间外,还有一个全局名称空间,他对应于文件级声明区域,因此前面所说的全局变量位于全局名称空间内。任何名称空间内的名称不会与另外一个名称空间中的名称发生冲突.名称空间中的声明和定义规则和全局命名规则类似,(类似于文件夹与子文件夹)

名称空间是开放的,可以把已有的名称并入原有的名称空间,例如

namespace A {
    int a;
    int b;
    int c;
    void fetch();
}
// ...
namespace A {
    int d;// 将d并入原来的A名称空间,也就是说可以分开写
}
// ...
namespace A {
    // 为前面的函数原型提供定义
    void fetch(){
        //...
    }
}
namespace A {
    namespace B {
        int a;
    }
}

如果要访问名称空间中的名称最简单的办法是使用作用域解析符(:😃.

A::a = 1;
A::b = 0;
A::B::a = 0; // 解析多层名称结构

包含名称空间的名称称为限定的空间,不包含的成为未限定的名称。

using声明和using编译指令

我们并不希望每次使用名称都对其进行限定,C++提供了两种机制来简化对名称空间的名称的使用。using声明使得特定的标识符可用,using编译指令使得整个名称空间可用。

using声明被限定的名称和他前面的关键词using组成

namespace A {
    int a;
}
using A::a;

using声明将特定的名称添加到他所属的声明区域中,如果在main函数中声明也可以使用a代替A::a.

由于using声明的作用域为其所在代码块所以也存在局部变量覆盖全局变量如下

int a;
int main(){
	using A::a;
    cin >> a; // A::a
    cin >> ::a // 全局变量a
}

using编译指令由名称空间名和他前面的关键字 using namespace他使得该名称空间的所有名称可用不需要作用域解析符号,作用域也为其所在代码块。

using namespace A; // 全局可用
//...
{
    using namespace A; // 代码块可用
    //...
}

使用using声明和编译指令增加了名称冲突的可能性。

using声明和using编译指令的比较

使用using编译指令倒入一个名称空间中所用名称与使用多个using声明是不一样的**。而更像是大量使用作用域解析运算符。使用using声明就好像声明了相应的类型,如果某个名称已经存在,则不能使用using声明。然而在使用using编译指令的情况将进行名称解析,就像包含了using声明和名称空间本省的最小声明区域中声明了名称一样。看实例

namespace Jill
{
  double bucket(double n);
  double fetch;
  struct Hill{};
}
char fetch; // 全局变量
#include <iostream>
int main()
{   
    using namespace std;
    using namespace Jill;
    Hill Thrill; // == Jill:Thirill
    double water = bucket(2); // Jill:bucket(2)
    double fetch; // 隐藏了Jill::fetcj
    cin >> fetch; // == 局部 fecth
    cin >> ::fetch; // == 全局 fetch
    cin >> Jill::fetch; // Jill::fetch
}

虽然编译指令将名称空间的名称是为在函数之外声明的(因为在函数内声明的变量可以覆盖它们)但是它并不能让该文件的其他函数能够使用这个名称。

假设名称空间和声明区域定义了相同的名称,如果试图使用using声明将名称空间的名称导入该声明区域,会导致发生出错,如果使用
using编译指令将该名称空间的名称那么局部名称会隐藏名称空间版本

一般来说使用using声明指令会比使用using编译指令安全,这是因为他只导入了特定的名称,如果改名与局部名称发生冲突,编译器将发出指示,using编译指令导入所有名称,可能包含并不需要名称,如果发生冲突,局部变量将会覆盖名称空间版本,编译器并不会发出警告,(这可能导致及其隐蔽的错误)

// 不要这样做
using namespace std;
// 应该这样做
int x;
std::cin >> x;
std::cout << x << std::endl;
// 或者如下
using std::cin;
using std::cout;
using std::endl;
cin >> x;
cout << x << endl;
其他特性

名称空间可以嵌套

namespace A{
    namespace B{
        namespace C{
            int d;
        }
    }
}

// 解析 连续使用:: 解析
A::B::D::d = 1;

// using 也可以嵌套
using A::B:C::d;
d = 1; //== A::B::D::d = 1;
using namespace A::B::C;
d = 1;

也可以在名称空间使用using声明和using编译指令

namespace A{
    int a;
}
namespace B{
    using namespace A;
    using A::a
}

// 解析a
A::a;
B::a;

名称空间别名

namespace A{
    namespace B{
        namespace C{
            namespace D{
                //...
            }
        }
    }
}
using sub_D = A::B::C::D;
//就可以使用 sub_b 来替换 A::B::C::D;

未命名名称空间

这就像后面跟着using编译指令,该名称空间潜在作用区域为从声明点到声明区域末尾,在这个方面他与全局变量类似,但是由于缺少名称,因此不能显示的使用using编译指令或者using声明来使他在其余位置可用,具体的来说不能在未命名名称空间属于文件之外的文件中来使用该名称空间中的名称,这提供了链接性为内部的静态变量的替代品。

static int counts;
int other();
int main(){
    //...
}

//等价于

namespace {
    int counts;
}
int other();
int main(){
    //...
}

名称空间实例

//main.cpp
#include "namesp.h" // 导入头文件
#include <iostream>
void other();
void another();

int main(){
    using debets::Debt; // 使用作用域解析
    Debt golf{{"Llonvne","Lcosvle"},100};
    debets::showDebt(golf);
    other();
    another();
    return 0;
}

void other(){
    using std::cin;
    using std::cout;
    using std::endl;
    using namespace debets;
    Person dg{"Li","Hua"};
    showPerson(dg);
    cout << endl;
    Debt zippy[3];
    for (auto & i : zippy) {
        getDebt(i);
    }
    for (const auto& i : zippy){
        showDebt(i);
    }
    cout << "total debts" << sumDebt(zippy,3);
}

void another(){
    using pers::Person;
    Person collector = {"Millo","Right"};
    pers::showPerson(collector);
    std::cout << std::endl;
}

// namesp.h
#ifndef NAMESP_H
#define NAMESP_H

#include <string>
namespace pers
{
  using std::string;
  struct Person
  {
      string fname;
      string lname;
  };
  void getPerson(Person & person);
  void showPerson(const Person & person);
}

namespace debets
{
  using namespace pers;
  struct Debt
  {
      Person name;
      double amount{};
  };
  void getDebt(Debt & debt);
  void showDebt(const Debt & debt);
  double sumDebt(const Debt arr[], int n);
}

#endif //NAMESP_H

// namesp.cpp
#include <iostream>
#include "namesp.h"

namespace pers
{
    using std::cin;
    using std::cout;
    using std::endl;


    void getPerson(Person & person)
    {
        cout << "Enter you first name" << endl;
        cin >> person.fname;
        cout << "Enter you last name" << endl;
        cin >> person.fname;
    }

    void showPerson(const Person & person)
    {
        cout << person.fname << " " << person.lname;
    }
}

namespace debets
{
    void getDebt(Debt & debt)
    {
        getPerson(debt.name);
        std::cout << "Enter Debts: ";
        std::cin >> debt.amount;
    }

    void showDebt(const Debt & debt)
    {
        showPerson(debt.name);
        std::cout << ": $" << debt.amount << std::endl;
    }

    double sumDebt(const Debt * arr, int n)
    {
        double total = 0;
        for (int i = 0; i < n; ++i) {
            total += arr[i].amount;
        }
        return total;
    }
}

名称空间及其前途

  1. 使用在已命名的名称空间中声明的变量而不是外部全局变量。

  2. 使用在已命名的名称空间中声明的变量而不是静态全局变量。

  3. 如果开发一个函数库或者类库,将其放在一个命名空间中,事实上C++现在提倡将标准函数放入std命名空间中,这种做法拓展到C语言库

  4. 仅将使用编译指令using作为一种权宜之计。

  5. 不要在头文件中使用using,这样做掩盖了让那些名称可用,另外,包含头文件的顺序可能影响程序,如果非要使用using编译指令将其放在所有#include文件后面。

  6. 导入名称首选作用域解析符或者using声明

  7. 对于using声明将其带作用域声明为局部而不是全局。

    别忘了使用名称空间的主旨是简化大型编程项目的管理工作对于只有一个文件的小程序使用using编译指令也不是什么大逆不道的事情。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值