2013-08-26 1 views
2

게임에 대해 다양한 유형의 자산을로드하고 유지 관리하는 ContentManager 클래스를 작성하려고합니다 (XNA's ContentManager과 비교). 내 헤더 파일처럼 보이는 다음C++ : 복사 작업이 불합리하거나 불가능한 리소스를 저장하는 중

class ContentManager 
{ 
public: 
    ContentManager(Direct3D& d3d, const std::string& rootDirectory = "Resource"); 
~ContentManager(); 

    template<typename T> 
    const T& Load(const std::string& assetName); 

private: 
    Direct3D& d3d_; 
    std::string rootDirectory_; 

    std::map<std::string, Texture> textures_; 
}; 

당신이 볼 수 있듯이, 나는 각 자산에 대한 명시 적 인스턴스화 일반적인 Load<T>() 방법 (단지 순간에 텍스처) 각 자산 유형에 대한지도가 유형을 저장하고 싶습니다. Load<Texture>()은 디스크에서 이미지 파일을 읽습니다 (아직 맵에없는 경우). Texture을 새로 작성하여 맵에 삽입하고 리턴합니다.

Texture 클래스는 기본적으로 생성하고 RAII 관용구를 따라 원시 IDirect3DTexture9을 캡슐화 (소멸자가 texture_Release()를 호출) : 내 코드를 테스트 할 때

class Texture 
{ 
public: 
    Texture(); 
    Texture(Direct3D& d3d, unsigned int width, unsigned int height, 
    const std::vector<unsigned char>& stream); 

    ~Texture(); 

    IDirect3DTexture9* GetD3DTexture() const { return texture_; } 

private: 
    IDirect3DTexture9* texture_; 
}; 

, 나는, 각 텍스처 두 번 출시 된 것을 깨달았 Texture 개체의 (얕은) 복사본이 어느 시점에서 생성 되었기 때문에 소멸자가 이들 각각에 대해 호출 되었기 때문에.

더 나아가 Load<T>()은지도의 요소에 대한 참조를 반환하지만 발신자는 직접 사본을 만들어 동일한 문제를 유발할 수 있습니다. 내 생각 : 맵에 새 요소를 삽입 할 std::pair를 생성 할 때 복사가 어쨌든 필요하기 때문에 Texture 개인의 복사 생성자를 만들기

  • 이 해결책입니다.

  • 딥 복사본을 만드는 사용자 지정 복사본 생성자를 정의하는 것은 실제로는 한 번만 있어야하는 기본 Direct3D 텍스처 복사본을 만들어야하므로 전혀 적합하지 않은 것처럼 보입니다.

여기 내 옵션에는 어떤 것이 있습니까? 이것은 스마트 포인터를 사용하여 해결할 수 있습니까? 어떤 유형이지도에 저장되어야하며 어떤 유형이 Load<T>()으로 반환되어야합니까?

+0

직무를 찾은 다음 과제를 옮깁니다. –

답변

4

C++ 11에서는 언어가 으로 정확하게 이동하는 기능을 추가했습니다. 사용자가 마주 치게 된 이유가 바로입니다.그리고 다행히, 이동 기계를 사용하도록 코드를 수정하는 것은 놀라 울 정도로 간단하다

class Texture 
{ 
public: 
    Texture() noexcept; 
    Texture(Direct3D& d3d, unsigned int width, unsigned int height, 
    const std::vector<unsigned char>& stream); 

    Texture(Texture&& rhs) noexcept 
    : texture_(rhs.texture_) //take the content from rhs 
    {rhs.texture_ = nullptr;} //rhs no longer owns the content 

    Texture& operator=(Texture&& rhs) noexcept 
    { 
    Clear(); //erase previous content if any 
    texture_ = rhs.texture_; //take the content from rhs 
    rhs.texture_ = nullptr; //rhs no longer owns the content 
    return *this; 
    } 

    ~Texture() noexcept {Clear();} 

    IDirect3DTexture9* GetD3DTexture() const noexcept { return texture_; } 

private: 
    void Clear() noexcept; //does nothing if texture_ is nullptr 
    IDirect3DTexture9* texture_; 
}; 

이 추가하는 "생성자를 이동"과 "이동 할당"하는 것입니다 이동 있도록 다른 하나 Texture에서 콘텐츠, 한 번에 한 번만 주어진 IDirect3DTexture9을 가리 킵니다. 컴파일러는이 두 가지를 감지하고 암시 적 복사본 생성자 및 복사 할당 생성을 중지해야하므로 Texture은 더 이상 복사 할 수 없습니다. IDirect3DTexture9을 복사하는 것이 어렵고 실제로 이해가되지 않기 때문에 원하는 작업을 더 이상 복사 할 수 없습니다. . 이제 Texture 클래스가 수정되었습니다.

이제 다른 수업은 어떻게됩니까? 변경 사항 없음. std::map<std::string, Texture>은 클래스에 noexcept 이동 연산자가 있음을 감지 할만큼 똑똑하기 때문에 사본 대신 자동으로 사용합니다. map도 이동 가능하지만 복사 할 수 없게 만듭니다. map은 이동 가능하지만 복사 할 수 없으므로 자동으로 ContentManager을 이동 가능하지만 복사 할 수 없게 만듭니다. 당신이 그것에 대해 생각할 때 어떤 의미가 있습니다, 주위에 콘텐츠를 옮기는 것은 괜찮습니다. 그러나 복사본을 원하지 않습니다.

Texture getter(); //returns a temporary Texture 
Texture a = getter(); //since the right hand side is a temporary, 
         //the compiler will try to move construct it, and only 
         //copy construct if it can't be moved. 
a = getter(); //since the right hand side is a temporary, 
         //the compiler will try to move assign it, and only 
         //copy assign if it can't be moved. 

void setter1(Texture&); //receives a reference to an outside texture object 
setter1(a); //works exactly as it always has 
setter1(getter()); //compiler error, reference to temporary 
setter1(std::move(a)); //compiler error, passing rreference && instead of lreference &. 

void setter2(Texture); //receives a local texture object 
setter2(a); //compiler error, no copy constructor 
setter1(getter()); //creates the Texture by moving the data from the temporary 
setter2(std::move(a)); //The compiler moves the content from a to the function; 
         //the function receives the content previously held by a, and 
         //a now has no content. Careful not to read from a after this. 

void setter3(Texture&&); //receives a reference to a temporary texture 
setter3(a); //compiler error, a is not a temporary 
setter3(getter()); //function receives reference to the temporary, all is good 
setter3(std::move(a)); //function thinks a is temporary, and will probably remove 
         //it's content. Careful not to read from a after this. 
+0

감사합니다. C++ 11은 실제로 저에게 새롭고 곧 새로운 기능에 대해 읽어야합니다. ;) 또 하나의 질문이 있습니다 :'Load ()'가'return textures_ [assetName];'과 같은 맵의 텍스처에 대한 참조를 리턴하도록하는 것이 여전히 맞습니까?이렇게하면 텍스처를 로컬 변수로 옮길 수 없으므로 이동은 원본 객체의 모든 내용을 지우므로 내용 관리자로부터 "훔칩니다"? – ph4nt0m

