Singleton 패턴이란?
- 객체가 단 하나만 필요할 때 사용하는 패턴
- 생성된 객체를 어디에서든지 참조할 수 있어야 한다
- ex) 설정파일의 설정값을 읽어 사용할 때
- 따라서 내부 값 변경이 많이 발생하는 클래스의 경우 singleton이 적합하지 않다
- 값 변경이 많은데 singleton을 써야 하는 경우가 있을까?
주의사항
- thread-safe한 패턴이 아니기 때문에 다음과 같은 문제가 있다
- 멤버 변수 접근 시 lock 처리
- instance 객체 생성 시 동시성 문제 처리
- 이 경우 생성 시점을 일찍/늦게 둔다든지, lock 처리를 한다든지 여러 방법이 있지만 여기에서는 생략하도록 함
생성 예제
// Header file
class Configuration {
public:
/**
* 이 부분은 구현 방식에 따라 달라질 수 있는데, 나는 이 방식을 주로 사용한다.
* static 변수의 특징은 프로세스 시작 시점에 바로 메모리에 올라온다는 점인데,
* 이 특징을 사용하여 getInstance() 함수처럼 instance에 대한 반환을 요청하지 않아도 되고,
* getInsatnce()를 호출할 때마다 instance의 생성 여부를 확인하는 단점을 해소할 수도 있기 때문이다.
*
* 물론 프로세스 시작 이후 한 번도 호출되지 않을 경우 불필요한 메모리 공간을 낭비한다는 단점은 있지만,
* 동시 접근 문제로 인한 골치를 해결할 수 있다는 것을 더 큰 이점으로 생각하고 이 방법을 주로 사용하는 편이다
*/
static Configuration instance;
public:
/**
* singleton 객체를 참조하는 곳에서 호출할 수 있는 멤버함수를 정의한다
*/
int getReadCount();
// std::string getTargetFileName();
void getTargetFileName(std::string& targetFileName);
int getTargetPos();
private:
/**
* 외부에서 객체 생성을 임의로 할 수 없도록, 생성자와 소멸자는 private으로 지정한다
*/
/**
* 생성자
* 생성자는 당연히 매개변수가 없다
* 매개변수가 필요하다는 것은, 생성되는 객체마다 다른 데이터가 지정되어야 한다는 의미인데
* 그건... singleton을 쓰는 메리트가 없지 않을까?
*/
Configuration();
/**
* 객체 생성 시점에 필요한 경우 init을 진행할 수도 있다
*/
void init();
/**
* 소멸자
*/
virtual ~Configuration();
private:
// 멤버 변수 정의
int m_readCount;
std::string m_targetFileName;
int m_targetPos;
};
// source file
// 이 코드를 반드시 cpp 파일에 추가해야 오류가 발생하지 않는다
Configuration Configuration::instance;
/**
* 생성자
*/
Configuration::Configuration() {
m_readCount = 0;
m_targetPos = 0;
}
/**
* 소멸자
*/
Configuration::~Configuration() {
}
/**
* 초기화 함수
*/
void Configuration::init() {
// 필요한 초기화 동작을 수행하면 된다
// TODO
m_readCount = 1;
m_targetPos = 10;
m_targetFileName = "testfile.txt";
}
/**
* getter
*/
int Configuration::getReadCount() {
return m_readCount;
}
void Configuration::getTargetFileName(std::string &targetFileName) {
targetFileName.clear();
targetFileName = m_targetFileName;
}
int Configuration::getTargetPos() {
return m_targetPos;
}