详解C++作用域与生命周期

Pascal之父Nicklaus Wirth曾经提出一个公式,展示出了程序的本质:程序=算法+数据结构。后人又给出一个公式与之遥相呼应:软件=程序+文档。这两个公式可以简洁明了的为我们展示程序和软件的组成。

程序的运行过程可以理解为算法对数据的加工过程,程序的运行的结果,就是算法加工数据产生的结果数据。算法描述的是对数据加工的步骤,对应于程序中的函数。数据结构描述的是数据在计算机中的组织结构,对应于程序中的数据类型。程序中数据对应的就是无处不在变量。对于我们编程人员,面对的无非就是函数,数据类型和变量。因此,C++谈及作用域与生命周期针对的就是这三大程序的组成要素:函数、数据类型和变量。下面将一一讲述。

1.作用域与生命周期的区别

作用域与生命周期是两个完全不同的概念。在英文中,作用域用“scope”表示,生命周期则用“duration”表示。作用域是一个静态概念,只在编译源程序的时候用到。一个标识符的作用域指在源文件中该标识符能够独立地合法出现的区域。生命周期则是一个运行时(Runtime)概念,它是指一个变量在整个程序从载入到结束运行的过程中存在的时间周期。由于函数和数据类型是静态的概念,它们没有生命周期的说法,它们从编译、程序的运行到结束整个过程是一直存在的。

C++中作用域的级别由高到低,主要有文件域(全局作用域)、名字空间域、类域、函数作用域和代码块作用域,其中函数作用域和代码块作用域又统称为局部域。

2.函数的作用域

函数分为类的成员函数和全局函数。

类的成员函数:

  • 作用域:类域。
  • 生命周期:无(程序运行期一直存在)。
  • 引用方法:其他文件中要使用点操作符(.)或指针操作符(->)或作用域运算符(::)来引用。
  • 内存分布:代码区。
  • 注意:类成员函数可以定义在类体内,即定义在头文件,当类被不同源文件包含时不会报重定义的错误,因为类体内实现的函数具有inline特性。

举例如下:

//main.cpp
class test
{
private:
 int i;
public:
 void show()
 {
 cout<<"i:"<<i<<endl;
 }
};
int main(int argc,char* argv[])
{
 test t;
 t.show()
}

全局函数:

  • 作用域:文件域(全局作用域)。
  • 生命周期:无(程序运行期一直存在)。
  • 引用方法:其他文件中要先进行函数原型声明,再使用。
  • 内存分布:代码段。
  • 注意:如果在两个源文件中定义了同名的全局函数,连接时会出现重定义错误。

举例如下:

//function.cpp
void printHello()
{
 cout<<"hello world"<<endl;
}

//main.cpp
void printHello();
int main(int argc,char* argv[])
{
 printHello();
}

3.数据类型的作用域

C++中的数据类型分为基本数据类型和非基本数据类型,非基本数据类型中又分为复合数据类型和构造数据类型。关于C++中的数据类型,详见本人另一篇blog: C++数据类型。

基本数据类型:
 基本数据类型包括整型(int)、实型(float和double)、字符型(char)、布尔型(bool)和无值型(void)。

  • 作用域:文件域(全局作用域)。
  • 生命周期:无(程序运行期一直存在)。
  • 引用方法:无需申明,直接使用。
  • 内存分布:代码段。

复合数据类型:

复合数据类型包括:数组(type[])、指针(type*)、引用(type&)、枚举(enum)。

如果复合数据类型是构造数据类型参与的复合,其作用域与构造数据类型一致。enum枚举类型的作用域与构造类型相同。

构造数据类型:

  • 作用域:类型定义所在的域,其他文件不可见。
  • 生命周期:无(程序运行期一直存在)。
  • 引用方法:其他文件中要先进行定义,再通过作用域运算符进行使用。
  • 内存分布:代码区。
  • 注意:只要文件不互相包含,如果在两个源文件中定义了同名的构造,不会出现重定义错误,因为数据类型不具有外部连接性。

举例如下:

