Singleton パターン(シングルトン・パターン)とは、オブジェクト指向のコンピュータプログラムにおける、デザインパターンの1つです。GoF (Gang of Four; 4人のギャングたち) によって定義されました。Singleton パターンを使用することで、そのクラスのインスタンスが1つしか生成されないことを保証するデザインパターンです。
Singleton パターン、の最初のサンプルプログラムを作成してみます。
「Meyers シングルトン」と呼ばれるシンプルなシングルトン型の Logger を紹介します。
主な特徴は以下の通りです。
NOTE
Meyers シングルトンとは、Scott Meyers が提唱した「関数内静的変数を使ったシングルトン実装」のことです。簡潔に説明すると以下の通りです。
| コンパイラ : | g++ | 13.3.0 |
| OS : | Ubuntu | 24.04.3 LTS |
POINT
["logger.h"]
// logger.h
#pragma once
#include <atomic> // for std::atomic
#include <mutex> // for std::mutex
#include <string> // for std::string
namespace singleton_example {
/**
* @brief スレッドセーフなシングルトンとして実装されたロガークラスのヘッダ。
*
* Meyers シングルトンを用いて遅延初期化を行います。
*/
class Logger {
private:
mutable std::mutex mutex_;
std::atomic<std::size_t> counter_{0};
Logger() = default;
~Logger() = default;
public:
// コピー/ムーブを削除してシングルトンを保証
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
Logger(Logger&&) = delete;
Logger& operator=(Logger&&) = delete;
/**
* @brief シングルトン Logger のインスタンスを取得する。
*
* @return Logger& シングルトンの Logger インスタンスへの参照。
*/
static Logger& getInstance();
/**
* @brief メッセージをログ出力する。
*
* @param message 出力するログメッセージ。
*/
void log(const std::string& message);
/**
* @brief これまでにログ出力したメッセージ数を取得する。
*
* @return std::size_t 出力済みメッセージの総数。
*/
std::size_t getCount() const;
};
} // namespace singleton_example
["logger.cpp"]
// logger.cpp
#include "logger.h"
#include <iostream>
namespace singleton_example {
Logger& Logger::getInstance()
{
static Logger instance;
return instance;
}
void Logger::log(const std::string& message)
{
std::lock_guard<std::mutex> lock(mutex_);
++counter_;
std::cout << "[" << counter_ << "] " << message << std::endl;
}
std::size_t Logger::getCount() const
{
return counter_.load();
}
} // namespace singleton_example
["singleton_pattern_01.cpp"]
// singleton_pattern_01.cpp
// C++23 サンプル: スレッドセーフな Meyers スタイルのシングルトン(簡単な使用例)
// プロジェクト規約に従っています: 関数は camelCase、変数は snake_case、公開 API は Doxygen コメントを使用
#include <iostream>
#include <thread> // for std::thread, std::this_thread::yield
#include <vector> // for std::vector
#include "logger.h"
using singleton_example::Logger;
int main()
{
// シングルトンへの同時アクセスを示すために複数スレッドを生成
const int num_threads = 6;
const int messages_per_thread = 5;
std::vector<std::thread> threads;
threads.reserve(num_threads);
for (int t = 0; t < num_threads; ++t) {
threads.emplace_back([t, messages_per_thread]() {
for (int i = 0; i < messages_per_thread; ++i) {
Logger::getInstance().log("thread " + std::to_string(t) + " message " + std::to_string(i));
// 出力が混在する可能性を高めるために少し yield する
std::this_thread::yield();
}
});
}
for (auto& th : threads) {
th.join();
}
std::cout << "合計ログ数: " << Logger::getInstance().getCount() << std::endl;
return 0;
}
ビルド方法:
上記3つのソースファイルを同じ場所に保存し、下記コマンドでビルドします。
g++ -std=c++20 -Wall -Wextra -Wpedantic -pthread singleton_pattern_01.cpp logger.cpp -o singleton_pattern_01.out
実行方法:
./singleton_pattern_01.out
実行結果:
$ ./singleton_pattern_01.out [1] thread 0 message 0 [2] thread 0 message 1 [3] thread 1 message 0 [4] thread 1 message 1 [5] thread 1 message 2 [6] thread 1 message 3 [7] thread 0 message 2 [8] thread 0 message 3 [9] thread 3 message 0 [10] thread 4 message 0 [11] thread 4 message 1 [12] thread 4 message 2 [13] thread 4 message 3 [14] thread 4 message 4 [15] thread 0 message 4 [16] thread 2 message 0 [17] thread 2 message 1 [18] thread 2 message 2 [19] thread 2 message 3 [20] thread 2 message 4 [21] thread 3 message 1 [22] thread 3 message 2 [23] thread 3 message 3 [24] thread 3 message 4 [25] thread 1 message 4 [26] thread 5 message 0 [27] thread 5 message 1 [28] thread 5 message 2 [29] thread 5 message 3 [30] thread 5 message 4 合計ログ数: 30 $
「静的メンバ変数方式」と呼ばれるシングルトン型の Logger を紹介します。
主な特徴は以下の通りです。
NOTE
| コンパイラ : | g++ | 13.3.0 |
| OS : | Ubuntu | 24.04.3 LTS |
[プログラムソース "logger.h"]
// logger.h
// Logger クラスの宣言(ヘッダ)
#pragma once
#include <atomic>
#include <memory>
#include <mutex>
#include <string>
/**
* @brief 明示的に破棄できるシングルトン Logger の宣言。
*
* 実装は logger.cpp にあり、スレッド安全な初期化と明示破棄をサポートします。
*/
class Logger {
private:
// メンバー変数(先に記載)
static std::unique_ptr<Logger> instance_ptr_;
static std::once_flag init_flag_;
mutable std::mutex mutex_;
std::atomic<std::size_t> counter_{0};
// unique_ptr のデフォルトデリータが private デストラクタを呼べるようにする
friend struct std::default_delete<Logger>;
// 特殊メンバはメソッド群の先頭に定義するが実装は cpp に置く
Logger() = default;
~Logger() = default;
public:
// コピー/ムーブを禁止
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
Logger(Logger&&) = delete;
Logger& operator=(Logger&&) = delete;
/**
* @brief シングルトンインスタンスを取得する。
*
* 初回呼び出し時にインスタンスを生成します(スレッド安全)。
*/
static Logger& getInstance();
/**
* @brief シングルトンを明示的に破棄する。
*
* 注意: 破棄後に他スレッドが Logger を使用していると危険です。
*/
static void destroyInstance();
/**
* @brief メッセージをログ出力する(スレッドセーフ)。
*/
void log(const std::string& msg);
/**
* @brief これまでに出力したメッセージ数を返す。
*/
std::size_t getCount() const;
};
[プログラムソース "logger.cpp"]
// logger.cpp #include "logger.h" #include <iostream> #include <memory> #include <mutex> #include <utility> // 静的メンバーの定義 std::unique_ptr<Logger> Logger::instance_ptr_; std::once_flag Logger::init_flag_; Logger& Logger::getInstance() { std::call_once(init_flag_, []() { instance_ptr_.reset(new Logger()); }); return *instance_ptr_; } void Logger::destroyInstance() { instance_ptr_.reset(); } void Logger::log(const std::string& msg) { std::lock_guard<std::mutex> lock(mutex_); ++counter_; std::cout << "[" << counter_ << "] " << msg << std::endl; } std::size_t Logger::getCount() const { return counter_.load(); }
[プログラムソース "singleton_pattern_02.cpp"]
// singleton_pattern_02.cpp
// 明示的に破棄できるシングルトン(std::unique_ptr + std::call_once)
// サンプル実装:スレッド安全にログ出力し、必要なら明示破棄が可能
#include <atomic>
#include <iostream>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include <utility>
#include "logger.h"
int main()
{
const int num_threads = 6;
const int msgs_per_thread = 5;
std::vector<std::thread> threads;
threads.reserve(num_threads);
for (int t = 0; t < num_threads; ++t) {
threads.emplace_back([t, msgs_per_thread]() {
for (int i = 0; i < msgs_per_thread; ++i) {
Logger::getInstance().log("thread " + std::to_string(t) + " message " + std::to_string(i));
std::this_thread::yield();
}
});
}
for (auto& th : threads) {
th.join();
}
std::cout << "Total messages: " << Logger::getInstance().getCount() << std::endl;
// 明示的に破棄する例(このプログラムでは不要だが動作する)
Logger::destroyInstance();
return 0;
}
[プログラムソース "Makefile"]
# Makefile for singleton_pattern_02 sample # # Targets: # all / build - build the executable # run - run the built executable # clean - remove built artifacts # help - show this help CXX := g++ CXXFLAGS := -std=c++23 -O2 -Wall -Wextra -pthread LDFLAGS := SRC_DIR := . SRCS := $(SRC_DIR)/logger.cpp $(SRC_DIR)/singleton_pattern_02.cpp OBJS := $(SRCS:.cpp=.o) TARGET := $(SRC_DIR)/singleton_pattern_02.out .PHONY: all build run clean help all: build build: $(TARGET) $(TARGET): $(SRCS) $(CXX) $(CXXFLAGS) -o $@ $(SRCS) $(LDFLAGS) run: $(TARGET) @echo "Running $(TARGET)" $(TARGET) clean: @echo "Cleaning..." -@rm -f $(TARGET) $(OBJS) help: @echo "Makefile for singleton_pattern_02 sample" @echo "Available targets:" @echo " make (or make all) - build the executable" @echo " make run - run the executable (builds first if needed)" @echo " make clean - remove build artifacts" @echo " make help - show this message"
ビルド方法:
make コマンドを実行するだけです
$ make g++ -std=c++23 -O2 -Wall -Wextra -pthread -o singleton_pattern_02.out ./logger.cpp ./singleton_pattern_02.cpp $
実行結果:
$ ./singleton_pattern_02.out [1] thread 0 message 0 [2] thread 0 message 1 [3] thread 0 message 2 [4] thread 0 message 3 [5] thread 0 message 4 [6] thread 1 message 0 [7] thread 1 message 1 [8] thread 1 message 2 [9] thread 1 message 3 [10] thread 1 message 4 [11] thread 2 message 0 [12] thread 2 message 1 [13] thread 2 message 2 [14] thread 2 message 3 [15] thread 2 message 4 [16] thread 3 message 0 [17] thread 3 message 1 [18] thread 3 message 2 [19] thread 3 message 3 [20] thread 5 message 0 [21] thread 5 message 1 [22] thread 5 message 2 [23] thread 4 message 0 [24] thread 4 message 1 [25] thread 4 message 2 [26] thread 4 message 3 [27] thread 4 message 4 [28] thread 5 message 3 [29] thread 5 message 4 [30] thread 3 message 4 Total messages: 30 $
NOTE
| コンパイラ : | g++ | 13.3.0 |
| OS : | Ubuntu | 24.04.3 LTS |
[プログラムソース "***.cpp"]
class Singleton {
private:
static Singleton* instance;
Singleton() = default;
Singleton() = default;
public:
// コピー/ムーブを削除してシングルトンを保証
Singleton(const Logger&) = delete;
Singleton& operator=(const Singleton&) = delete;
Singleton(Singleton&&) = delete;
Singleton& operator=(Singleton&&) = delete;
static Singleton* getInstance() {
if (!instance) {
std::lock_guard<std::mutex> lock(mtx);
if (!instance) {
instance = new Singleton();
}
}
return instance;
}
};
Singleton* Singleton::instance = nullptr;
std::mutex mtx;
NOTE
| コンパイラ : | g++ | 13.3.0 |
| OS : | Ubuntu | 24.04.3 LTS |
[プログラムソース "***.cpp"]
template <typename T>
class Singleton {
public:
static T& instance() {
static T instance;
return instance;
}
};
本ページの情報は、特記無い限り下記 MIT ライセンスで提供されます。
| 2025-11-01 | - | 新規作成 |