【C++的奇迹之旅(二)】C++关键字&&命名空间使用的三种方式&&C++输入&输出&&命名空间std的使用惯例

简介: 【C++的奇迹之旅(二)】C++关键字&&命名空间使用的三种方式&&C++输入&输出&&命名空间std的使用惯例

📝前言

C++是在C的基础之上,容纳进去了面向对象编程思想,并增加了许多有用的库,以及编程范式

等。熟悉C语言之后,对C++学习有一定的帮助,本章节主要目标:

  1. 补充C语言语法的不足,以及C++是如何对C语言设计不合理的地方进行优化的,比如:作用
    域方面、IO方面、函数方面、指针方面、宏方面等。
  2. 为后续类和对象学习打基础。

🌠 C++关键字(C++98)

C++总计63个关键字,C语言32个关键字

ps:下面我们只是看一下C++有多少关键字,不对关键字进行具体的讲解。后面我们学到以后再

细讲。

语言的发展就像是练功打怪升级一样,也是逐步递进,由浅入深的过程。我们先来看下C++的历史版本。

1712453188169.png

🌉 命名空间

C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。

🌠命名空间定义

定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}

中即为命名空间的成员。

 1. 正确的命名空间定义
//正确的命名空间定义
namespace Asen
{
  //命名空空间中可以定义变量/函数/类型
  int rand = 10;
  
  int Add(int begin, int end)
  {
    return begin + end;
  }

  struct Node
  {
    struct Node* next;
    int data;
  };
}
  1. 命名空间可以嵌套
namespace Asen
{
  int a;
  int b;
  
  int Add(int left, int right)
  {
    return left + right;
  }

  namespace needs_confidence
  {
    int Sub(int begin, int end)
    {
      return begin - end;
    }
  }
}
  1. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
    如:一个工程中的test.h和上面test.cpp中两个asen会被合并成一个

注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中

🌉命名空间使用

命名空间中成员该如何使用呢?比如以下代码运行:

namespace Asen
{
  int a = 0;
  int b = 1;
  
  int Add(int left, int right)
  {
    return left + right;
  }

  namespace needs_confidence
  {
    int Sub(int begin, int end)
    {
      return begin - end;
    }
  }
}


int main()
{
  printf("%d\n", a);
  return 0;
}

运行截图:

🌠命名空间的使用有三种方式:

🌉加命名空间名称及作用域限定符

int main()
{
  printf("%d\n", Asen::a);
  return 0;
}

🌠使用using将命名空间中某个成员引入

using Asen::b;
int main()
{
  printf("%d\n", Asen::a);
  printf("%d\n", b);
  return 0;
}

🌉 使用using namespace 命名空间名称 引入

using namespace Asen;
int main()
{
  printf("%d\n", Asen::a);
  printf("%d\n", b);
  Add(66, 88);
  return 0;
}

🌠命名空间的查找先后顺序

一. 命名空间的查找顺序是:


当前命名空间 > 父命名空间 > 全局命名空间 > 模块命名空间 > 内置模块命名空间


从最具体的当前命名空间开始向上级别进行查找,一直到全局和内置模块命名空间,以此来解决可能出现的同名变量和函数的命名冲突问题。


具体规则如下:


  1. 当前命名空间

首先在当前命名空间中查找。

  1. 父命名空间

如果当前命名空间没有找到,则查找其直接父命名空间。

  1. 嵌套的父命名空间

如果直接父命名空间也没有找到,则继续向上查找更高层次的父命名空间。

  1. 全局命名空间

如果所有父命名空间都没有找到,则最后在全局命名空间中查找。

  1. 导入的命名空间

如果使用了using指令导入其他命名空间,也会在导入的命名空间中查找。

举个例子:

namespace A 
{
  void func() 
  {
    // do something 
  }
}

namespace B 
{
    namespace C 
   {
       void func() 
       {
         // do something
       } 
   }

   using namespace A;

    void test() 
   {
       func(); // 会调用B::C::func()
     }
}

这里B命名空间中的test函数,首先在B命名空间中查找func,没有找到,然后去B的子命名空间C中查找,找到就调用C中的func。


简单总结

编译默认查找

a、当前局部域 : 自留地

b、全局域找 : 村子野地

c、到展开的命名空间中查找 : 相当于张大爷在自己的自留地加了声明,谁需要就来摘

二. 以下是有关命名空间的查找使用实例:

