【C++】C++入门 -- 命名空间

🫧个人主页: 小年糕是糕手
💫个人专栏:《C++》《数据结构(初阶)》《C/C++刷题集》《C语言》
🎨你不能左右天气,但你可以改变心情;你不能改变过去,但你可以决定未来!

目录
一、前言
从这篇博客开始我将开始更新C++有关内容,后面会同步更新Linux,相信大家总是在网上看到C++难学的消息,从而有了胆怯的心理,当然我们不去否认C++确实对新手没有java和python这俩门语言友好,所以在这篇博客开始之前希望给大家一些激励:
这里引用浙江大学的翁恺老师在我们初学C语言时候告诉过我们的一句话:“学计算机一定要有一个非常强大的心理状态,计算机的所有东西都是人做出来的,别人能想得出来,我也一定能想出来,在计算机里头没有任何黑魔法,所有的东西只不过是我现在不知道而已,总有一天我会把所有的细节,所有的内部的东西全都搞明白!”
1.1、书籍推荐

- C++ Primer:主要讲解语法,经典的语法书籍,前中后期都可以看,前期如果自学看可能会有点晦涩难懂,能看懂多少看懂多少,就当预习,中后期作为语法字典,非常好用。
- STL 源码剖析:主要从底层实现的角度结合 STL 源码,庖丁解牛式剖析 STL 的实现,是侯捷老师的经典之作。可以很好的帮助我们学习别人用语法是如何实现出高效简洁的数据结构和算法代码,如何使用泛型封装等。让我们不再坐井观天,闭门造车,本书课程上一半以后,中后期可以看。
- Effective C++:本书也是侯捷老师翻译的,本书有的一句评价,把 C++ 程序员分为看过此书的和没看过此书的。本书主要讲了 55 个如何正确高效使用 C++ 的条款,建议中后期可以看一遍,工作 1-2 年后再看一遍,相信会有不一样的收获。
1.2、C++参考文档
https://legacy.cplusplus.com/reference/
https://en.cppreference.com/w/
说明:第一个链接不是C++官方文档,标准也只更新到C++11,但是以头文件形式呈现,内容比较易看好懂。后一个链接是C++官方文档,信息很全,更新到了最新的C++标准,但是相比第一个不那么易看;几个文档各有优势,我们结合着使用。
二、第一个C++程序
大家还记得我们在C语言中写的第一个程序吗?
#include<stdio.h>
int mian()
{
printf("Hello World\n");
return 0;
}
这里是否换起了大家的回忆、大家对编程学习的初衷,大家追求的诗和远方就在前方,我们一起砥砺前行,共达山巅!
我们这里用C++给大家实现:
#include<iostream>
using namespace std;
int main()
{
cout << "hello world\n" << endl;
}
这里的std、cout等我们都看不懂,没关系,下面我会依次为大家进行讲解。接下来让我们共同进入C++的学习吧。
三、命名空间
3.1、namespace
3.1.1、namespace的价值
在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
C语言项目类似下面程序这样的命名冲突是普遍存在的问题,C++引入namespace就是为了更好的解决这样的问题:
#include <stdio.h>
#include <stdlib.h>
int rand = 10;
int main()
{
// 编译报错:error C2365: “rand”: 重定义;以前的定义是“函数”
printf("%d\n", rand);
return 0;
}
通俗的来说就是#include<stdlib.h>头文件中包含了rand,这时候我们再去命名一个变量rand就会发生重复导致报错。
3.1.2、namespace的定义
- 定义命名空间,需要使用到
namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。命名空间中可以定义变量 / 函数 / 类型等。namespace本质是定义出一个域,这个域跟全局域各自独立,不同的域可以定义同名变量,所以下面的rand不在冲突了。- C++ 中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找一个变量 / 函数 / 类型出处 (声明或定义) 的逻辑,所有有了域隔离,名字冲突就解决了。局部域和全局域除了会影响编译查找逻辑,还会影响变量的生命周期,命名空间域和类域不影响变量生命周期。
namespace只能定义在全局,当然他还可以嵌套定义。- 项目工程中多文件中定义的同名
namespace会认为是一个namespace,不会冲突。- C++ 标准库都放在一个叫
std(standard) 的命名空间中。
3.1.2.1、正常的命名空间定义
#include<stdio.h>
#include<stdlib.h>
namespace bit
{
// 命名空间中可以定义变量/函数/类型
int rand = 10;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
//我们在C语言中学过编译器查找确认,默认先局部查找->全局查找->没有找到就报错未声明的标识符
//那有没有什么办法可以让我们直接访问全局呢?
int a = 1;
int main()
{
int a = 0;
printf("%d\n", a);
// ::域作用限定符
//域作用限定符左边啥都不加默认全局
printf("%d\n", ::a);
//这里我们访问的是<stdlib.h>头文件中的rand
printf("%p\n", rand);
//我们要是想访问namespace中的,需要使用::
//指定作用域,就直接去这个域查找->没有找到就报错未声明的标识符
printf("%d\n", bit::rand);
printf("%p\n", bit::Add);
struct bit::Node node;
return 0;
}
这里我们学习到了一个新的操作符:"::",这是C++中的域作用限定符,可以帮助我们精准定位需要访问的对象。
3.1.2.2、命名空间可以嵌套
我们通过namespace定义了一个新的空间,空间里可以是变量/函数/类型,那我们肯定会想知道namespace里可不可以定义namespace呢,答案是可以的,而且操作起来也十分简单。
#include<stdio.h>
#include<stdlib.h>
namespace Byte
{
namespace bit1
{
int rand = 1;
int Add(int left, int right)
{
return left + right;
}
}
namespace bit2
{
int rand = 2;
int Add(int left, int right)
{
return (left + right) * 10;
}
}
}
int main()
{
printf("%d\n", Byte::bit1::rand);
printf("%d\n", Byte::bit2::rand);
printf("%d\n", Byte::bit1::Add(1, 2));
printf("%d\n", Byte::bit2::Add(1, 2));
return 0;
}
3.1.2.3、不同文件的namespace会合并
多文件中可以定义同名namespace,他们会默认合并到一起,就像同一个namespace一样
文件一:
namespace MySpace {
int a = 10;
}
文件二:
namespace MySpace {
void func() {
// 可以直接访问同一命名空间中的a
std::cout << a << std::endl;
}
}
编译器会将这两个
MySpace视为同一个命名空间,因此func()可以直接访问a,无需额外声明。这种特性在多文件项目中非常实用,允许我们将一个命名空间的成员拆分到不同文件中管理,同时保持逻辑上的统一性。
3.2、命名空间使用
编译查找一个变量的声明 / 定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。所以下面程序会编译报错。所以我们要使用命名空间中定义的变量 / 函数,有三种方式:
- 指定命名空间访问,项目中推荐这种方式。
- using 将命名空间中某个成员展开,项目中经常访问的不存在冲突的成员推荐这种方式。
- 展开命名空间中全部成员,项目不推荐,冲突风险很大,日常小练习程序为了方便推荐使用。
#include<iostream>
//全部指定
//using namespace std;
//部分展开
using std::cout;
using std::endl;
int main()
{
int i = 100;
//这是我们在C语言阶段学过的左移
i << 5;
double d = 1.1;
//这里就是流插入,任何变量,将i转换成字符串,插入到流中
//他的最大特点就是自动识别类型
std::cout << i;
std::cout << "hello world";
//可以连续使用
std::cout << i << " " << d;
//换行--俩种
std::cout << i << '\n' << "\n";
std::cout << "hello world" << std::endl;// end line
//输入并输出
std::cin >> i >> d;
std::cout << i << " " << d << std::endl;
//如果我们不想每句话都加std::
//我们可以加上:using namespace std;
//缺点是我们不可以定义和库中相同的变量cout等
return 0;
}
在 C++ 中,“部分展开” 指的是通过
using语句只将命名空间中的特定成员引入到当前作用域,而不是展开整个命名空间。以代码中的
using std::cout;和using std::endl;为例:
- 这两条语句的作用是,将标准命名空间
std中的cout(输出流对象)和endl(换行操作符)单独引入当前作用域。- 这样在后续代码中,我们可以直接使用
cout和endl,而不需要每次都写std::cout和std::endl;但对于std命名空间中的其他成员(如cin),仍然需要通过std::cin的方式访问。这种方式的优势是平衡了代码简洁性和命名冲突风险:既减少了部分常用成员的书写复杂度,又避免了因展开整个命名空间(如
using namespace std;)而可能引发的大量名字冲突问题,因此在项目开发中较为推荐。
更多推荐


所有评论(0)