Singleton
The Singleton Pattern: ensures a class has only one instance, and provides a global point of access to it.
只有一个实例的类,如下是若干考虑:
首先,要产生类实例,需要调用构造函数。为了防止用户申明或者new一个类的实例,我们可以把这个类的构造函数设置为protected或者private,那么用户申明或者new就不可能编译通过,当然,delete也是不允许的,也申明为protected或者private
可是,我们在哪里创建类实例呢?虽然外部不能创建类的对象,但是类的内部,是可以调用构造函数,从而可以创建类实例的。我们给类的外部提供一个方法GetInstance()。它在没有类实例时创建类实例并返回实例(指针或引用);在有实例时,直接返回已有实例。这样我们就需要保存实例的指针以备GetInstance()使用,该指针也应该定义在类上,否则在没有类实例之前,怎么给这个成员变量赋值呢?如此,第一个版本得到:
class Singleton
{
public:
static Singleton& Instance()
{
if (!si_instance)
{
si_instance = new Singleton;
}
return *si_instance;
}
private:
//禁止外部构造函数调用
Singleton(){}
//对象静态指针,用于保存创建的对象
static Singleton* si_instance; //声明静态数据成员
};
Singleton* Singleton::si_instance=0; //定义并初始化全局存贮,局部作用的静态数据成员
Static用在类外:
static和extern都是變量的存贮类型,申明為static的變量,不能被其他文件(指目标文件)引用,extern可以被本文件和其他文件引用(指目标文件)。不能同時使用它們申明同一個變量。它們的共同點是存放位置都在程序的static memory中(全局存贮)。
其中static可以用于局部變量的申明,此時每次Call Function時,其中的Static局部變量的值不執行初始化,即保留上一次引用結束後的值。
extern是default的申明。
不提倡使用static局部變量。
具體的使用還與編譯器有關,以上的說明建立在ANSI C所定義的C Complier基礎上。
CPP文件产生目标文件,显然,CPP文件include的头文件中的static或extern变量在CPP中仍然有效(非只在头文件中有效)。链接时,static变量只在本obj内可引用,而extern则在所有obj内可引用(这样就可能会重名,出现重定义错误,所以才引入static关键字)。对于静态全局函数,也是用于限制这个全局函数在本obj文件中。
Static用在类定义中:
类是类型的定义。类中的所有成员的定义均是一个申明,说明这个类的内部构成。只有产生类的实例的代码,才需要为类的成员分配存贮空间,或在栈上,或在堆中。
为处理定义在类上的变量,我们用static修饰该成员。这时static的语义是该成员是一个extern类型的全局变量。由于类定义是申明,比如在类fun中申明static int i,而i并没有被定义(产生实例),我们需要在类的外部定义并初始化fun::i,这就是为什么类中的静态变量必须初始化,且必须在类外初始化。
然而,这又带来了额外的问题,那就是如果在多个CPP都包含了类fun定义文件,那么对fun::i的定义并初始化语句就会在多个CPP文件中出现,链接就无法通过!
这就是为什么要区分头文件和CPP文件的原因。我们把“申明”写在头文件中,把“定义”(也就是会产生实例的代码)写在CPP文件中,因为我们从不include CPP文件,所以,定义fun::i只有一次!!!这个原则同样适用于类的方法。类的方法f()如果在类的外部实现,也应该写在CPP中,否则当头文件被多个CPP文件引用时,就会出错。如果把成员函数体直接写在类中,那么不会有问题,尽管多个包含类头文件的CPP文件的obj都产生了fun::f(),但是,这个函数定义的作用域不会超出这个类的代码块,因此不会冲突。编译时实际上产生了这个函数的多个副本,每个副本只在局部作用。链接时用到的副本会写入exe文件,甚至被处理为内联函数。相对的,如果把fun::f()写在类的实现CPP文件中,那么就只在编译时产生一个fun::f()代码,看起来编译会较少花费时间。
对于类的成员函数,用static修饰时,表示它是一个定义在类上的方法,函数并没有存贮类别一说,这个寒暑不需要传递this指针就可以调用,即通过类名就可以引用,当然,如果创建了类对象,通过对象也可以引用。普通的成员函数则需要通过this指针来使用(实际上是传递了this指针给那个函数)
小结:在定义性代码(类外的代码)中,static保证修饰对象生存期为程序运行全过程,在代码块中定义,作用域在代码块中,在代码块外(全局定义),作用域是obj文件
在申明性代码中(类中),static保证修饰对象的生存期是程序执行全过程,作用域是全局,因为是申明,不是定义,所以对于成员变量需要在类外部定义全局的成员,对于成员函数在外部和内部定义都可以,因为static仅说明不传递this指针给该函数。
这个版本有一个问题,就是对象创建了,可是在哪里删除这个对象呢?我们是在类的内部new对象的,外部没有new,那么外部也不应该去delete。Si_instance是一个静态变量,用来保存指针ÿ