作用域

作用域规则告诉我们一个变量的有效范围,它在哪儿创建,在哪儿销毁(也就是说超出了作用域)。变量的有效作用域从它的定义点开始,到和定义变量之前最邻近的开括号配对的第一个闭括号。也就是说,作用域由变量所在的最近一对括号确定。

(1) 全局变量:

全局变量是在所有函数体的外部定义的,程序的所在部分(甚至其它文件中的代码)都可以使用。全局变量不受作用域的影响(也就是说,全局变量的生命期一直到程序的结束)。如果在一个文件中使用extern关键字来声明另一个文件中存在的全局变量,那么这个文件可以使用这个数据。

(2) 局部变量:

局部变量出现在一个作用域内,它们是局限于一个函数的。局部变量经常被称为自动变量,因为它们在进入作用域时自动生成,离开作用域时自动消失。关键字auto可以显式地说明这个问题,但是局部变量默认为auto,所以没有必要声明为auto。

(3) 寄存器变量

寄存器变量是一种局部变量。关键字register告诉编译器“尽可能快地访问这个变量”。加快访问速度取决于现实,但是,正如名字所暗示的那样,这经常是通过在寄存器中放置变量来做到的。这并不能保证将变置在寄存器中,甚至也不能保证提高访问速度。这只是对编译器的一个暗示。

使用register变量是有限制的:(1) 不可能得到或计算register 变量的地址; (2) register变量只能在一个块中声明(不可能有全局的或静态的register变量)。然而可以在一个函数中(即在参数表中)使用register变量作为一个形式参数。

一般地,不应当推测编译器的优化器,因为它可能比我们做得更好。因此,最好避免使用关键字register。

(4) 静态变量

关键字static有一些独特的意义。通常,函数中定义局部变量在函数中作用域结束时消失。当再次调用这个函数时,会重新创建变量的存储空间,其值会被重新初始化。如果想使局部变量的值在程序的整个生命期里仍然存在,我们可以定义函数的局部变量为static(静态的),并给它一个初始化。初始化只在函数第一次调用时执行,函数调用之间变量的值保持不变,这种方式,函数可以“记住”函数调用之间的一些信息片断。这也就是所谓的静态局部变量,具有局部作用域,它只被初始化一次,自从第一次被初始化直到程序运行结束都一直存在,它和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只在定义自己的函数体内始终可见。

我们可能奇怪为什么不使用全局变量。static局部变量的优点是在函数范围之外它是不可用的,所以它不可能被轻易改变。这会使错误局部化。

此外同样存在静态全局变量,具有全局作用域,它与全局变量的区别在于如果程序包含多个文件的话,它作用于定义它的文件里,不能作用到其它文件里,即被static关键字修饰过的变量具有文件作用域。这样即使两个不同的源文件都定义了相同名字的静态全局变量,它们也是不同的变量。

(5) 外部变量

extern告诉编译器存在着一个变量和函数,即使编译器在当前的文件中没有看到它。这个变量或函数可能在一个文件或者在当前文件的后面定义。例如extern int i;编译器会知道i肯定作为全局变量存在于某处。当编译器看到变量i的定义时,并没有看到别的声明,所以知道它在文件的前面已经找到了同样声明的i。

(6) const常量

const告诉编译器这个名字表示常量,不管是内部的还是用户定义的数据类型都可以定义为const。如果定义了某对象为常量,然后试图改变它,编译器将会产生错误。在C++中一个const必须有初始值。

(7) volatile变量

限定词const告诉编译器“这是不会改变的”(这就是允许编译器执行额外的优化);而限定词volatile则告诉编译器“不知道何时变化”,防止编译器依据变量的稳定性作任何优化。

从分配内存空间看:全局变量,静态局部变量,静态全局变量都在静态存储区分配空间,而局部变量在栈里分配空间。

命名空间

使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突。如果没有命名空间,这些变量、函数、类的名称将都存在于全局命名空间中,会导致很多冲突。比如,如果我们在自己的程序中定义了一个函数toupper(),这将重写标准库中的toupper()函 数,这是因为这两个函数都是位于全局命名空间中的。命名冲突还会发生在一个程序中使用两个或者更多的第三方库的情况中。此时,很有可能,其中一个库中的名 称和另外一个库中的名称是相同的,这样就冲突了。这种情况会经常发生在类的名称上。比如,我们在自己的程序中定义了一个Stack类,而我们程序中使用的某个库中也可能定义了一个同名的类,此时名称就冲突了。

