singleton 클래스를 디자인하고 만드는 것에 대해 여러 글을 읽어 보았습니다. 그래서 이번에 정리 차원에서 이래 저래 검토해 본 “singleton 클래스 이야기”라는 글을 써 볼까 합니다. 본 글에서는 App라는 클래스(application 혹은 process의 정보를 제공하는 클래스)를 예를 들면서 설명을 해 보겠습니다.

위키피디아에서는 singleton을 다음과 같이 정의하고 있습니다. 즉 쉽게 말해서 객체(instance)가 하나뿐인 클래스라고 쉽게 생각하면 됩니다.

In software engineering, the singleton pattern is a design pattern used to implement the mathematical concept of a singleton, by restricting the instantiation of a class to one object.

여기에서 몇가지 전제 조건이 생기게 됩니다.

  • (A) instance가 함부로 생성되지 않도록 constrcutor 및 destructor를 public으로 선언하지 않고 private로 선언한다.

  • (B) instance의 복사가 허용되지 않도록 copy constructor 및 assign operator를 public으로 선언하지 않고 private로 선언한다.

  • (C) instance의 interface를 제공하기 위해 instance()라는 메소드를 public으로 제공한다.

상기 조건을 이용해서 App라는 클래스를 만들어 보겠습니다.

class App {
private: // private constructor and destructor prevent creating and deleting object. // (A)
  App();
  virtual ~App();

private: // private copy constructor and assign operator prevent copying object. // (B)
  App(const App&);
  const App& operator = (const App&);

public: // public instance methods supports class reference access. // (C)
  static App& instance() {
    static App app;
    return app;
  }
};

참고로 boost::noncopyable 이라는 클래스를 상속받아서 상기 요구 사항중 (B)를 해결할 수도 있습니다.

  • (A)를 private(protected가 아닌)으로 선언을 하는 것은 App 클래스의 상속을 허용하지 않기 위함입니다. singleton 클래스를 상속하면 안되는 이유는 추후에 다시 설명하겠습니다.

  • (B)를 public으로 선언하지 않은 이유는 객체의 복사를 방지하기 위해서입니다. (B)를 private으로 하느냐 protected로 하느냐는 별 의미가 없습니다. 어차피 클래스 상속이 (A)에 의해 차단이 되므로.

  • (C)에서는 app라는 object를 static local로 선언을 하였습니다. static local로 선언하는 것과 static global(member)로 선언하는 방식 2가지가 있을 수 있는데, 각각 장단점이 있습니다. 이는 추후에 자세한 설명을 하도록 하겠습니다.

설계된 App class를 이용해서 클래스에 접근을 해 보도록 하겠습니다. 의도하는 바대로 compile error가 나면서 객체의 외부 생성 및 복사를 방지함을 알 수가 있습니다.

void testA()
{
  App app; // 'App::App' : cannot access private member declared in class 'App'
           // 'App::~App' : cannot access private member declared in class 'App'
};

void testB(App* app1, App* app2)
{
  *app1 = *app2; // 'App::operator =' : cannot access private member declared in class 'App'
}

void testC()
{
  App& app = App::instance(); // compile ok
}
#include <boost/noncopyable.hpp>

class App : boost::noncopyable {
private: // private constructor and destructor prevent creating and deleting object. // (A)
  App();
  virtual ~App();
public:
  static App& instance() {
    static App app;
    return app;
  }
};

출처 : gilgil.net