Last Updated on 2022年9月27日
变量的"声明,定义"以及"初始化"
想要彻底理解这一部分的内容,你应该对OS/进程/虚拟内存/编译/链接过程有一定的了解,仅从语言层次学习是比较抽象的.
声明和定义
- 从编译的角度看:
- 仅声明某个变量,那么就只创建了Symbol,这个Symbol对应的存储空间需要在后续的链接过程中resolve.
- 定义某个变量,在创建Symbol的同时,编译器为其分配了存储空间, 这个存储空间可以被链接器reslove道引用这个Symbol的地方.
- 对开发者而言:
- 如果你希望使用某个已经存在的对象,那么就应该使用声明语句.
- 每个定义语句都有"声明"的效果,不存在只定义不声明的语句.
- 就C++而言,若语句仅有
extern
,且不含任何初始化部分,则该语句就是一个纯粹的声明,例如extern T &val;//纯声明
extern T val;//纯声明
extern T val(ob);//定义
extern T val=ob;//定义
初始化,赋值和析构
- 这三个概念很容易通过在特定位置插入函数调用来理解.
- 初始化:对应了构造函数的调用.
- 赋值:对应了赋值运算符的调用
- 析构:对应了析构函数的调用.
- 逻辑上说,所有的对象初始化操作都是在运行期,对应的构造函数将会被调用.
- 这将用于解释全局变量/static变量构造函数的调用时机.
- 内置类型的构造函数会有依场景由不同的行为:
- 静态区的数据0初始化
- 运行期的所有数据都随机初始化,如栈,堆,malloc,new等都是如此.
switch-case,goto
都可以用于跳过构造函数的调用,部分编译器会对这种行为报错,因为这可能导致对象处于错误的状态
new/delete
表达式都是复合操作,前者是malloc()+T()
后者是~T()+free()
- 对C++而言,
placement new
可以在特定地址手动的触发构造,这样创建的对象应当手动调用析构函数进行销毁,而不是使用delete
- 对于内置类型,直接
new
和malloc
的效果相同,获得的都是未初始化的内存,但是new
可以添加初始化参数,如new int(10) ;new int[10]{1}
- 对C++而言,
补充:栈/堆/静态区的对象
名称 | 功能 | 生命周期 |
---|---|---|
栈区(0xFFFF方向) | 存储函数调用栈帧,函数局部变量也存储在栈帧中 | 函数执行期间 |
堆区 | 由进程在运行期手动申请存储空间,并在其中创建对象 | 手动管理 |
静态区 | 编译系统在编译期预分配好的空间,主要供全局变量,static变量使用 | 整个进程 |
常量区(0x0000方向) | 存储运行时不会改变的量,如程序代码,字符串等 | 整个进程 |