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

news2024/9/21 12:41:50

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

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

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

⚙️操作环境:Visual Studio 2022

目录

一、前言

二、域的概念

1. 类域

2. 命名空间域

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

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

5. :: 域作用限定符

总结:

三、命名空间

1. 命名空间的定义

2. 命名空间的使用

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

总结 

共勉


一、前言

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

二、域的概念

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

1. 类域

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

  • 代码例子

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

2. 命名空间域

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

  • 代码例子

namespace MyNamespace {
    int value = 100;  // 命名空间域中的变量
}

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

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

#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 函数内。

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

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

#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 函数外面,它是一个全局变量,作用范围是整个程序。

int a = 0;  // 全局变量,全局域

5. :: 域作用限定符

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

int a = 0;  // 全局变量

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

总结:

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

三、命名空间

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

1. 命名空间的定义

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

// 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. 命名空间的使用

#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;
}

代码示例: 

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

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

输出:10
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
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. 更多使用示例及其注意事项!

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

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

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

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

namespace hcc
{
	int a = 30;
}

int main()
{
	int a = 10;

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

	return 0;
}
int a = 20;

namespace hcc
{
	int a = 30;
}

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

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

namespace hcc
{
	int a = 30;
}

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

	return 0;
}

总结 

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

共勉

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2140300.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis——常用数据类型string

目录 常用数据结构&#xff08;类型&#xff09;Redis单线程模型Reids为啥效率这么高&#xff1f;速度这么快&#xff1f;&#xff08;参照于其他数据库&#xff09; stringsetgetMSET 和 MGETSETNX&#xff0c;SETEX&#xff0c;PSETEXincr&#xff0c;incrby&#xff0c;decr…

sshj使用代理连接服务器

之前我是用jsch连接服务器的&#xff0c;但是没办法使用私钥连接&#xff0c;搜了一下似乎是不支持新版的SSH-rsa&#xff0c;并且jsch很久没更新了&#xff0c;java - "com.jcraft.jsch.JSchException: Auth fail" with working passwords - Stack Overflow 没办法…

mybatis的基本使用与配置

注释很详细&#xff0c;直接上代码 项目结构 源码 UserMapper package com.amoorzheyu.mapper;import com.amoorzheyu.pojo.User; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select;import java.util.List;Mapper //在运行时生成代…

从数据仓库到数据中台再到数据飞轮:金融行业的数据技术进化史

前言​ 大家好&#xff0c;我是一名大数据开发工程师&#xff0c;在金融行业深耕多年&#xff0c;其实数据技术的演进不仅是技术层面的革新&#xff0c;更是业务模式与决策方式的深刻变革。从最开始的数据仓库兴起&#xff0c;到数据中台的普及&#xff0c;再到数据飞轮的出现…

MFEA/D-DRA--基于分解和动态资源分配的多目标多任务优化

MFEA/D-DRA–基于分解和动态资源分配的多目标多任务优化 title&#xff1a; A Multiobjective multifactorial optimization algorithm based on decomposition and dynamic resource allocation strategy author&#xff1a; Shuangshuang Yao, Zhiming Dong, Xianpeng Wang…

跨界融合,GIS如何赋能游戏商业——以《黑神话:悟空》为例

在数字化时代&#xff0c;地理信息系统&#xff08;GIS&#xff09;技术正以其独特的空间分析和可视化能力&#xff0c;为游戏产业带来革命性的变革。《黑神话&#xff1a;悟空》作为中国首款3A级别的动作角色扮演游戏&#xff0c;不仅在游戏设计和技术上取得了突破&#xff0c…

金融行业中如何利用数据中台的数据来有效的驱动业务决策呢?

前言​ 在金融行业中&#xff0c;利用数据中台的数据来有效驱动业务决策是一个复杂而关键的过程。其实我们的核心就是帮助金融机构最大化数据中台的价值&#xff0c;并推动业务决策的科学性和准确性。本文我从技术的角度来剖析一下这一过程。​ 什么是数据中台&#xff1f;​…

Git常用指令大全详解

Git常用指令大全详解 Git&#xff0c;作为目前最流行的分布式版本控制系统&#xff0c;其强大的功能和灵活性为开发者提供了极大的便利。无论是个人项目还是团队协作&#xff0c;Git都扮演着不可或缺的角色。本文将详细总结Git的常用指令&#xff0c;帮助大家更好地掌握这一工…

PHP:强大的Web开发语言