我们常见的using namespace std;这句代码就是指使用C++自己的名称空间,即std。

定义命名空间的基本形式如下:

 namespace 名称
{
//代码
}

示例:

 namespace CounterNameSpace
{ int upperbound;
int lowerbound; class counter
{ int count;
public:
counter(int n)
{ if ( n <= upperbound ){
count = n;
} else {
count = upperbound;
}
}
void reset(int n)
{
if ( n < upperbound )
{
count = n;
}
}
int run() {
if ( count > lowerbound)
{
return count--;
} else {
return lowerbound;
}
}
};
}

在命名空间中声明的标识符是可以被直接引用的,不需要任何的命名空间的修饰符。

然而,既然命名空间定义了一个范围,那么我们在命名空间之外就需要使用范围解析运算符来引用命名空间中的对象。例如,在命名空间CounterNameSpace定义的范围之外给upperbound赋值为10,就必须这样写:

 CounterNameSpace::upperbound = ;

或者在CounterNameSpace定义的范围之外想要声明一个counter类的对象就必须这样写:

 CounterNameSpace::counter obj;

一般来讲,在命名空间之外想要访问命名空间内部的成员需要在成员前面加上命名空间和范围解析运算符。

相同的空间名称是可以被多次声明的,这种声明向相互补充的,这就使得命名空间可以被分割到几个文件中甚至是同一个文件的不同地方中。

例如:

 namespace NS

 {

 int i;

 }

 //...

 namespace NS

 {

     int j;

 }

其中命名空间NS被分割成两部分,但是两部分的内容却是位于同一命名空间中的。也就是NS。最后一点:命名空间是可以嵌套的。也就是说可以在一个命名空间内部声明另外的命名空间。

using关键字

如果在程序中需要多次引用某个命名空间的成员,那么按照之前的说法,我们每次都要使用范围解析符来指定该命名空间,这是一件很麻烦的事情。为了解决这个问题,人们引入了using关键字。using语句通常有两种使用方式:

using namespace 命名空间名称;

using 命名空间名称::成员;

第一种形式中的命名空间名称就是我们要访问的命名空间。该命名空间中的所有成员都会被引入到当前范围中。也就是说,他们都变成当前命名空间的一部分了,使用的时候不再需要使用范围限定符了。第二种形式只是让指定的命名空间中的指定成员在当前范围中变为可见。

我们用前面的CounterNameSpace来举例,下面的using语句和赋值语句都是有效的:

 using CounterNameSpace::lowerbound; //只有lowerbound当前是可见的
lowerbound = ; //这样写是合法的,因为lowerbound成员当前是可见的
using CounterNameSpace; //所有CounterNameSpace空间的成员当前都是可见的
upperbound = ; //这样写是合法的,因为所有的CounterNameSpace成员目前都是可见的