//main.cpp
namespace dd
{
 class test
 {
 private:
 int i;
  public:
 void show()
   {
   cout<<"i:"<<i<<endl;
   }
 };
}
using namespace dd;//引用命名空间域中的构造类型test,否则无法使用
int main(int argc,char* argv[])
{
 test t;
 t.show();
}

4.变量的作用域与生命周期

我们面对的变量主要分为全局变量、全局静态变量、局部变量和局部静态变量。下面一一讲述他们的作用域与生命周期。

全局变量:

  • 作用域:全局作用域(全局变量只需在一个源文件中定义,就可以作用于所有的源文件);
  • 生命周期:程序运行期一直存在;
  • 引用方法:其他文件中要使用必须用extern 关键字声明要引用的全局变量。;
  • 内存分布:全局/静态存储区;
  • 注意:如果在两个文件中都定义了相同名字的全局变量,连接出错:变量重定义。

举例如下:

//define.cpp
int g_iValue = 1; 

//main.cpp
extern int g_iValue; 

int main()
{
  cout << g_iValue;
  return 0;
} 

全局静态变量:

  • 作用域:文件作用域(只在被定义的文件中可见);
  • 生命周期:程序运行期一直存在;
  • 内存分布:全局/静态存储区;
  • 定义方法:static关键字,const 关键字;
  • 注意:只要文件不互相包含,在两个不同的文件中是可以定义完全相同的两个静态变量的,它们是两个完全不同的变量。

举例如下:

//define.cpp
const int iValue=8;  

//main.cpp
int iValue;
static const int iValue_2;
static int iValue_3;
int main(int argc,char* argv[])
{
 cout<<"iValue:"<<iValue<<endl;
 return 0;
}

局部变量:

  • 作用域:局部作用域(只在局部作用域中可见,如函数域,代码块域);
  • 生命周期:程序运行出局部作用域即被销毁;
  • 内存分布:栈区;
  • 注意:auto指示符标示。

举例如下:

void print()
{
 int a=0;
 cout<<a<<endl;
}

局部静态变量:

  • 作用域:局部作用域(只在局部作用域中可见);
  • 生命周期:程序运行期一直存在;
  • 内存分布:全局静态存储区;
  • 定义方法:局部作用域用中用static定义;
  • 注意:只被初始化一次,多线程中需加锁保护。

举例如下:

void function()
{
  static int iREFCounter = 0;
} 

5.扩展知识点

5.1变量存储类型说明符

C语言中提供了四种存储类型说明符auto,register,extern和static,四种存储类型有两种存储期:自动存储期和静态存储期。其中auto和register对应自动存储期,被修饰的变量在进入声明该变量的程序块时被建立,它在该程序块活动时存在,退出该程序块时撤销。静态存储期的变量从程序载入运行到程序结束一直存在。

5.2static使用建议

(1)若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度;
 (2)若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;
 (3)设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题,因为他们都放在静态数据存储区,可被其他函数共享;
 (4)如果我们需要一个可重入的函数,那么我们一定要避免函数中使用static变量。这样的函数被称为带“内部存储器”功能的函数;
 (5)函数中必须要使用static变量情况:比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为野指针。

以上就是详解C++作用域与生命周期的详细内容,更多关于C++作用域与生命周期的资料请关注我们其它相关文章!

(0)

