【C++】深入理解作用域和命名空间:从基础到进阶详解

🦄个人主页:小米里的大麦-CSDN博客

🎏所属专栏:C++_小米里的大麦的博客-CSDN博客

🎁代码托管:C++: 探索C++编程精髓,打造高效代码仓库 (gitee.com)

**⚙️操作环境:**Visual Studio 2022

目录

一、前言

二、域的概念

[1. 类域](#1. 类域)

[2. 命名空间域](#2. 命名空间域)

[3. 局部域(联想局部变量)](#3. 局部域(联想局部变量))

[4. 全局域(联想全局变量)](#4. 全局域(联想全局变量))

[5. :: 域作用限定符](#5. :: 域作用限定符)

总结:

三、命名空间

[1. 命名空间的定义](#1. 命名空间的定义)

[2. 命名空间的使用](#2. 命名空间的使用)

[3. 更多使用示例及其注意事项!](#3. 更多使用示例及其注意事项!)

总结

共勉


一、前言

从C语言到现在的C++非常不容易,但是C++这块硬骨头还是要啃呐,C++也算是一门新语言,不过是和C语言有着一些渊源罢了,同样的,学习一门语言还是从最基础的开始,本文我们将详细介绍域(作用域)和命名空间的知识,接下来,车速太快,不要掉队哦!

二、域的概念

"域" (也可以理解为作用域)是指变量和标识符可见或有效的范围。C++中有几种常见的作用域,下面是关于域的详细解释:

1. 类域

  • 解释:类域是类中定义的成员变量或成员函数的作用范围。类中的成员变量只有在类的对象实例化后才能使用,成员函数可以访问类的成员变量。类域的变量可以通过对象或者类名来访问。

  • 代码例子

cpp 复制代码
class MyClass {
public:
    int classVar = 10;  // 类域中的成员变量
};

2. 命名空间域

  • 解释 :命名空间域是指用 namespace 声明的作用域。命名空间用于组织代码,避免命名冲突。命名空间中的变量或函数通过 ::(作用域解析运算符)进行访问。

  • 代码例子

cpp 复制代码
namespace MyNamespace {
    int value = 100;  // 命名空间域中的变量
}

int main() {
    std::cout << MyNamespace::value << std::endl;  // 使用命名空间域中的变量
}

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

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
int rand = 10;
// C语言没办法解决类似这样的命名冲突问题,所以C++提出了namespace来解决
int main()
{
	printf("%d\n", rand);
	return 0;
}
解释:
编译错误 error C2365: "rand": 重定义;
以前的定义是"函数" 是因为标准库中已经有一个名为 rand 的函数,
而你在代码中又定义了一个同名的全局变量 int rand = 10;,导致了命名冲突。

在C语言中,没有命名空间的机制来区分相同名称的变量和函数,
因此如果你定义的标识符和标准库中的某个标识符相同,就会出现冲突,导致编译报错。

标准库中的 rand 函数用于生成随机数,定义在 <stdlib.h> 头文件中。
当你在全局作用域中定义一个 int rand,编译器无法区分你定义的变量和标准库的 rand 函数,
因此会认为这是重定义,从而报错。

3. 局部域(联想局部变量)

  • 解释 :局部域指的是在函数或代码块(如 ifforwhile 等)内部定义的变量的作用范围。局部变量只在定义它的代码块中有效,出了这个范围后,变量就无法被访问。

  • 代码解释 :在 main 函数中,定义了 int a = 1;,这是局部变量 a,它的作用范围仅限于 main 函数内。

cpp 复制代码
int main() {
    int a = 1;  // 这是局部变量,局部域
    printf("%d\n", a);  // 输出局部变量 a 的值:1
}

还有一个知识点:**当全局变量和局部变量冲突时,局部变量优先原则!**举个例子吧:

cpp 复制代码
#include <iostream>
using namespace std;

int a = 10;
int main()
{
	int a = 20;
	cout << a << endl;

	return 0;
}
当全局变量a和局部变量a发生冲突,则会输出局部变量a的值20.

4. 全局域(联想全局变量)

  • 解释 :全局域指的是定义在所有函数或类之外的变量。全局变量可以在整个程序中访问,任何函数或类都能使用它(除非被 static 修饰)。

  • 代码解释int a = 0; 定义在 main 函数外面,它是一个全局变量,作用范围是整个程序。

cpp 复制代码
int a = 0;  // 全局变量,全局域

5. :: 域作用限定符

  • 解释:: 是作用域解析运算符,用于区分不同域中的同名变量。例如,当局部域和全局域中都有一个相同名字的变量时,使用 :: 可以访问全局变量,而不用 :: 访问的就是局部变量。
cpp 复制代码
int a = 0;  // 全局变量

int main() {
    int a = 1;  // 局部变量
    printf("%d\n", a);    // 输出局部变量 a 的值:1
    printf("%d\n", ::a);  // 输出全局变量 a 的值:0
}

总结:

  • 类域:类中的成员变量和函数作用范围。
  • 命名空间域:命名空间中的变量和函数作用范围。
  • 局部域:函数或代码块内部变量的作用范围。
  • 全局域:全局变量的作用范围,整个程序中有效。
  • ::域作用限定符:用于区分全局域和局部域中的同名变量。

三、命名空间

上面,我们已经初步了解了域的概念,下面,我们着重讲解一下命名空间:

1. 命名空间的定义

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

cpp 复制代码
// hcc是命名空间的名字,一般开发中是用项目名字做命名空间名。
// 博主在这里使用的是自己名字缩写,当然命名方式多种多样,如张三:zs
// 1. 正常的命名空间定义
namespace hcc
{
	// 命名空间中可以定义变量/函数/类型
	int rand = 10;
	int Add(int left, int right)
	{
		return left + right;
	}

	struct Node
	{
		struct Node* next;
		int val;
	};
}
//2. 命名空间可以嵌套
// test.cpp
namespace N1
{
	int a;
	int b;
	int Add(int left, int right)
	{
		return left + right;
	}
	namespace N2
	{
		int c;
		int d;
		int Sub(int left, int right)
		{
			return left - right;
		}
	}
}
//3. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
// ps:一个工程中的test.h和上面test.cpp中两个N1会被合并成一个
// test.h
namespace N1
{
	int Mul(int left, int right)
	{
		return left * right;
	}
}

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

2. 命名空间的使用

cpp 复制代码
#include <stdio.h>
namespace hcc
{
	// 命名空间中可以定义变量/函数/类型
	int a = 0;
	int b = 1;
	int Add(int left, int right)
	{
		return left + right;
	}
	struct Node
	{
		struct Node* next;
		int val;
	};
}
int main()
{
	printf("%d\n", hcc::a);//正确写法
	
	printf("%d\n", a);// 编译报错:error C2065: "a": 未声明的标识符
	return 0;
}

代码示例:

cpp 复制代码
1. 使用命名空间名称及作用域限定符
在这种方式下,直接使用 N:: 来限定命名空间中的成员。
#include <iostream>
namespace N {
    int a = 10;
}

int main() {
    std::cout << N::a << std::endl;  // 输出 N 命名空间中的 a
    return 0;
}

输出:10
cpp 复制代码
2. 使用 using 将命名空间中的某个成员引入
使用 using N::b 只引入命名空间 N 中的特定成员 b,而非整个命名空间。
#include <iostream>
namespace N {
    int a = 10;
    int b = 20;
}

int main() {
    std::cout << N::a << std::endl;  // 仍然需要使用作用域来访问 a
    using N::b;
    std::cout << b << std::endl;     // 直接访问 b
    return 0;
}
输出:
10
20
cpp 复制代码
3. 使用 using namespace 引入整个命名空间
使用 using namespace N; 引入整个命名空间 N,可以直接访问所有成员。
#include <iostream>
namespace N {
    int a = 10;
    int b = 20;
    int Add(int x, int y) {
        return x + y;
    }
}

int main() {
    using namespace N;
    std::cout << a << std::endl;       // 直接访问 a
    std::cout << b << std::endl;       // 直接访问 b
    std::cout << Add(10, 20) << std::endl;  // 直接调用 Add 函数
    return 0;
}
输出:
10
20
30

3. 更多使用示例及其注意事项!

访问嵌套命名空间的变量需要用::分隔符!!(类比于结构体嵌套访问)。

cpp 复制代码
#include <stdio.h>
namespace N1
{
	int a;
	int b;
	int Add(int left, int right)
	{
		return left + right;
	}
	namespace N2
	{
		int c;
		int d;
		int Sub(int left, int right)
		{
			return left - right;
		}
	}
}
int main()
{
	printf("N1::a = %d\n", N1::a);
	printf("N1::c = %d\n", N1::N2::c);//注意访问嵌套命名空间的变量需要用::分隔符
	printf("N1::d = %d\n", N1::N2::d);//先访问N1::N2再访问d

	return 0;
}
  • using namespace std;
  • 直接展开会有风险:我们定义如果跟库重名,就会报错!
  • 建议项目里面不要去展开,建议日常练习可以随意展开使用。
  • 项目建议指定访问,不要轻易展开命名空间!
cpp 复制代码
平时也可以选择展开某个:把常用展开,例如:
using std::cout;
using std::endl;
这样就可以正常使用cout和endl了

域的"优先级":局部域->全局域 -> 展开了命名空间域 or 指定访问命名空间域

  • 通常是局部域->全局域,默认不会去命名空间域搜索,展开了命名空间域 or 指定访问命名空间域。这样就构成了局部域->全局域 -> 展开了命名空间域 or 指定访问命名空间域的"优先级"。
cpp 复制代码
int a = 20;

namespace hcc
{
	int a = 30;
}

int main()
{
	int a = 10;

	printf("%d\n", a);
	//此时打印的值是10

	return 0;
}
cpp 复制代码
int a = 20;

namespace hcc
{
	int a = 30;
}

int main()
{
	printf("%d\n", a);
	//没了局部变量a,打印的值就会变成20
    //但是,如果全局变量a也没了,就会报错:未定义的标识符

	return 0;
}
cpp 复制代码
那么,我们该如何访问命名空间域的量呢?
展开了命名空间域 or 指定访问命名空间域

namespace hcc
{
	int a = 30;
}

int main()
{
	printf("%d\n", ::a);//展开了命名空间域
	printf("%d\n", hcc::a);//指定访问命名空间域

	return 0;
}

总结

C++ 中的作用域和命名空间是保证程序结构清晰、避免命名冲突的重要机制。从局部域、全局域到命名空间的使用,不同层次的作用域控制让我们在编写复杂项目时能够保持代码的可读性与可维护性。通过命名空间,我们不仅能更好地组织代码,还能提升代码的可扩展性和复用性。在理解了这些概念之后,我们可以更加自如地在项目中运用它们,从而编写出高效、稳定的 C++ 程序。掌握这些基础知识也是深入学习 C++ 高级特性的重要一步。

共勉

相关推荐
m0_6312704035 分钟前
标准C++(二)
开发语言·c++·算法
沫刃起38 分钟前
Codeforces Round 972 (Div. 2) C. Lazy Narek
数据结构·c++·算法
GZM8888882 小时前
配置VS Code以进行C/C++编程:深入探讨与实操指南
c++
martian6652 小时前
学懂C++(六十):C++ 11、C++ 14、C++ 17、C++ 20新特性大总结(万字详解大全)
开发语言·c++·c++20
小灰灰爱代码3 小时前
C++——判断year是不是闰年。
数据结构·c++·算法
小灰灰爱代码3 小时前
C++——求3个数中最大的数(分别考虑整数、双精度数、长整数数的情况),用函数重载方法。
数据结构·c++·算法
handsome2134 小时前
WSL中使用GPU加速AMBER MD--测试
笔记·学习
爱coding的橙子4 小时前
CCF-CSP认证考试准备第十七天
数据结构·c++·算法
程序猿阿伟5 小时前
《C++移动语义:解锁复杂数据结构的高效之道》
数据结构·c++·html
594h26 小时前
PAT 甲级 1002题
数据结构·c++·算法