之前做项目的时候,涉及到单例模式的有关内容,但之所以拿出来讲,不仅仅是因为设计模式,还涉及到static成员变量的有关知识点;
单例模式的概念
单例模式是一种设计模式,用于确保一个类只有一个实例,并提供一个全局访问点来访问该实例。
这在某些情况下很有用,比如当只需要一个对象来协调某些共享资源、控制特定操作的时候,或者为了避免创建大量相同的对象而造成资源浪费。
单例模式常用的一种场景,比如我设计了一个服务器,需要给服务器加上日志打印模块,显然在服务器运行过程中,我们只需要保证日志处理的实例只有一个,从而集中管理和控制日志输出,提高代码的可维护性和可读性。
单例模式的特点
一般而言,单例(模式)类有这些特点,它会包含:
- 私有构造函数
- 单例类之所以将构造函数设定为私有的,目的就是为了防止外部代码直接创建实例,失去单例模式的作用;
- 静态实例变量
- 单例类通常有一个静态变量,该变量主要是为了保存单例实例;
- 静态成员函数
- 该成员函数的目的主要是为了获取单例实例;
单例模式常用的实现方式有两种:
- 饿汉式单例:在类加载的时候就创建单例实例,保证全局唯一性。优点是线程安全,但可能在实际使用时造成资源浪费,因为即使没有使用也会创建实例。
- 懒汉式单例:在第一次使用时才创建单例实例,避免了资源浪费。但需要考虑线程安全问题,可以通过同步方法、双重检查锁等方式来实现线程安全。
单例模式在多线程环境下的使用确实是需要尤为注意的一点,因为某种程度上,单例模式类也是一种共享资源;
我们看一段代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Log {
public:
// ....
static Log* Instance(); // 该函数用来获取静态实例
{
static Log inst; // 全局的日志实例,保证了在整个应用程序中只有这一个实例
return &inst; // 由于是static的,作用域结束也不会被销毁
}
// ....
private:
// .....
Log(); // 构造函数设计为private的
virtual ~Log();
// .....
};
显然,上面这段代码属于懒汉式单例;
此外,对单例类的使用还需要注意线程安全,但是一般而言我们作为服务器日志打印系统,服务器程序的实例正常情况下只有一个,因此只要是在服务端下调用日志,线程安全是有保证的;
说到这里,这边再添加一些额外的思考;
补充思考
这部分之所以配合单例模式理解,原因主要在于一个众所周知的知识点:类的静态成员函数不得访问类的非静态成员(函数);
- 因为静态成员没有隐含的
this指针,找不到类的(非静态)成员
那么,请问构造函数算类的成员函数吗?
如果算,为什么上述代码中可以在静态成员中去构造Log对象呢?
这是一个思考了许久的问题,目前的理解方式是这样的:静态成员确实因为没有隐含的this指针,无法访问类的非静态成员,但是访问类的构造函数,需要this指针吗?显然,在构造函数执行之前,甚至不存在所谓的this指针,类似我们看这么一段代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;
class A {
public:
A() {}
void func() { cout << "This is func" << endl; }
~A() {}
}
int main() {
A a; // 执行这段需要this指针吗?不需要
a.func(); // 通过隐含的this指针访问func()
func(); // 访问错误,找不到该函数
}
因此类比到类的静态成员函数,静态成员函数在自身的作用域内,是可以通过构造函数去构造对象的,就像main函数,可以在自己的作用域内构造对象一个道理;
不知道目前的理解够不够准确,随时补充!