C++学习笔记(九):作用域和命名空间的更多相关文章

  1. 多线程学习笔记九之ThreadLocal

    目录 多线程学习笔记九之ThreadLocal 简介 类结构 源码分析 ThreadLocalMap set(T value) get() remove() 为什么ThreadLocalMap的键是W ...

  2. MDX导航结构层次:《Microsoft SQL Server 2008 MDX Step by Step》学习笔记九

    <Microsoft SQL Server 2008 MDX Step by Step>学习笔记九:导航结构层次   SQL Server 2008中SQL应用系列及BI笔记系列--目录索 ...

  3. python3.4学习笔记(九) Python GUI桌面应用开发工具选择

    python3.4学习笔记(九) Python GUI桌面应用开发工具选择 Python GUI开发工具选择 - WEB开发者http://www.admin10000.com/document/96 ...

  4. Go语言学习笔记九: 指针

    Go语言学习笔记九: 指针 指针的概念是当时学C语言时了解的.Go语言的指针感觉与C语言的没啥不同. 指针定义与使用 指针变量是保存内存地址的变量.其他变量保存的是数值,而指针变量保存的是内存地址.这 ...

  5. go微服务框架kratos学习笔记九(kratos 全链路追踪 zipkin)

    目录 go微服务框架kratos学习笔记九(kratos 全链路追踪 zipkin) zipkin使用demo 数据持久化 go微服务框架kratos学习笔记九(kratos 全链路追踪 zipkin ...

  6. Python学习笔记九

    Python学习笔记之九 为什么要有操作系统 管理硬件,提供接口. 管理调度进程,并且将多个进程对硬件的竞争变得有序. 操作系统发展史 第一代计算机:真空管和穿孔卡片 没有操作系统,所有的程序设计直接 ...

  7. (C/C++学习笔记) 九. 变量的存储类型

    九. 变量的存储类型 ● 变量的存储类型(见附页) ● 注释 ①对于自动变量,它属于动态存储方式. 但是也可以用static定义它为静态自动变量,或称静态局部变量,从而成为静态存储方式.由此看来,一个 ...

  8. (C/C++学习笔记) 三. 作用域和可见性

    三. 作用域和可见性 ● 标识符的作用域 标识符的作用域是标识符在程序源代码中的有效范围,从小到大分为函数原型作用域.块作用域(局部作用域),文件作用域(全局作用域). 1. 函数原型作用域 函数原型 ...

  9. vue学习笔记(九)vue-cli中的组件通信

    前言 在上一篇博客vue学习笔记(八)组件校验&通信中,我们学会了vue中组件的校验和父组件向子组件传递信息以及子组件通知父组件(父子组件通信),上一篇博客也提到那是对组件内容的刚刚开始,而本 ...

  10. C#线程学习笔记九:async & await入门二

    一.异步方法返回类型 只能返回3种类型(void.Task和Task<T>). 1.1.void返回类型:调用方法执行异步方法,但又不需要做进一步的交互. class Program { ...

随机推荐

  1. Mac与Linux的一个巨大不同

    就是Mac仍处在桌面市场的商业圈里,尽管它的市场很小,但是正版率却很高,而且还有专门的Mac Store提供付费下载. Linux在桌面市场几乎没有份额,也不会有人会去买它的应用软件,基本上只存在于服 ...

  2. dup和dup2函数以及管道的实现

    疑问:管道应该不是这样实现的,因为这要求修改程序的代码 dup和dup2也是两个非常有用的调用,它们的作用都是用来复制一个文件的描述符.它们经常用来重定向进程的stdin.stdout和stderr. ...

  3. 新建一个struts2项目

    1,新建-动态web项目: 2,将struts2的必要jar包复制到WEB-INF\lib文件夹下,一共有9个,如图一所示. 图一 3,配置web.xml文件,将以下内容写到web.xml文件中. & ...

  4. Debug 和 Release 编译方式的本质区别

    一.Debug 和 Release 编译方式的本质区别 Debug 通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序.Release 称为发布版本,它往往是进行了各种优化,使得程 ...

  5. PHPUNIT 单元测试

    在windows上的安装可以参考其手册 首先下载phpunit.phar文件 1. 为php的二进制可执行文件建立 一个目录,如C:\bin 2. 将C:\bin添加到系统环境变量中, 3. 打开命令 ...

  6. Ext入门学习系列(五)表格控件(1)

    上节学习了Ext面板控件,为后面的各个控件学习奠定基础,在此基础上本章将学习网络开发最期待的功能——表格控件. 我们都知道网络编程语言中,除了.net其他的基本没有提供网格控件,而最近的asp.net ...

  7. xmlns 属性

    xmlns 属性 xmlns 属性可以在文档中定义一个或多个可供选择的命名空间.该属性可以放置在文档内任何元素的开始标签中.该属性的值类似于 URL,它定义了一个命名空间,浏览器会将此命名空间用于该属 ...

  8. 发送复杂的HTTP GET请求并且取回响应。

    设计思想: 创建一个HttpWebRequest类的实例,并通过GetReponse()方法取回响应的HTTP响应. 实例方案: string url="http://www.baidu.c ...

  9. 二维坐标的平移,旋转,缩放及matlab实现

    本文结合matlab 软件解释二维坐标系下的平移,旋转,缩放 首先确定点在二维坐标系下的表达方法,使用一个1*3矩阵: Pt = [x,y,1] 其中x,y 分别为点的X,Y坐标,1为对二维坐标的三维 ...

  10. js基础第八天

    返回前面起第一个字符位置 indexOf("sdfsdf");它是从左边索引为0开始数,而且只找第一个,然后返回该字符的位置.返回是个数值.如果找不到该字符,那么就会返回-1. 返 ...