c++ 的namespace及注意事项
前文
下文中的出现的"当前域"为"当前作用域"的简写
namepsace在c++中是用来避免不同模块下相同名字冲突的一种关键字,本文粗略的介绍了一下namespace的使用以及需要注意的地方:
- 1.可通过显示指定namespace,or使用using引入符号的方式, or使用using namepsace加载整个namespace的方式使用一个名称空间下的符号
- 2.显示指定符号所属namespace的情况下,无论该表达式在哪里都是按照namespace绝对路径进行匹配而不是相对路径
- 3.using 指令是存在作用域和指令顺序性的,允许同时使用多个namespace,同时使用多个namespace存在相同的符号并不会覆盖,但编译会报ambigious,可通过显示指定符号所属namespace解决
- 4.using namespace相当于在当前域把namespace下的所有符号导入全局,如果当前域有局部变量,会出现局部变量覆盖全局变量的情况;
- 5.直接使用using引入变量A,相当于在当前域涉及到的A都是这个引入的A,不允许覆盖,同名,重复,否则报错。
- 6.对于匿名namespace, 编译器为每个不同编译单元(.o文件)生成的不同的namespace并使用它,可通过匿名空间实现类似于static的符号外部不可见性
- 7.namespace在编译期完成后的本质就是给符号加前缀
- 8.人生苦短,我还是显示指定吧
正文
使用namespace
下文简单的创建了一个名为n1的namespace,在其中放入了一个变量number,并在main()中使用了它
namespace n1{
int number = 1;
};
int main(){
//1.显式指出使用那个namespace下的number
std::cout<<n1::number<<std::endl;
//2.引入n1::number后再使用它
using n1::number;
std::cout<<number<<std::endl;
return 0;
}
还有另一种方式获取到n1::number,可以通过using namespace加载整个namespace进来,但这种方式有其复杂性(后文详述),通常不推荐使用
namespace n1{
int number = 1;
}
int main(){
using namespace n1; //使用namespace n1
std::cout<<number<<std::endl;
return 0;
}
符号的namepsace查找规则
由于namespace是支持嵌套的,在显式指出符号所属的namespace的情况下,无论这个表达式放在什么地方,都是按照绝对路径从头开始匹配namespace,什么意思呢, 如下:
namespace n1{
void fn(){
std::cout<<"1"; //这里是去找std::cout, 而不是找 n1::std::cout
}
}
global namspace
因为编译器为程序默认创建使用一个的global namespace(未显示指定在其余namespace下的定义生命变量都在global namespace下), 对于global namespace, 直接使用::, 前面什么都不需要加, 一般而言全局域的::是可以省略的,因为所有创建的东西都是在全局名称空间下,包括自己创建的namespace, 所以std::cout和::std::cout是一样的。
int number = 0;
int main(){
std::cout<<::temp<<std::endl;
return 0;
}
作用域
下面的例子显示了 using 是有作用域的,和一般的变量的作用域规则一样
namespace n1{
int number = 1;
}
namespace n2{
int number = 2;
}
int main(){
{
using n1::number;
std::cout<<number<<std::endl; // 1 输出n1::number
{
using n2::number;
std::cout<<number<<std::endl; // 2 输出n2::number
}
std::cout<<number<<std::endl; // 1 输出n1::number
}
return 0;
}
覆盖
当使用using namespace的时候,就会使用一个namespace,同时支持多个namespace的使用,在同时使用多个namespace的情况下,是会存在同名符号冲突的情况的,如下:
此时还是需要通过显示的指出所属namespace来解决
int number = 0; //global namespace
namespace n1{
int number = 1; //n1 namespace
}
int main(){
using namespace n1;
std::cout<<number<<std::endl; //由于同时存在两个全局变量number,所以此处会报ambigious的错误
return 0;
}
但如果此处using的变量则没有问题,其相当于在当前域引入了一个变量number
int number = 0;
namespace n1{
int number = 1;
}
int main(){
using n1::number; //声明接下来要使用n1::number了,接下里的number都是它,相当于在当前域引入了一个变量number
std::cout<<number<<std::endl; //没问题,输出1
return 0;
}
当使用一个namespace的时候,就相当于在当前域把该namespace的变量都导入全局中了,如往常,局部变量会覆盖全局变量
namespace n1{
int number = 1;
}
int main(){
int number = 0;
using namespace n1;
std::cout<<number<<std::endl;//输出的结果为0, 局部变量覆盖了n1::number
return 0;
}
但是要注意,如果是引入变量到当前域,和之前所说的一样相当于定义一个变量,编译器严格控制了,不允许再定义相同的符号
namespace n1{
int number = 1;
}
int main(){
using n1::number; // error: redeclaration of ‘int number’
int number = 0;
return 0;
}
匿名namespace
当定义一个名称空间时,可以忽略这个名称空间的名称,这种名称空间又被称为匿名namespace(匿名空间)
namespace {
int number;
}
编译器在内部会为这个名称空间生成一个唯一的名字,而且还会为这个匿名的名称空间生成一条using指令。所以上面的代码在效果上等同于:
namespace __UNIQUE_NAME_ {
int number;
}
using namespace __UNIQUE_NAME_;
匿名空间在不同的编译单元下的__UNIQUE_NAME_ 是不同的,可以用来解决链接阶段不同编译单元(.o文件)的符号同名现象,如下
**************f1.c***************
namespace{
int number;
}
void fn1(){}
**************f2.c***************
namespace{
int number;
}
void fn2(){}
**************main.c***************
int main(){
return 0;
}
**************编译链接***************
⚡ root@acnszavl00033 ~/temp/test g++ -c f1.c
⚡ root@acnszavl00033 ~/temp/test g++ -c f2.c
⚡ root@acnszavl00033 ~/temp/test g++ -o a.out main.c f1.o f2.o
但如果去掉了f1.c和f2.c中的namespace就会报重定义的错,因为在链接阶段会发现有两个同名符号(详见编译链接(1)的2.1 关于链接);namespace在编译期后的本质,就是给不同的名称空间下的符号加上前缀, 而static将变量的可见性限制为.o文件内部(而不是源文件内部),因无法在编程时显示调用我要xxx.o文件的匿名空间从而使用到其符号,所以也常见google也使用匿名空间以替代static
跨文件使用namespace
有时候我们希望跨文件使用namespace,可以如下使用方式
**************fn1.h***************
namespace n1{
extern int number; //声明
}
**************fn1.c***************
namespace n1{
int number = 1; //定义
}
**************main.c***************
#include"fn1.h"
#include<iostream>
int main(){
std::cout<<n1::number<<std::endl;
return 0;
}
当我们使用g++ main.c fn1.c去编译的时候应该想到,其做了如下操作,
g++ -c main.c //生成main.o
g++ -c fn1.c //生成fn1.o
g++ main.o fn1.o
因为我们知道namespace在编译期后的本质,就是给不同的名称空间下的符号加上前缀, 所以在链接阶段,要链接符号前缀相同,所以可以链接上
c++ 的namespace及注意事项的更多相关文章
- C# using、namespace使用注意事项
一.using 用法 1.引用命名空间. 如: using System; 2.自动释放对象使用的资源. 如: using (SqlConnection connection = new SqlCon ...
- Chapter3 (字符串,向量,数组) --C++Prime笔记
1.using用法:using namespace ::name;注意事项:一般不在头文件使用using否则很容易导致运用命名空间不对错误. 2.string的方法: ①getline(输入流,str ...
- C#/VB.NET 创建图片超链接
超链接(Hyperlink)可以看做是一个"热点",它可以从当前Web页定义的位置跳转到其他位置,包括当前页的某个位置.Internet.本地硬盘或局域网上的其他文件,甚至跳转到声 ...
- jdom使用入门及namespace注意事项【原】
报文样例 <person:info xmlns:person="http://person/abc" id="1"> <fruit id=&q ...
- vector的 emplace 和 insert 以及使用vector进行iterator遍历 且 erase的时候注意事项
vector<int> first;//Size()==2 first.push_back(); first.push_back(); //first.insert(2); vector& ...
- C#扫盲之:带你掌握C#的扩展方法、以及探讨扩展方法的本质、注意事项
1.为什么需要扩展方法 .NET3.5给我们提供了扩展方法的概念,它的功能是在不修改要添加类型的原有结构时,允许你为类或结构添加新方法. 思考:那么究竟为什么需要扩展方法呢,为什么不直接修改原有类型呢 ...
- EntityFrame Work 5 性能注意事项(转自MSDN)
1.简介 对象关系映射框架是一种在面向对象的应用程序中提供数据访问抽象的便捷方式.对于 .NET 应用程序,Microsoft 推荐的 O/RM 是实体框架.但任何抽象都要考虑性能. 本白皮书旨在介绍 ...
- PHP命令空间namespace及use的用法实践总结
以下皆为本人自我理解内容,如有失误之处,请多多包涵. 文章大纲: 使用namespace的目的 namespace的使用方法 使用use的目的 use的使用方法 使用namespace的目的: 团队合 ...
- ASP.NET CORE 自定义视图组件(ViewComponent)注意事项
*红色字体为固定命名,蓝色为一般命名规则,黄色为ASP.NET CORE 默认查找文件名 概要:1.简单ViewComponent的用法 2.ViewComponent控制器返回值 3.注意事项 1 ...
随机推荐
- Python计算IV值
更多大数据分析.建模等内容请关注公众号<bigdatamodeling> 在对变量分箱后,需要计算变量的重要性,IV是评估变量区分度或重要性的统计量之一,python计算IV值的代码如下: ...
- 【JS】小工具
带天数的倒计时 function countDown(dateline){ var timer=null, //倒计时 current_time=Math.floor(new Date().getTi ...
- LNMP-Nginx配置不记录静态文件、过期时间
用户访问web网站,通常日志文件会记录很多web站点上的一些静态文件信息,如果长期不处理,日志文件会越来越大,占用的系统资源也越大,此时就需要我们配置不记录静态文件和过期时间,减少日志文件记录过多不必 ...
- js中promise解决callback回调地狱以及使用async+await异步处理的方法
1.callback回调地狱 function ajax(fn) { setTimeout(()=> { console.log('你好') fn() }, 1000) } ajax(() =& ...
- android studio 项目生成的apk变小的原因
问题:感觉直接在apk文件夹下面拷出来的apk不能安装使用,而且apk比较小,可能就是这个问题引起的 Android Studio版本升级到2.3后,增加了instant run功能,对项目的buil ...
- Ubuntu系统下arm-linux-gcc交叉编译环境搭建过程
搭建所需环境Linux版本:Ubuntu 14.10 交叉编译器版本:arm-linux-gcc-4.4.3资源链接 何为交叉编译环境搭建交叉编译环境,即安装.配置交叉编译工具链.在Ubuntu环境下 ...
- django生命周期请求l流程图
django思维导图链接:https://www.processon.com/view/link/5dddb0f8e4b074c442e5c68c
- Django聚合查询 orm字段及属性
目录 一 聚合查询 1. 级联 级联删除 级联更新 2. 聚合函数 使用 aggregate 使用场景 3. 分组查询 语法 使用 annotate 代码 4. F与Q查询 F查询 Q查询 二 ORM ...
- C#访问SFTP:Renci.SshNet.Async
SFTP是SSH File Transfer Protocol的缩写,安全文件传送协议.安全文件传送协议.可以为传输文件提供一种安全的网络的加密方法.sftp 与 ftp 有着几乎一样的语法和功能. ...
- 用Python抢到回家的车票,so easy!
“ 盼望着,盼望着,春节的脚步近了,然而,每年到这个时候,最难的,莫过于一张回家的火车票. 据悉,今年春运期间,全国铁路发送旅客人次同比将增长 8.0%.达到 4.4 亿人次. 2020 年铁 ...