1 编译期初始化
在源代码被编译过程中,编译期会加入代码逻辑,以完成确定的内存分配 和 变量的初始化。分配内存,并非实际分配内存,而是写入其内存分配大小信息
如:全局变量为内置类型,并且大小确定
int a=2;?
static int b=3; ? //static的不同只是其只在本文件中可见
static int c=a+b;
在编译期初始化,那么在实际运行期都是确定的结构和逻辑,将带来更高的性能,因为编译器完成了一定的工作。
1.1 全局变量
全局变量在main函数第一次使用它之前,就把它初始化好。但这个初始化可细分为: 编译时初始化?和 加载时初始化,即static initialization?和?dynamic initialization。
(静态变量见 5 补充)
1.1.1 编译时初始化(静态初始化)
是针对于那些简单的、c++内部定义的数据结构(也称 内置结构),如int,double,bool? 及? 数组结构? 的初始化,又可分为两种方式:
- .bss段 ? ?未初始化的变量,也就是我们没指定初值,编译器分配0值给它,编译时编译器将其分配在.bss段,不占用rom空间
- .data 段? 已初始化的变量,也就是我们指定了初值,编译器将其分配在.data段,占用rom空间。
2 加载时初始化
全局类对象在main函数执行前,由加载程序完成其初始化,其无法在编译期初始化,由于那时候还无法调用类的构造函数。
同时,在加载期,是线程安全的。例如,饿汉方式的单例类:
借助main执行前的加载期完成初始化,由于还在加载所以确保线程安全。
class A
{
private:
A(){}
static A* instance;
public:
static A* getInstance()
{
return instance;
}
};
A* A::instance=new A(); //main 执行前的加载期,完成的初始化
int main()
{
}
3 运行期初始化
指代实际程序运行期间对象(变量)的创建,包含那些动态创建的对象。由于编译和加载时无法确定大小,因此它们只能延迟到运行期才能完成初始化,将带来程序的性能开销。并且由于运行期间可能是多线程环境,对于共享变量,还可能带来线程安全问题。?
int main()
{
int* p=new int[N];
A* arr=new A(0);
}
另外针对静态变量,若其是普通的具有本文可见性的普通静态变量
其可能在编译期(内置类型)初始化或者在加载期(类的静态成员)初始化。
但针对函数内部的局部static变量,其在第一次被调用时初始化,并且只初始化一次。
类的静态成员
c++规定,const的静态成员可以直接在类内进行初始化,而非const的静态成员需要在类外声明以初始化,对于后一种情况,我们一般选择在类的实现文件中初始化。至此,具体的初始化方式和上面说的是一致的,可在编译期间初始化,也可在运行期间初始化。
在类中定义const变量有以下两个好处:
1.静态数据成员没有进入程序的全局名字空间,因此不存在与程序中的其他全局名字冲突的可能性。
2.可以实现信息的隐藏。
#include<iostream>
using namespace std;
class sb{
private:
const int i=5;
public:
void print();
};
4 总结
另外,静态初始化先于动态初始化。因为静态初始化发生在编译时期,直接写进.bss段和.data段,在程序执行时直接加载;而动态初始化则是在运行时期,由运行时库调用相应构造函数进行初始化,同样要写进.bss段或.data段。
5 补充
.data段和.bss段的区别:
.data段存放的是已初始化好的全局变量和静态变量; .bss段存放的是未初始化的全局变量和静态变量,给其赋0值【在有些编译器中,初始化为0的静态变量和全局变量也放在.bss段】 ?
|