编译期初始化:在源代码被编译过程中,编译期安插一些代码逻辑,完成确定的内存分配(并非实际分配内存,而是确定其大小占用,由编译期安插代码),变量的初始化。
如:全局变量为内置类型,并且大小确定
int a=2;
static int b=3; //static的不同只是其只在本文件中可见
static int c=a+b;
在编译期初始化,那么在实际运行期都是确定的结构和逻辑,将带来更高的性能,因为编译器完成了一定的工作。
加载时初始化:在main()函数执行前,完成包括全局变量,静态变量的初始化:
例如全局的类对象:
class A
{
public:
virtual void fun()
{
cout<<"funciton A:fun() call"<<endl;
}
};
A a;//将在main执行前,加载时初始化
int main()
{
......
全局类对象在main函数执行前,由加载程序完成其初始化,其无法在编译期初始化,由于那时候还无法调用类的构造函数。
同时,在加载期,是线程安全的。例如,饿汉方式的单例类:
借助main执行前的加载期完成初始化,由于还在加载所以确保线程安全。
class A
{
private:
A(){}
static A* instance;
public:
static A* getInstance()
{
return instance;
}
};
A* A::instance=new A();
int main()
{
}
运行期初始化:
指代实际程序运行期间对象(变量)的创建,包含那些动态创建的对象。由于编译和加载时无法确定大小,因此它们只能延迟到运行期才能完成初始化,将带来程序的性能开销。并且由于运行期间可能是多线程环境,对于共享变量,还可能带来线程安全问题。
int main()
{
int* p=new int[N];
A* arr=new A(0);
}
另外针对静态变量,若其是普通的具有本文可见性的普通静态变量
其可能在编译期(内置类型)初始化或者在加载期(类的静态成员)初始化。
但针对函数内部的局部static变量,其在第一次被调用时初始化,并且只初始化一次。
语言的语法是针对源代码是否违反规定,其发生在编译期,一般认为在编译期出现的错误是及时有效的,而不至于延迟到运行期再发生错误,那样更为不可控。
一个常见的例子是,类的访问权限只会在编译期进行检查,因为这些权限是语言特性,仅仅会被编译器进行检查,确保符合规范,编译链接后,成为机器代码,这些语言特性也不复存在。
class A
{
public:
virtual void fun()
{
cout<<"A:fun() call"<<endl;
}
};
class B:public A
{
private:
void fun()
{
cout<<"B:fun() call"<<endl;
}
};
int main()
{
A* ptr=new B(); //编译期没有问题
ptr->fun();
}
这段代码编译期没有问题,因为ptr被视作A类指针,其fun()是pulibc的
同时,在运行期也没有问题! 因为运行期不会在检查是否符合语言规范(也不可能检查)
可以看到,基于虚函数机制,B的private函数被正确调用