namespace Asen
{
  namespace needs_confi
  {
    int rand = 0;
     
    int Add(int left, int right)
    {
      return left + right;
    }

    struct Node
    {
      struct Node* next;
      int data;
    };
  }
}

namespace needs_confi
{
  int rand = 0;

  int Add(int left, int right)
  {
    return (left + right) * 10;
  }
}

int main()
{
  printf("%p\n", rand);
  printf("%d\n", Asen::needs_confi::rand);

  printf("hello world\n");

  printf("%d\n", Asen::needs_confi::Add(2, 3));
  printf("%d\n", needs_confi::Add(2, 3));

  struct Asen::needs_confi::Node pnode;
}

三· 展开命名空间暴露问题:

展开命名空间可能会导致的主要问题如下:

1.名称冲突

当使用using namespace将一个命名空间中的所有名称导入到当前作用域时,如果导入的命名空间和当前命名空间存在同名的变量/函数等,就会产生名称冲突,编译或运行时可能会出现错误。

2.污染全局作用域

使用using namespace后,导入的所有名称都会暴露到全局作用域,可能会与其他代码产生冲突,也更难追踪是哪个命名空间中的名称。

3.难以区分来源

如果直接使用using namespace,在代码中看到一个名称就不知道它来自哪个具体的命名空间。这给代码维护和调试带来困难。

4.性能影响

使用using namespace后,编译器需要在更广泛的作用域中查找名称,这可能会影响编译效率和程序性能。

5.依赖隐藏

使用using namespace可能会隐藏某些依赖关系,例如标准库名称可能会屏蔽用户自定义的同名名称。

6.难以控制

直接使用using namespace没有办法精细控制导入的范围,无法选择性导入某些名称。

因此一般来说,不推荐在头文件中使用using namespace,在源文件中使用也应谨慎。建议直接使用具体的命名空间限定名称。


如:这里我包含了两个头文件,其中这个#include"Stack.h"的内容如下,这里主要是用namespace 空间定义,然后展开namespace ,目的是为了看两个命名空间都有相同的内容,在查找时看看会先用哪个?还是会编译报错:

#include"Queue.h"
#include"Stack.h"

namespace xjh
{
  typedef struct Stack
  {
  }ST;

  void STInit(ST* ps)
  {}


  struct Queue
  {
    //...
  };
}

// 展开命名空间 
using namespace ahui;
using namespace xjh;

// 编译默认查找
// a、当前局部域        : 自留地
// b、全局域找       : 村子野地
// c、到展开的命名空间中查找  : 相当于张大爷在自己的自留地加了声明,谁需要就来摘

int main()
{
  struct Stack st1;
  STInit(&st1);
  printf("%d\n", sizeof(st1));

  ahui::ST st;
  printf("%d\n", sizeof(st));

  STInit(&st);
  STPush(&st, 1);
  STPush(&st, 2);
  STPush(&st, 3);
  STPush(&st, 4);

  return 0;
}

代码运行:

🌉 C++输入&输出

和我们刚学C语言时,学习了printf和scanf来进行输出和输入,C++同样也有输入和输出,我们来看下C++是如何来实现问候的。

#include<iostream>
// std是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中
using namespace std;
int main()
{
  cout<<"Hello world!!!"<<endl;
  return 0;
}

运行图:

说明:

  1. 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件

以及按命名空间使用方法使用std。

  1. cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含<

iostream >头文件中。

  1. <<是流插入运算符,>>是流提取运算符。
  2. 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。
  3. C++的输入输出可以自动识别变量类型。

实际上cout和cin分别是ostream和istream类型的对象,>>和<<也涉及运算符重载等知识,

这些知识我们我们后续才会学习,所以我们这里只是简单学习他们的使用。后面我们还有有

一个章节更深入的学习IO流用法及原理。

注意:早期标准库将所有功能在全局域中实现,声明在.h后缀的头文件中,使用时只需包含对应

头文件即可,后来将其实现在std命名空间下,为了和C头文件区分,也为了正确使用命名空间,

规定C++头文件不带.h;旧编译器(vc 6.0)中还支持<iostream.h>格式,后续编译器已不支持,因

此推荐使用+std的方式。

#include <iostream>
using namespace std;
int main()
{
   int a;
   double b;
   char c;
     
   // 可以自动识别变量的类型
   cin>>a;
   cin>>b>>c;
     
   cout<<a<<endl;
   cout<<b<<" "<<c<<endl;
   return 0;
}

