2013-10-02 3 views
4

SDL을 사용하여 취미로 2D 게임 엔진을 프로그래밍하는 동안 현재 C++ 11 스마트 포인터를 사용하는 방법을 배우고 있습니다. 그러나 SDL에 대한 OOp 래퍼를 구현하는 동안 문제가 발생했습니다.싱글 톤에 대한 공유 포인터가 서로 인식하지 못합니다.

의도는 SDL이 생성 될 때 초기화하고 SDL이 소멸 될 때 SDL을 종료하는 싱글 톤 클래스를 만드는 것입니다. 싱글 톤 클래스는 을 싱글 톤으로 반환하는 정적 메소드 getInstance을 가지며, 인스턴스가 없으면 단일 인스턴스를 구성합니다. 싱글 톤의 모든 클라이언트는 자신에게 shared_ptr을 소유하고, 모든 클라이언트가 파괴되면 singleton 또한 파괴되었다. 싱글 톤 (및 다른 전역 변수)은 일반적으로 좋지 않다고 생각하지만 싱글 톤이 적합한 몇 가지 경우 중 하나 일 수 있습니다. 사용중인 SDL 라이브러리가 하나 밖에 없기 때문에 가능합니다.

getInstance 메서드에서 shared_ptr을 반환하는 데 문제가 있습니다. 동일한 shared_ptr 관리자 개체를 사용하는 대신 shared_ptr 인스턴스는 관련이 없으며 단일 인스턴스를 삭제하면 해당 단일 개체가 할당 취소됩니다.

#include <iostream> 
#include <memory> 
using namespace std; 

class Foo 
{ 
public: 
    ~Foo(){cout << "Foo <" << this << "> destroyed\n"; instance_ = nullptr;} 
    static shared_ptr<Foo> getInstance() 
    { 
     if(instance_ == nullptr) 
      instance_ = new Foo; 
     //problem: the shared pointers created are unaware of each other 
     return shared_ptr<Foo>(instance_); 
    } 
private: 
    Foo(){cout << "Foo <" << this << "> constructed\n";} 
    Foo(Foo& other){} 
    void operator=(Foo& other){} 
    static Foo* instance_; 
}; 

Foo* Foo::instance_ = nullptr; 

int main() 
{ 
    shared_ptr<Foo> a = Foo::getInstance(); 
    shared_ptr<Foo> b = Foo::getInstance(); 
    shared_ptr<Foo> c = Foo::getInstance(); 
} 

출력 :

Foo <0x3e2a10> constructed 
Foo <0x3e2a10> destroyed 
Foo <0x3e2a10> destroyed 
Foo <0x3e2a10> destroyed 
+0

@DyP 아, 같은 해결책. 나는 단지 궁금해 : 왜 당신은 대답하지 않았어? –

+0

@DanielFrey OP의 문제가 어디서 왔는지 설명하지 않기 때문에 – dyp

+1

'shared_ptr'은 근본적으로 잘못되었습니다. 당신은 작동하게 만들 수는 있지만 여전히 잘못되었습니다 :'shared_ptr'은 * shared ownership *을 부여합니다.그러나 싱글 톤은 소유권을 공유하지 않으며, 하나의 소유자 (해당 클래스의 컨텍스트 (예 : 스레드))와 해당 소유 인스턴스의 여러 소비자를 갖습니다. 다시 말해 : weak_ptr 또는 원시 포인터를 반환합니다. 아니면 제대로하고 * 포인터를 반환하지 * 참조. 여기에 포인터를 사용하는 것은 쓸모가 없습니다. –

답변

9

과 같이 할 수 귀하의 방법

static shared_ptr<Foo> getInstance() 
{ 
    static std::shared_ptr<Foo> instance = std::make_shared<Foo>(); 
    return instance; 
} 

C++ (11)이 또한 스레드 때문에 단 하나의 정적 인스턴스 (작성 그런 식으로 -safe)이며 항상 정적 인 shared_ptr에서 복사본을 반환합니다. 즉, 반환 된 모든 공유 포인터가 소유권을 공유 함을 의미합니다.

원래의 시도는 동일한 일반 포인터에서 shared_ptr의 개별 인스턴스를 만들었지 만 다른 공유 포인터는 서로에 대한 지식이 없으므로 각각 고유 한 "공유 카운트"가 있으므로 분리 된 소유권이됩니다.


업데이트 : 질문 다시 읽기 프로그램이 끝날 때까지 평생을 연장하고 싶지는 않다고 생각합니다. 모든 반환 공유 포인터가 범위를 벗어나 갔다 즉시 인스턴스를 파괴하는이 방법을 고려하십시오

static std::shared_ptr<Foo> getInstance() 
{ 
    static std::weak_ptr<Foo> instance; 
    static std::mutex mutex; // (a) 
    const std::lock_guard<std::mutex> lock(mutex); // (b) 
    if(const auto result = instance.lock()) return result; 
    return (instance = std::make_shared<Foo>()).lock(); 
} 

삭제할 수 어디 라인 표시 (a)와 (b)를 사용하여 멀티에없는 경우 쓰레드 환경. 이것은 당신이 getInstance()로부터받은 모든 공유 포인터 밖으로 범위 가서 인스턴스가 삭제 된 경우, getInstance()에 대한 다음 호출이 Foo의 또 다른 새 인스턴스를 만들 것을 의미, 실제로 소위 피닉스 - 싱글 것을

참고.

+0

생성이 스레드 안전하다고 알고 있지만 thread-safe도'return'에 대해'atomic_load'가 필요한지 궁금합니다. – dyp

+0

공유 포인터가 모두 범위를 벗어나서 싱글 톤이 파괴 된 경우,'getInstance()'를 호출하면 모든 조건에서 새로운 싱글 톤을 인스턴스화 할 수 있습니까? – jms

+0

@ user1062874 로컬 정적 변수 때문에 항상 인스턴스가 있습니다. 대신에'weak_ptr'을 사용할 수 있고 빈'shared_ptr'을 생성한다면 생성을 강제해야합니다. – dyp

2

정적 멤버에 원시 포인터를 유지하는 대신 weak_ptr을 유지해야합니다. lock() function을 사용하여 shared_ptr으로 다시 변환하십시오. 비어있는 경우 새 싱글 톤 객체를 할당해야합니다.

static shared_ptr<Foo> getInstance() 
    { 
     shared_ptr<Foo> p = instance_.lock(); 
     if (!p) 
     { 
      p = new Foo; 
      instance_ = p; 
     } 
     return p; 
    } 
private: 
    static weak_ptr<Foo> instance_; 
+0

이것은 단일 스레드 프로그램에서만 작동합니다. –

+0

@DanielFrey, 사실. 문제 설명에서 멀티 스레드를 보지 못했습니다. 나는 또한 내 대답을 쓰는 동안 당신이 만든 편집을 보지 못했습니다. –

관련 문제