1. static的作用? #
static在C++中有多种用途:
- 隐藏:当
static用于全局变量/函数时,会改变变量/函数的可见性,静态的全局变量/函数只能在定义它们的源文件中可见。这可以用来实现模块内的封装。比如我在我的Webserver中的epoller.cpp中定义了一个静态全局函数:
/*
ET 模式下,read 必须一直读到返回 EAGAIN(缓冲区空了)。如果
fd 是阻塞模式,读到空时会卡住整个线程,其他连接全饿死。
*/
static void set_nonblocking(int fd)
{
int old_flags = fcntl(fd, F_GETFL, 0); // 取当前flags
fcntl(fd, F_SETFL, old_flags | O_NONBLOCK); // 加上非阻塞
}这个函数是epoller模块的内部细节,不暴露给外部。
- 保持局部变量持久且唯一:当
static用于局部变量时,该变量的生命周期会持续到整个程序执行期间。这个静态的局部变量在第一次执行包含它的函数的时候被初始化,然后在程序的整个生命周期内保持不变(也就是不被销毁),即使函数多次调用,还是和上次的变量一样。比如在单例模式中,我的服务器代码中就用到了静态局部变量来实现Log日志的单例:
/* /include/log.h */
class Log
{
// 构造函数定义为私有方法,禁止外部实例化
private:
Log();
~Log();
...
// 提供一个公有静态方法来获取唯一实例
public:
static Log* get_instance();
...
};
/* log.cpp */
Log* Log::get_instance()
{
// 局部静态变量,第一次调用时被初始化,之后保持不变
static Log instance;
return &instance;
}
单例模式
是一种软件设计模式,思路是:一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。 单例模式在多线程的应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例,这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。 解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。 但是在C++11中,局部静态变量的初始化是线程安全的,因此可以直接使用局部静态变量来实现单例模式,而不需要额外的锁机制。
-
静态类成员变量:当
static用于类成员变量时,该变量属于整个类,而不是某个特定的对象。所有对象共享同一个静态成员变量,而不是为每一个实例都分配独立的存储空间。 -
静态类成员函数:当
static用于类成员函数时,该函数不依赖于任何对象实例,可以直接通过类名调用。静态成员函数只能访问静态成员变量和其他静态成员函数,不能访问非静态成员变量或函数。比如,在定义“工具型”函数的时候,可以将其定义为静态成员函数。在Qt中QFileDialog类中就有一个静态成员函数getOpenFileName,它可以直接通过类名调用,而不需要创建QFileDialog的实例:
// static public member: getOpenFileName
// 可以直接用类名来使用这个成员,作为**工具类函数**
QString fileName = QFileDialog::getOpenFileName(this, "Open the file");就像是一个工具一样,用的时候直接用类名调用即可。