>注意:关于coutcin还有很多更复杂的用法,比如控制浮点数输出精度,控制整形输出进制格式等

等。因为C++兼容C语言的用法,这些又用得不是很多,我们这里就不展开学习了。后续如果有需要,我

们再学习。

🌠std命名空间的使用惯例

stdC++标准库的命名空间,如何展开std使用更合理呢?

  1. 在日常练习中,建议直接using namespace std即可,这样就很方便。
  2. using namespace std展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型/对

象/函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模

大,就很容易出现。所以建议在项目开发中使用,像std::cout这样使用时指定命名空间 +

using std::cout展开常用的库对象/类型等方式。


🚩总结

感谢你的收看,如果文章有错误,可以指出,我不胜感激,让我们一起学习交流,如果文章可以给你一个小小帮助,感谢💓 💗 💕 💞,喜欢的话可以点个关注,也可以给博主点一个小小的赞😘呀

相关文章
|
1月前
|
安全 程序员 编译器
【C++】如何巧妙运用C++命名空间:初学者必备指南
【C++】如何巧妙运用C++命名空间:初学者必备指南
|
2月前
|
安全 C++
C++: std::once_flag 和 std::call_once
`std::once_flag` 和 `std::call_once` 是 C++11 引入的同步原语,确保某个函数在多线程环境中仅执行一次。
|
3月前
|
程序员 C++ 开发者
C++命名空间揭秘:一招解决全局冲突,让你的代码模块化战斗值飙升!
【8月更文挑战第22天】在C++中,命名空间是解决命名冲突的关键机制,它帮助开发者组织代码并提升可维护性。本文通过一个图形库开发案例,展示了如何利用命名空间避免圆形和矩形类间的命名冲突。通过定义和实现这些类,并在主函数中使用命名空间创建对象及调用方法,我们不仅解决了冲突问题,还提高了代码的模块化程度和组织结构。这为实际项目开发提供了宝贵的参考经验。
61 2
|
2月前
|
程序员 C++ 容器
C++编程基础:命名空间、输入输出与默认参数
命名空间、输入输出和函数默认参数是C++编程中的基础概念。合理地使用这些特性能够使代码更加清晰、模块化和易于管理。理解并掌握这些基础知识,对于每一个C++程序员来说都是非常重要的。通过上述介绍和示例,希望能够帮助你更好地理解和运用这些C++的基础特性。
41 0
|
3月前
|
编译器 C语言 C++
C++入门 | 命名空间、输入输出、缺省参数
C++入门 | 命名空间、输入输出、缺省参数
45 4
|
2月前
|
C语言 C++
C++(六)Namespace 命名空间
命名空间(Namespace)是为了解决大型项目中命名冲突而引入的机制。在多库集成时,不同类库可能包含同名函数或变量,导致冲突。C++通过语法形式定义了全局无名命名空间,并允许对全局函数和变量进行作用域划分。命名空间支持嵌套与合并,便于协同开发。其使用需谨慎处理同名冲突。
|
4月前
|
存储 C++ 运维
开发与运维函数问题之使用C++标准库中的std::function来简化回调函数的使用如何解决
开发与运维函数问题之使用C++标准库中的std::function来简化回调函数的使用如何解决
52 6
|
4月前
|
C++ 运维
开发与运维编译问题之在C++中在使用std::mutex后能自动释放锁如何解决
开发与运维编译问题之在C++中在使用std::mutex后能自动释放锁如何解决
68 2
|
4月前
|
安全 编译器 C++
C++一分钟之-C++中的属性命名空间
【7月更文挑战第22天】C++11引入属性作为元数据,虽无内置属性命名空间,但可通过自定义属性与命名空间组合实现类似效果。例如,创建`perf`命名空间存放`slow`和`fast`属性来标记函数性能。正确使用属性需注意位置、避免重复和确保与实现一致,以提升代码可读性和编译器理解。通过模拟属性命名空间,可以更有效地管理和使用属性。
44 1
|
4月前
|
存储 安全 编译器
【C++入门 四】学习C++内联函数 | auto关键字 | 基于范围的for循环(C++11) | 指针空值nullptr(C++11)
【C++入门 四】学习C++内联函数 | auto关键字 | 基于范围的for循环(C++11) | 指针空值nullptr(C++11)