+1

@ ph4nt0m :'std :: move'로 명시 적으로 말하지 않는 한, 지역 변수와 참조 ('T &')는 내용을 옮기지 않습니다. (또는 지역 변수를 반환하면 암시 적으로 이동합니다. 이 함수는'const T &'를 돌려주기 때문에 아무도 해당 참조를 통해 내용을 제거 할 수 없으므로 맵은 안전합니다. C++ 11은 일부 사본을 기본적으로 이동으로 대체했지만 이전에 복사하지 않은 경우 복사 또는 이동하지 않으므로 이전 코드도 여전히 안전합니다. –

+0

나는이 모든 것을 완전히 이해했는지 모르겠다. 왜냐하면 그것은 나에게 새로운 것이기 때문이다. 하지만 텍스쳐 t = content.Load ("tex.png");'과 같은 것을 생각했습니다. 이것은 이동인가 복사인가? 나는 보통 복사 생성자를 사용한다고 생각한다. 그래서't'가 범위를 벗어나면 내부의 D3D 텍스처가 릴리즈된다. 왜냐하면'ContentManager'가 관리하는 리소스를 해제해야하기 때문에 내가 원하는 것이 아닌 것이다. – ph4nt0m

1

생성자를 비공개로 만듭니다.
오브젝트 자체 대신 오브젝트에 대한 포인터를 보유하는 맵을 작성하십시오.

+0

간접 참조의 또 다른 계층을 도입 할 필요가 없습니다. 언어는이 문제를 정확히 해결하기 위해 역학을 구축했습니다. –

0

각 개체에 대한 참조 카운터를 유지하는 것이 가장 좋습니다. 대신 대신 공유 포인터를 사용합니다 (std :: map < std :: string, std :: tr1 :: shared_ptr < ... >>).

콘텐츠 관리자를 사용하여 개체를 릴리스 할 수도 있습니다 (예 : Release (T & 개체)).지도 자체에 참조 카운터를 유지합니다. 하지만 가장 좋은 것은 공유 포인터입니다.

+0

필요 없음 간접 참조의 또 다른 계층을 소개합니다.이 언어는이 문제를 정확히 해결하기 위해 역학을 구축했습니다. –

0

예, 스마트 포인터를 사용하는 것이 좋습니다. 특히 shared_ptr을 사용할 수 있습니다. 사용 가능한 컴파일러 버전이나 프레임 워크에 따라 C++11 variety 또는 예를 들어 다음과 같이 사용할 수 있습니다. Boost's implementation.

+0

간접 참조의 또 다른 계층을 도입 할 필요가 없으며, 언어는 _yactly_이 문제를 해결할 수있는 역학을 구축했습니다. –

0

대답은 당신의 손가락 끝에서 권리 : 우변은 분명히 당신에게 새로운 개념이기 때문에 그래서 사람들은,


이제 변경이 필요하지 않습니다, 여기에 충돌 과정의 사용하지 않는 :)

  1. insert 당신은 큰 세 가지의 규칙을 잊었 : 당신이 복사 생성자 중 하나를 쓰기 할당 연산자 또는 소멸자를 복사 할 경우, 당신은 아마 다른 두를 작성해야합니다. 이 경우, 그들을 비공개
  2. map의 요소를 삽입하는 방법에는 여러 가지가있다는 insert

순수 C++ 03 솔루션 단지 그들 중 하나입니다 : 이제

class Texture 
{ 
public: 
    Texture(): texture_(0) {} 

    Load(Direct3D& d3d, unsigned int width, unsigned int height, 
     const std::vector<unsigned char>& stream); 

    ~Texture(); 

    IDirect3DTexture9* GetD3DTexture() const { return texture_; } 

private: 
    IDirect3DTexture9* texture_; 
}; 


template<typename T> 
T const& ContentManager::Load(const std::string& assetName) { 
    Texture& t = textures_[assetName]; 

    t.Load(....); 

    return ...; 
} 

, 당신을 먼저 기본 생성자를 사용하여 객체를지도에 배치하고 다음에이 실제로 채워집니다.

  • 객체
  • 사용 emplace을 재배치 할 수 있도록 이동 생성자를 정의하고 textures_.emplace(std::piecewise_construct, std::make_tuple(assetName), std::make_tuple(...));
  • 같이 쌍 중 piecewise_construct 생성자를 대상으로 : C++ 11에서

    , 두 개 대안이

관련 문제