相关推荐

  • 浅谈C++变量作用域

    C++ 变量作用域 作用域是程序的一个区域,一般来说有三个地方可以定义变量: 在函数或一个代码块内部声明的变量,称为局部变量. 在函数参数的定义中声明的变量,称为形式参数. 在所有函数外部声明的变量,称为全局变量. 我们将在后续的章节中学习什么是函数和参数.本章我们先来讲解什么是局部变量和全局变量. 局部变量 在函数或一个代码块内部声明的变量,称为局部变量.它们只能被函数内部或者代码块内部的语句使用.下面的实例使用了局部变量: #include <iostream> using namespa

  • C/C++中的名字空间与作用域示例详解

    前言 本文主要给大家介绍了关于C/C++中名字空间与作用域的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. C语言中有名字空间这个概念吗? 提到名字空间(或者可能更普遍的叫法,命名空间),很可能先想到的是C++,甚至是C#.C中没有名字空间吧?一开始我也是这样认为的,直到我看了C primer plus这本书,才直到C语言中其实也有名字空间的概念!而为什么我们更熟悉C++中的名字空间呢?可能是因为我们一些C++程序,不过知不知道为什么,总是要加上一句using nam

  • C++中变量的类型与作用域学习教程

    C++ 变量类型 变量其实只不过是程序可操作的存储区的名称.C++ 中每个变量都有指定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上. 变量的名称可以由字母.数字和下划线字符组成.它必须以字母或下划线开头.大写字母和小写字母是不同的,因为 C++ 是大小写敏感的. 基于前一章讲解的基本类型,有以下几种基本的变量类型,将在下面进行讲解: 类型 描述 bool 存储值 true 或 false. char 通常是一个八位字节(一个字节).这是一个整数类型

  • c++作用域运算符用法(全局变量和局部变量)

    通常情况下,如果有两个同名变量,一个是全局变量,另一个是局部变量,那么局部变量在其作用域内具有较高的优先权,它将屏蔽全局变量. 作用域运算符 复制代码 代码如下: #include<iostream>using namespace std;int num=10;int main(){   int num;    num=25;    cout<<"num is "<<num<<endl;    return 0;} 程序的输出结果是num

  • C++中四种对象生存期和作用域以及static的用法总结分析

    一.四种对象生存期和作用域 栈对象隐含调用构造函数(程序中没有显式调用) 堆对象隐含调用构造函数(程序中没有显式调用),要显式释放 全局对象.静态全局对象 全局对象的构造先于main函数 已初始化的全局变量或静态全局对象存储于.data段中 未初始化的全局变量或静态全局对象存储于.bss段中 静态局部对象 已初始化的静态局部变量存储于.data段中 未初始化的静态局部变量存储于.bss段中 复制代码 代码如下: #include <iostream>using namespace std;cl

  • C++临时性对象的生命周期详细解析

    有关临时对象的生命周期有三种情况: 1)一般情况:临时性对象的被摧毁,应该是对完整表达式(full-expression)求值过程中的最后一个步骤.该完整表达式造成临时对象的产生. 实例代码如下: 复制代码 代码如下: #include <iostream>using namespace std;class A{public:    A(int i): m_i(i)    {        cout << "A(): " << m_i <<

  • 详解C++作用域与生命周期

    Pascal之父Nicklaus Wirth曾经提出一个公式,展示出了程序的本质:程序=算法+数据结构.后人又给出一个公式与之遥相呼应:软件=程序+文档.这两个公式可以简洁明了的为我们展示程序和软件的组成. 程序的运行过程可以理解为算法对数据的加工过程,程序的运行的结果,就是算法加工数据产生的结果数据.算法描述的是对数据加工的步骤,对应于程序中的函数.数据结构描述的是数据在计算机中的组织结构,对应于程序中的数据类型.程序中数据对应的就是无处不在变量.对于我们编程人员,面对的无非就是函数,数据类型

  • 详解Angular组件之生命周期(二)

    一.view钩子 view钩子有2个,ngAfterViewInit和ngAfterViewChecked钩子. 1.实现ngAfterViewInit和ngAfterViewChecked钩子时注意事项 以父组件调用子组件方法中例子为基础,在父组件中实现ngAfterViewInit和ngAfterViewChecked钩子. 这两个钩子是在组件的模版所有内容组装完成后,组件模版已经呈现给用户看了,之后这两个钩子方法会被调用. @ViewChild('child1') child1:Child

  • 详解ES6 Promise的生命周期和创建

    一:Promise的概念 Promise的中文意思是'承诺',什么叫承诺?承诺就是现在没有发生,在将来的某个时刻一定会发生的事情. 放在编程语言的环境下,Promise就是异步事件的结果的占位符.我们不用去管异步事件的结果什么时候来,只需要关心异步事件的结果产生的时候,你想要做什么就对了. 二:Promise的生命周期 异步事件不是立即执行程序,它的结果可能要在动作发生后一段时间才到,所以它有个生命周期.例如用电饭锅煮米饭,从[米下锅开始定时]到[定时结束],这是煮米饭的生命周期. 一个Prom

  • 详解Spring中bean生命周期回调方法

    生命周期回调方法 对于spring bean来讲,我们默认可以指定两个生命周期回调方法.一个是在ApplicationContext将bean初始化,包括注入对应的依赖后的回调方法:另一个是在ApplicationContext准备销毁之前的回调方法.要实现这种回调主要有三种方式:实现特定的接口.在XML配置文件中指定回调方法和使用JSR-250标准的注解. 1 实现特定接口 针对bean初始化后的回调和ApplicationContext销毁前的回调,Spring分别为我们了提供了Initia

  • 详解ASP.NET页面生命周期事件

    下面是ASP.NET页面初始的过程:1. Page_Init();2. Load ViewState;3. Load Postback data;4. Page_Load();5. Handle control events;6. Page_PreRender();7. Page_Render();8. Unload event;9. Dispose method called; 下面对其中的一些过程作下描述:1. Page_Init();这个过程主要是初始化控件,每次页面载入执行这个初始过程,

  • 详解ASP.NET页面生命周期

    ASP.NET页面运行时候,页面将经历一个生命周期,在生命周期中将执行一系列的处理步骤.包括初始化.实例化控件.还原和维护状态.运行时间处理程序代码以及进行呈现.熟悉页面生命周期非常重要,这样我们才能在生命周期的合适阶段编写代码.如果我们能在写代码的时候想着我们现在是在做生命周期的哪一步那将是非常好的. 几个代表性的问题 在开始的时候我们先思考几个问题,看看我们在描述完页面生命周期的时候,能不能回答上这几个问题 1.为什么在服务器端能通过this.textbox1.Text获取到用户提交过来的数

  • 详解Spring中Bean的作用域与生命周期

    目录 一.Bean的作用域 二.Bean的生命周期 使用代码演示Bean的生命周期 一.Bean的作用域 通过Spring容器创建一个Bean的实例时,不仅可以完成Bean的实例化,还可以使用Bean的scope属性为bean设置作用域. 语法格式:<bean id="别名" scope="作用域" class="对应实现类"> 作用域的种类:(sing) singleton和prototype区别:(该两种比较常用) ① singl

  • Java开发学习之Bean的作用域和生命周期详解

    目录 一.Bean 的作用域 二.Spring 的执行流程 三.Bean 的生命周期 一.Bean 的作用域 在之前学习Java基础的时候,有接触到作用域这样的概念.一个变量并不一定在任何区域都是有效的,限定这个变量的可用性的代码范围就是该变量的作用域. 但是在这里 Bean 的作用域的概念和以前所认为的作用域有所不同. Bean 的作用域是指 Bean 在 Spring 整个框架中的某种行为模式. 接下来,将会举一个案例来讲讲什么是作用域,什么是行为模式 案例概要: 创建一个共有的 Bean

  • 详解JavaScript 作用域

    作用域是可访问变量的集合. JavaScript 作用域 在 JavaScript 中, 对象和函数同样也是变量. 在 JavaScript 中, 作用域为可访问变量,对象,函数的集合. JavaScript 函数作用域: 作用域在函数内修改. JavaScript 局部作用域 变量在函数内声明,变量为局部作用域. 局部变量:只能在函数内部访问. // 此处不能调用 carName 变量 function myFunction() { var carName = "Volvo"; //

  • 浅谈Spring中Bean的作用域、生命周期

    本文主要探究的是关于Bean的作用域.生命周期的相关内容,具体如下. Bean的作用域 Spring 3中为Bean定义了5中作用域,分别为singleton(单例).prototype(原型).request.session和global session,5种作用域说明如下: 1.singleton:单例模式,Spring IoC容器中只会存在一个共享的Bean实例,无论有多少个Bean引用它,始终指向同一对象.Singleton作用域是Spring中的缺省作用域,也可以显示的将Bean定义为

随机推荐