공부 일지/C++

[C++] 2.6 싱글톤 패턴 singleton pattern

Roble 2023. 9. 20. 00:46
  • 어떤 클래스의 객체가 프로그램 전체에서 단 하나만 만들어지도록 하는 것.
  • 오직 한개의 인스턴스만을 갖도록 보장.
  • 그래픽스 엔진이나 사운드 엔진에 종종 사용 된다.

 

 

 

 

싱글톤을 사용하는 이유

1. 객체 생성 없이 사용될 수 있게 한다.

사운드 엔진은 여러가지 많은 클래스, 객체에서 많이 사용된다. 사운드 기능을 쓰는 객체마다 계속 사운드 엔진 객체를 생성해내고 매개변수로 넘기고 하는 것 너무 낭비.


2. 사운드 엔진 객체를 딱 하나를 여러곳에서 공유 할 수 있게 한다.

사운드 엔진은 객체가 여러개일 필요가 없다. 단 하나면 된다. 사운드 엔진의 기능들만 가져다 쓰면 된다.
사운드 엔진의 map 에 저장되어있는 사운드들 굳이 다 객체로 찍어낼 필요 없이 기존에 만들어진 사운드 엔진 객체를 공유하는게 더 효율적이다.

 

 

 

 

 

싱글톤 구성법

  • 사운드 엔진 클래스에서 자기 자신(사운드 엔진 객체)을 static 변수에 넣어두면 된다.
  • 사운드 엔진 객체는 딱 1개만 생성될 수 있게 여러 장치(예를들면 class의 private)를 통하여 제어한다
    • 생성자를 private로 두어 객체로 찍어내는 것을 막는다
    • static 변수에 사운드 엔진 객체를 넣어줄 때 변수 값이 null일때만 넣는다

 

 

 

 

static 멤버 변수 특징

  1. static 멤버 변수의 값을 모든 인스턴스들이 공유할 수 있다.
  2. 인스턴스 생성 없이도 클래스 이름으로 접근하여 사용할 수 있다.
  3. 딱 한번만 생성된다.
  4. 초기화는 반드시 클래스 밖에서 해준다.
    1. 초기화는 보통 cpp 파일에서 따로 초기화 1회 해주는 함수를 만들어서 해준다.
    2. 공유되는 변수이기 때문에 객체 생성시마다 변수가 중복 선언 되는것을 방지 하기 위해

 

 

 

 

 

싱글톤을 사용하지 않고 사운드 엔진을 객체로 생성하여 사용한 경우 문제점

  • 사운드엔진 객체를 생성해주는 것은 컴퓨터 사운드 카드에게 너무 부담 되는 일이다.
  • 예를 들어 총알은 계속해서, 많이 생성되기 때문에 그때마다 게속 사운드엔진 객체가 또 생기고 또 생기고 하게된다.

 

 

 

 

 

 

 

 

 

 

전체 코드

강사님이 사운드 엔진에 대한 이해를 돕기 위해 만드신 것으로 실무엔 비효율적인 코드임

#pragma once

#include "fmod.hpp"
#include <iostream>
#include <map>
#include <string>

namespace jm
{
	//Note: This example implementation of sound engine is inefficient.
	class SoundEngine
	{
	private:
		FMOD::System  *system = nullptr;
		//FMOD::Channel *channel = nullptr;
		std::map<std::string, FMOD::Sound*> sound_map;
		std::map<FMOD::Sound*, FMOD::Channel*> channel_map;// not efficient
		
		FMOD_RESULT   result;
		unsigned int  version;
		void          *extradriverdata = nullptr;

	public:
		SoundEngine()
		{
			using namespace std;

			result = FMOD::System_Create(&system);
			if (result != FMOD_OK) {
				cout << "FMOD::System_Create() fail" << endl;
				exit(-1);
			}

			result = system->getVersion(&version);
			if (result != FMOD_OK) {
				cout << "getVersion() fail" << endl;
				exit(-1);
			}
			else printf("FMOD version %08x\n", version);

			result = system->init(32, FMOD_INIT_NORMAL, extradriverdata);
			if (result != FMOD_OK) {
				cout << "system->init() fail" << endl;
				exit(-1);
			}
		}

	public:
		~SoundEngine()
		{
			system->release();
		}

		void createSound(const std::string & filename, const std::string & sound_name, const bool & use_loop)
		{
			sound_map[sound_name] = nullptr;

			auto & sound_ptr = sound_map[sound_name];

			const int loop_flag = use_loop ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF;

			result = system->createSound(filename.c_str(), loop_flag, 0, &sound_ptr);
			
			if (result != FMOD_OK) {
				std::cout << "system->createSound() fail" << std::endl;
				exit(-1);
			}
		}

		void playSound(const std::string & sound_name)
		{
			if (sound_map.count(sound_name) <= 0) {
				std::cout << sound_name << " isn't initialized." << std::endl;
				exit(-1);
			}

			const auto & sound_ptr = sound_map[sound_name];
			auto & channel_ptr = channel_map[sound_ptr];

			bool is_playing = false;
			result = channel_ptr->isPlaying(&is_playing);

			if (is_playing) return; // don't play if this is already playing

			result = system->playSound(sound_ptr, 0, false, &channel_ptr);

			if (result != FMOD_OK) {
				std::cout << "system->playSound() fail" << std::endl;
				exit(-1);
			}
		}

		void stopSound(const std::string & sound_name)
		{
			if (sound_map.count(sound_name) <= 0) {
				std::cout << sound_name << " isn't initialized." << std::endl;
				exit(-1);
			}

			const auto & sound_ptr = sound_map[sound_name];
			auto & channel_ptr = channel_map[sound_ptr];

			bool is_playing = false;
			result = channel_ptr->isPlaying(&is_playing);

			if (is_playing == false) return; // don't stop playing if this is not playing

			result = channel_ptr->stop();

			if (result != FMOD_OK) {
				std::cout << "system->playSound() fail" << std::endl;
				exit(-1);
			}
		}
	};
}