[Cpp基础] [03] 变量声明-定义及初始化

Last Updated on 2020年4月14日

变量的”声明,定义”以及”初始化”

想要彻底理解这一部分的内容,你应该对OS/进程/虚拟内存/编译过程有一定的了解,仅从语言层次学习是比较抽象的.

声明和定义

  • 从编译的角度看:
    • 仅声明某个变量,那么就只创建了符号,这个符号不必分配存储空间,其具体地址需要在链接期resolve
    • 定义某个变量,在创建符号的同时,编译器为其分配了存储空间,符号地址在编译期就已经基本确定(后续可能会有重定位).
  • 对开发者而言:
    • 如果你希望使用某个已经存在的对象,那么就应该使用声明语句.
    • 每个定义语句都有”声明”的效果,不存在只定义不声明的语句.
  • 就C++而言,若语句仅有extern,且不含任何初始化部分,则该语句就是一个纯粹的声明,例如
    • extern T &val;//纯声明
    • extern T val;//纯声明
    • extern T val(ob);//定义
    • extern T val=ob;//定义

初始化,赋值和析构

  • 从自定义类的角度讲,这三个概念就很容易理解了:
    • 初始化:对应了构造函数的调用,初始化决定了对象创建完成之后,它所占用的内存空间的初始状态.
    • 赋值:对应了重载赋值运算符的调用
    • 析构:对应了析构函数的调用.
  • 逻辑上说,所有的对象初始化操作都是在运行期,是执行到定义语句时才触发的.
    • 这将用于解释全局变量/static变量构造函数的调用时机.
    • 对于静态变量,编译器会在编译期就为它们分配好值,这个操作可以用于加速部分静态对象的初始化过程.(例如,内置类型的静态变量,其初始化就没有运行时开销)
    • switch-case,goto都可以用于跳过初始化语句,部分编译器会对这种行为报错,因为这可能导致使用未被初始化的对象
  • 所有对象在生命周期内只会被初始化一次.
  • 当名字对编译器从可见变为不可见时,将触发自动析构.(常见的如退出作用域,goto)
  • new/delete表达式都是复合操作,前者是malloc()+T()后者是~T()+free()
    • 对C++而言,placement new可以在特定地址手动的触发构造,这样创建的对象应当手动调用析构函数进行销毁,而不是使用delete
    • 对于内置类型,直接newmalloc的效果相同,获得的都是未初始化的内存,但是new可以添加初始化参数,如new int(10) ;new int[10]{1}
  • 对于没有显式初始化的”定义语句”,将执行默认初始化:
    • 自定义类将执行T()构造函数,如果没有,将报错.
    • 内置类型的栈对象:不做任何操作
    • 内置类型的静态对象:填充为0

补充:栈/堆/静态区的对象

名称 功能 生命周期
栈区(0xFFFF方向) 存储函数调用栈帧,函数局部变量也存储在栈帧中 函数执行期间
堆区 由进程在运行期手动申请存储空间,并在其中创建对象 手动管理
静态区 编译系统在编译期预分配好的空间,主要供全局变量,static变量使用 整个进程
常量区(0x0000方向) 存储运行时不会改变的量,如程序代码,字符串等 整个进程