DLLでLoki::SingletonHolderを使う場合の注意点
C++でSingletonクラスを作成するのに便利なのがLoki::SingletonHolderクラス。しかし、Loki::SingletonHolderはテンプレートクラスかつ静的メンバ変数でオブジェクトを管理しているため、ダイナミックリンクライブラリ(DLL)で使う場合には注意が必要だ。
直接Loki::SingletonHolderクラスを使った場合
package1/Foo.h
#ifndef PACKAGE1_FOO_H #define PACKAGE1_FOO_H #include "package1/package1.h" #include <loki/Singleton.h> namespace package1 { class PACKAGE1_API Foo { friend Loki::CreateUsingNew<Foo>; friend Loki::DefaultLifetime<Foo>; private: Foo(); Foo(const Foo &); Foo &operator=(const Foo &); ~Foo(); public: void func(); }; typedef Loki::SingletonHolder<Foo> FooHolder; } #endif
package1/Foo.cpp
#include "package1/Foo.h" #include <iostream> namespace package1 { Foo::Foo() { std::cout << "Foo::Foo() called, this: " << this << '\n'; } Foo::~Foo() { std::cout << "Foo::~Foo() called, this: " << this << '\n'; } void Foo::func() { std::cout << "Foo::func() called, this: " << this << '\n'; } }
package1/use_foo.cpp
#include "package1/use_foo.h" #include "package1/Foo.h" #include <iostream> namespace package1 { void use_foo() { std::cout << "-> package1::use_foo()\n"; Foo &foo = FooHolder::Instance(); foo.func(); std::cout << "<- package1::use_foo()\n"; } }
package2/use_foo.cpp
#include "package2/use_foo.h" #include "package1/Foo.h" #include <iostream> using namespace package1; namespace package2 { void use_foo() { std::cout << "-> package2::use_foo()\n"; Foo &foo = FooHolder::Instance(); foo.func(); std::cout << "<- package2::use_foo()\n"; } }
main/main.cpp
#include "package1/Foo.h" #include "package1/use_foo.h" #include "package2/use_foo.h" #include <tchar.h> #include <iostream> using namespace package1; int _tmain(int argc, _TCHAR* argv[]) { std::cout << "-> main()\n"; Foo &foo = FooHolder::Instance(); foo.func(); package1::use_foo(); package2::use_foo(); std::cout << "<- main()\n"; return 0; }
main.exeの実行結果
-> main() Foo::Foo() called, this: 003D80B8 Foo::func() called, this: 003D80B8 -> package1::use_foo() Foo::Foo() called, this: 003D8160 Foo::func() called, this: 003D8160 <- package1::use_foo() -> package2::use_foo() Foo::Foo() called, this: 003D8190 Foo::func() called, this: 003D8190 <- package2::use_foo() <- main() Foo::~Foo() called, this: 003D80B8 Foo::~Foo() called, this: 003D8190 Foo::~Foo() called, this: 003D8160
パッケージ毎にFooオブジェクトが生成されてしまっていることが確認できる。
これを防ぐには、Loki::SingletonHolderを直接使うのではなく、Loki::Singletonクラスを経由して使う。
Loki::Singletonクラスを経由してLoki::SingletonHolderを使った場合
package1/Foo.h
#ifndef PACKAGE1_FOO_H #define PACKAGE1_FOO_H #include "package1/package1.h" #define LOKI_SINGLETON_EXPORT PACKAGE1_API #include <loki/Singleton.h> #undef LOKI_SINGLETON_EXPORT namespace package1 { class PACKAGE1_API Foo { friend Loki::CreateUsingNew<Foo>; friend Loki::DefaultLifetime<Foo>; private: Foo(); Foo(const Foo &); Foo &operator=(const Foo &); ~Foo(); public: void func(); }; } #endif
package1/Foo.cpp
#include "package1/Foo.h" #include <iostream> namespace package1 { Foo::Foo() { std::cout << "Foo::Foo() called, this: " << this << '\n'; } Foo::~Foo() { std::cout << "Foo::~Foo() called, this: " << this << '\n'; } void Foo::func() { std::cout << "Foo::func() called, this: " << this << '\n'; } typedef Loki::SingletonHolder<Foo> FooHolder; } LOKI_SINGLETON_INSTANCE_DEFINITION(package1::FooHolder); template class Loki::Singleton<package1::FooHolder>;
package1/use_foo.cpp
#include "package1/use_foo.h" #include "package1/Foo.h" #include <iostream> namespace package1 { void use_foo() { std::cout << "-> package1::use_foo()\n"; Foo &foo = Loki::Singleton<Foo>::Instance(); foo.func(); std::cout << "<- package1::use_foo()\n"; } }
package2/use_foo.cpp
#include "package2/use_foo.h" #include "package1/Foo.h" #include <iostream> using namespace package1; namespace package2 { void use_foo() { std::cout << "-> package2::use_foo()\n"; Foo &foo = Loki::Singleton<Foo>::Instance(); foo.func(); std::cout << "<- package2::use_foo()\n"; } }
main/main.cpp
#include "package1/Foo.h" #include "package1/use_foo.h" #include "package2/use_foo.h" #include <tchar.h> #include <iostream> using namespace package1; int _tmain(int argc, _TCHAR* argv[]) { std::cout << "-> main()\n"; Foo &foo = Loki::Singleton<Foo>::Instance(); foo.func(); package1::use_foo(); package2::use_foo(); std::cout << "<- main()\n"; return 0; }
main.exeの実行結果
-> main() Foo::Foo() called, this: 003D80B8 Foo::func() called, this: 003D80B8 -> package1::use_foo() Foo::func() called, this: 003D80B8 <- package1::use_foo() -> package2::use_foo() Foo::func() called, this: 003D80B8 <- package2::use_foo() <- main() Foo::~Foo() called, this: 003D80B8
こうすることで、Fooオブジェクトが一つだけ生成される。
Modern C++ Design―ジェネリック・プログラミングおよびデザイン・パターンを利用するための究極のテンプレート活用術 (C++ In‐Depth Series)
- 作者: アンドレイアレキサンドレスク,Andrei Alexandrescu,村上雅章
- 出版社/メーカー: ピアソンエデュケーション
- 発売日: 2001/12
- メディア: 単行本
- 購入: 12人 クリック: 214回
- この商品を含むブログ (102件) を見る