PHP&#xff1a;强大的Web开发语言 一、PHP 简介及优势 PHP 的基本概念 PHP&#xff08;PHP: Hypertext Preprocessor&#xff09;即 “超文本预处理器”&#xff0c;是一种通用开源脚本语言&#xff0c;最初由 Rasmus Lerdorf 于 1994 年创建。它可以在服务器上执行&#xf…

题目:单调栈

1、关于栈的概述 栈是一种数据结构&#xff0c;遵循“后进先出”&#xff08;LIFO, Last In, First Out&#xff09;的原则。这意味着最后被插入栈中的元素会最先被移除。可以把它想象成一个垒盘子的情况&#xff0c;新的盘子总是放在最上面&#xff0c;而最上面的盘子会最先被…

Matlab:科学计算与工程应用的强大利器

Matlab&#xff1a;强大的科学计算工具 一、Matlab 简介与重要性 Matlab 作为一款强大的科学计算软件&#xff0c;在工程、科学、数学等多个领域都有着广泛的应用及至关重要的地位。 在工程计算领域&#xff0c;它涵盖了众多方面。例如&#xff0c;线性代数与矩阵运算中&…

CODESYS资源使用表

1、CODESYS标准化编程之输入输出映射请参考下面文章链接: CODESYS标准化编程之输入输出映射-CSDN博客文章浏览阅读78次。在介绍输入输出映射之前大家需要了解开关量防抖滤波功能块,相关链接如下:开关量防抖滤波器(梯形图和SCL源代码)_开关量输入滤波程序-CSDN博客文章浏览阅…

禹神:一小时彻底搞懂跨域解决方案

1. 浏览器的同源策略 2. 跨域会受到哪些限制 4. CORS 解决 Ajax 跨域问题 exposedHeaders 不加这个&#xff0c;js拿不到这个响应头(浏览器控制台network中能看见&#xff0c;但是js拿不到) 5. JSONP 解决跨域问题 JSOP只能解决get请求 服务端代码 客户端代码 服务端代码升…

Gartner发布报告揭秘微软数据安全功能和许可

制定数据安全计划以增强合规性并降低数据风险仍然是安全和风险管理领导者关注的问题。这项研究阐明了 Microsoft 的数据安全许可结构&#xff0c;并确定了围绕 Purview 构建数据安全计划的关键要素。 主要发现 客户对微软数据安全的询问表明&#xff0c;安全和风险管理 (SRM) 领…

transformer模型进行英译汉,汉译英

上面是在测试集上的表现 下面是在训练集上的表现

全面掌控大模型:MaxKB与Ollama的高效本地部署策略

随着大模型的广泛应用&#xff0c;越来越多的开发者希望能够在本地运行这些模型&#xff0c;既提高数据隐私性&#xff0c;又避免依赖云端服务。本文将详细介绍如何在本地使用 Ollama 进行大模型部署&#xff0c;以及如何通过 MaxKB 导入本地知识库并进行交互操作。为了使该过程…

在线包装盒型生成工具,各种异型包装盒型,PDF导出方便

1、templatemaker.nl Passepartout ✂ Templatemaker ︎https://www.templatemaker.nl/en/passepartout/这是一个荷兰设计师建的一个在线盒型自动生成工具&#xff0c;包含各类新奇盒型&#xff0c;大家可以一起去观摩一下。 网站首页顶部各种盒型展示&#xff0c;大家根据需…

【CTF MISC】XCTF GFSJ1088 [中等] QR1 Writeup(图像处理+QR Code识别)

[中等] QR1 一张空白的图片&#xff1f; 解法 一张空白图片。 用 Photoshop 打开&#xff0c;放大&#xff0c;发现很多小黑点。 将图片复制到新文档&#xff0c;用魔棒工具选择白色部分。 Ctrl Shift i 反选。编辑&#xff0c;描边&#xff0c;黑色&#xff0c;10px&#…

2024年汉字小达人区级自由报名备考冲刺:往年真题练一练

2024年第十一届汉字小达人的区级活动的时间9月25-30日正式开赛&#xff0c;满打满算&#xff0c;还有16天时间准备。 还有一些孩子和家长&#xff0c; 刚刚被老师通知可以参加这个比赛&#xff0c;很关心的就是现在准备汉字小达人比赛是否来得及。别想这么多了&#xff0c;bet…

27. 完整的训练套路(三) train()、eval()

完整的训练套路(三) train() eval() 1. 什么是tain() eval() 在许多代码中我们经常会看到模型开始训练前会先进行一个 model.train()&#xff0c; 模型的测试之前会有一行 model.eval() 官方文档 https://pytorch.org/docs/stable/generated/torch.nn.Module.html#torch.nn.M…