一文の説明
クラスにはインスタンスが 1 つだけあることを確認し、それ自体をインスタンス化し、このインスタンスをシステム全体に提供します。
理解
-
「クラスにはインスタンスが 1 つだけあることを確認してください」
- これには、その構築メソッドがパブリックであってはならず、外部の世界から気軽に呼び出されてはならない、つまり、外部の世界によってインスタンス化できないため、その構築メソッドはプライベートのみである必要があります。
-
「そして自分自身をインスタンス化し、このインスタンスをシステム全体に提供します」
-
インスタンスが 1 つだけの場合、このインスタンスがシステム全体に提供される場合、このインスタンスはこのクラスに属している必要があるため、このインスタンスはクラス メンバー変数、つまり静的変数でなければなりません。
-
さらに、このインスタンスをシステム全体に提供するには、1 つのみ
静的メンバーメソッド
。
C++ では、静的変数には静的メンバー メソッドを介してのみアクセスできます。
-
効果
明らかに、シリアル番号生成、カウンター、グローバル管理クラスなど、クラスのインスタンスが 1 つだけ存在することを保証することは、ソフトウェア アーキテクチャで広く使用されています。
2つの実装
シングルトン クラス オブジェクトのインスタンス化期間に応じて、シングルトン クラスの実装は 2 つのカテゴリに分類できます。
お腹を空かせた中華風
#include <iostream>
class Only {
public:
// 提供类实例的唯一接口,静态成员方法
static Only* GetInstance() {
return m_singleton;
}
private:
// 类构造函数一定是private修饰
Only () {
};
// 类实例对象一定是一个静态变量
static Only* m_singleton;
}
// 静态成员变量(属性)类外初始化,注意需要在cpp文件初始化
Only* Only::m_singleton = new Only();
明らかに、プログラムがロードされた時点で空腹の中華スタイルの実装が完了するため、より不安なため、空腹の中華スタイルと呼ばれます。
怠惰
#include <iostream>
class Only {
public:
// 提供类实例的唯一接口,静态成员方法
static Only* GetInstance() {
if(NULL == m_singleton) {
m_singleton = new Only();
}
return m_singleton;
}
private:
// 类构造函数一定是private修饰
Only () {
};
// 类实例对象一定是一个静态变量
static Only* m_singleton;
}
// 静态成员变量(属性)类外初始化
Only* Only::m_singleton = NULL;
明らかに、遅延スタイルのインスタンスの初期化は、最初の呼び出しによってクラス インスタンス インターフェイスを取得するときに遅延され、呼び出されない場合は初期化されません。
ダブルチェックロック
Lazy Manの実装原理から、マルチスレッドの場合、メソッドがstatic Only* GetInstance()
複数のスレッドから同時に呼び出される可能性があるため、static Only* singleton
複数回初期化され、メモリ リークが発生することが予想されます。毛糸にしようかな?ロック。
#include <iostream>
#include <mutex>
// 锁
mutex m_Only_mutex;
class Only {
public:
// 提供类实例的唯一接口,静态成员方法
static Only* GetInstance() {
m_Only_mutex.lock();
if(NULL == m_singleton) {
m_singleton = new Only();
}
m_Only_mutex.unlock();
return m_singleton;
}
private:
// 类构造函数一定是private修饰
Only () {
};
// 类实例对象一定是一个静态变量
static Only* m_singleton;
}
// 静态成员变量(属性)类外初始化
Only* Only::m_singleton = NULL;
これだけで十分だと思われますが、実際には最適化できる箇所が 2 か所あります。
-
ロックの範囲はさらに狭くすることができます。結局のところ
static Only* GetInstance()
、このインターフェイスは複数回呼び出される可能性があるため、null ステートメントは内部でロックされる必要があり、その後、複数のスレッドが同時に null ステートメントに対して実行される可能性があるため、次の最初の文でミューテックス領域では、null チェックが 1 回実行されるため、二重チェック ロックが実行されます。 -
マルチスレッドを使用する場合、現在のシングルトン クラス インスタンス オブジェクトを volatile で変更する必要があります。なぜですか? なぜなら
m_singleton = new Only();
3 つのステップに分けることができます。
- メモリの割り当て
- オブジェクトを初期化する
m_singleton
割り当てられたばかりのメモリを指します
この操作プロセスは実際には非アトミック操作です。つまり、このプロセス中に CPU の順序が変更される可能性があります。例: スレッド A はステップ 1 とステップ 3 を実行しましたが、ステップ 2 はまだ実行していません。この時点では、
m_singleton
スレッド A は空ではありません。スレッド B がスレッド B より前に呼び出すと、static Only* GetInstance()
B は初期化されていないが貴重なスレッドを取得しますm_singleton
。並べ替えを避けるために、揮発性の変更を使用する必要があります。
#include <iostream>
#include <mutex>
// 锁
mutex m_Only_mutex;
class Only {
// 提供类实例的唯一接口,静态成员方法
static Only* GetInstance() {
// 双重检查锁
if(NULL == m_singleton) {
m_Only_mutex.lock();
if(NULL == m_singleton) {
m_singleton = new Only();
}
}
m_Only_mutex.unlock();
return m_singleton;
}
private:
// 类构造函数一定是private修饰
Only () {
};
// 类实例对象一定是一个静态变量
static Only* volatile m_singleton;
}
// 静态成员变量(属性)类外初始化
Only* Only::m_singleton = NULL;
メモリ回復の最適化
シングルトン クラスのインスタンスの場合、ファイルを閉じたり外部リソースを解放したりするなど、現在のインスタンスを解放する必要がある場合があります。ただし、インスタンスは静的メンバー変数であるため、クラスの破壊的な動作ではこの要件を満たすことができません。インスタンスを正常に削除する方法が必要です。
-
プログラムの最後に呼び出すことができます
GetInstance()
そして、返されたポインターに対して削除操作を呼び出します。
これは機能的ではありますが、あまり洗練されておらず、エラーが発生しやすくなります。このような追加のコードは忘れられやすく、削除後にコードが再度呼び出されないことを保証するのが難しいためです
GetInstance()
(空腹の中国風に)。 -
必要に応じて、このクラスに自分自身を削除するように通知します。つまり、自分自身を削除する操作をシステム内の適切な時点でハングアップし、適切なタイミングで自動的に実行されるようにします。
プログラムの終了時に、システムはすべてのグローバル変数を自動的に破棄します。実際、これらの静的メンバーがグローバル変数でもあるのと同様に、システムはクラスのすべての静的メンバー変数も破棄します。したがって、この機能を利用してシングルトン クラス インスタンスを実装し、プログラムの終了時にリソースを自動的に解放します。
#include <iostream>
#include <mutex>
// 锁
mutex m_Only_mutex;
class Only {
// 提供类实例的唯一接口,静态成员方法
static Only* GetInstance() {
// 双重检查锁
if(NULL == m_singleton) {
m_Only_mutex.lock();
if(NULL == m_singleton) {
m_singleton = new Only();
}
}
m_Only_mutex.unlock();
return m_singleton;
}
private:
class AutoRelease {
public:
AutoRelease() {
}
// 通过AutoRelease类的析构函数,释放m_singleton
~AutoRelease() {
if(m_singleton) {
delete m_singleton;
}
}
};
// 类构造函数一定是private修饰
Only () {
};
// 类实例对象一定是一个静态变量
static Only* volatile m_singleton;
// 类静态变量,用于程序退出时,单例类自动析构
static AutoRelease _autoRelease;
}
// 静态成员变量(属性)类外初始化
Only* Only::m_singleton = NULL;
Only::AutoRelease Only::_autoRelease;
参考文献リスト
- https://www.bilibili.com/video/BV1af4y1y7sS?spm_id_from=333.999.0.0
- https://blog.csdn.net/qq_29542611/article/details/79301595
- https://blog.csdn.net/weixin_44363885/article/details/92838607
- https://bbs.csdn.net/topics/600461189
- シングルトンモードのメモリ解放