2010-11-30 2 views
5

내가 사용할 수 있습니다 알고c를 저장하고 구조체의 포인터로드 게임 ++

MyGame game; // the game object 
// 

ofstream out("mygame.bin", ios::binary); 
out.write((char *)&game, sizeof(MyGame)); 

저장하고 게임을로드,하지만 내가 MyGame이 구조 내부 포인터가있는 경우에? 포인터는 단지 저장되지만 포인터는 가리키는 데이터가 아닌가?

: 어떻게 해결할 수 있습니까?

답변

5

에 직렬화에 대한 자세한 내용과이 마술 할 것으로 예상 할 수있다. 개체에 저장 /로드 메서드를 구현해야합니다. 예컨대 : 깊은 포인터에 대한 지원을 저장하고, 복원 및 공유 데이터에 대한 포인터의 적절한 직렬화에 내장 된

class Serializable 
{ 
    virtual void save(std::ofstream& _out) const = 0; 
    virtual void load(std::ifstream& _in) = 0; 
}; // eo class Serializable 


// some game object 
class MyObject : public Serializable 
{ 
    int myInt; 
    std::string myString; 

    virtual void save(std::ofstream& _out) const 
    { 
     _out << myInt << myString; 
    }; // eo save 

    virtual void load(std::ifstream& _in) 
    { 
     _in >> myInt >> myString; 
    }; // eo load 
}; // eo class SomeObject 

class MyGame : public Serializable 
{ 
    MyObject a; 
    MyObject b; 

    virtual void save(std::ofstream& _out) const 
    { 
     a.save(_out); 
     b.save(_out); 
    }; // eo save 

    virtual void load(std::ifstream& _in) 
    { 
     a.load(_in); 
     b.load(_in); 
    }; // eo load 
}; // eo class MyGame 
+0

이것은 실제로 바이너리가 아닙니다.;) – Nim

+0

또한 스트림은 필드를 구분하는 분리 기호 (일반적으로 공백)가 필요합니다.이 예에서는 스트림 출력 작업에 누락되어 있으므로 정수를 읽으려고하면 조작중인 스트림이 단순히 예외를 throw합니다 (예 : 생각 해봐, 내가 잘못 할 수 있기 때문에 시험하지 않았다.) – Nim

+0

이것은 잘라 내지 않을 것이다. 먼저 OP가 요구하는대로 포인터를 모두 직렬화하지 않기 때문에 여기에서 "속임수"를 사용합니다. 또한, 네트워크에서 데이터를 전송하는 것과 관련하여 htonl() 및 ntohl()을 사용하여 수행되는 작업과 같은 원시 연산자의 직렬화를 훨씬 강력하게 제어 할 수있는 경우에는 << and >> 연산자가 트릭을 수행한다고 가정합니다. . – Jon

1

당신은 스트림 출력 연산자 (<<를) 과부하 및 각 필드 (또는 그 반대)

편집 출력 스트림 수 : 여기에 완벽한 예입니다 ...

#include <iostream> 
#include <fstream> 
#include <map> 

using namespace std; 

template <typename T> 
void serialize(ostream& str, const T& field) 
{ 
    str.rdbuf()->sputn(reinterpret_cast<const char*>(&field), sizeof(T)); 
} 

template <typename T> 
void deserialize(istream& str, T& field) 
{ 
    str.rdbuf()->sgetn(reinterpret_cast<char*>(&field), sizeof(T)); 
} 

class MyGame 
{ 
public: 
MyGame() : a(), b() {} 
MyGame(int av, int bv) : a(av), b(bv) {} 

friend ostream& operator<<(ostream& str, MyGame const& game); 
friend istream& operator>>(istream& str, MyGame& game); 

    int getA() const { return a; } 
    int getB() const { return b; } 

private: 
int a; 
int b; 
}; 

ostream& operator<<(ostream& str, MyGame const& game) 
{ 
    serialize(str, game.a); 
    serialize(str, game.b); 
    return str; 
} 

istream& operator>>(istream& str, MyGame& game) 
{ 
    deserialize(str, game.a); 
    deserialize(str, game.b); 
    return str; 
} 

int main(void) 
{ 
    { 
    ofstream fout("test.bin", ios::binary); 
    MyGame game(10, 11); 
    fout << game; 
    } 

    { 
    ifstream fin("test.bin", ios::binary); 
    MyGame game; 
    fin >> game; 
    cout << "game.a: " << game.getA() << ", game.b: " << game.getB() << endl; 
    } 

    return 0; 
} 

당신은 이해해야합니다 그러나이 방법의 문제점은 결과 파일이 플랫폼에 따라 달라질 수 있습니다 (예 : 이식 불가능).

0

시도 game.serialize(out);. serialize 멤버 함수에서 포인터 멤버의 serialize를 호출합니다.

0

지속성이 필요한 유형별로 직렬화 기능을 만듭니다.
각 멤버에게 전화하십시오.

실제로 네트워크를 통해 직렬화하거나 디버그 목적으로 시각화하는 것과 비슷합니다.

boost.serialize는 당신을 도울 수 있습니다.

2

char * cast를 재정의하지 않았다고 가정하면이 옵션은 대부분 포인터가 아닌 데이터 만 저장합니다.

필요한 것은 객체의 직렬화입니다. 비트 스트림에서 객체의 상태를 마샬링하고이를 출력하는 메소드를 제공 할 수 있습니다. 또한 국가를 복원 할 방법이 필요합니다.

당신은 당신은 단지 스트림 포인터를 쓸 수 없습니다 wikipedia

+0

아래와 같이 직렬화 및 복원 방법을 << and >> 연산자로 재정의 할 수 있습니다! –

1

부스트하는 serialization library 있습니다.

상당히 광범위한 라이브러리이지만, 자신의 프로젝트에서이 코드를 사용하기 위해 많은 코드를 작성할 필요는 없습니다. 내 생각에 가장 간단한 직렬화 요구 사항에 대한 학습 노력의 가치가 있습니다.

0

포인터의 값을 그냥 덤프하는 "Naive"직렬화는 직렬화 해제 할 때 해당 포인터가 유효하지 않으므로 결코 작동하지 않습니다.

같이 갈 것 이런 종류의 문제에 대한 일반적인 접근 :

  1. 게임에 연재 할 수있는 각 개체가 나는 이러한 모든 객체가 궁극적에서 파생 된 것 같은데 (A serialize() 가상 함수를 구현 되세요 같은 기본 클래스).
  2. 기본 클래스에 get_serialized_id() 공용 기능을 구현 시켰습니다.이 함수는 정적으로 자동 증가 된 변수를 사용하여 각 객체 인스턴스에 대해 고유 한 ID를 생성하지만 처음 호출 될 때만 호출합니다 (후속 호출은 기존 값을 반환합니다). 이제

, 직렬화 :

  1. 시작을 std::map<int, YourBaseClass*>로. 키에 get_serialized_id()이 반환 한 값을 사용하여 game 개체를이지도에 추가합니다.
  2. 아직지도 화되지 않은 개체가지도에있는 경우 :
    • 첫 번째 개체를 가져옵니다.
    • get_serialized_id()을 일련 번호로 지정하십시오.
    • serialize()에 대한 구현을 호출하여 직렬화하십시오. 평소와 같이 기본 데이터를 유지해야합니다. 포인터를 통해 사용 가능한 데이터의 경우 각 객체에 대해 get_serialized_id()을 호출하고 반환 된 번호를 직렬화하면됩니다. 또한 해당 개체를지도에 추가하십시오.

이 각각의 "랜덤"ID와 함께 (「랜덤 "순서대로) 직렬화 객체의 무리가 발생합니다.

역 직렬화 할 때 :

  1. 시작을 std::map<int, YourBaseClass*>로. 저장된 파일의 첫 번째 항목을 읽습니다.
  2. 이 첫 번째 객체가 가리키는 각 객체에 대해 고유 한 ID를 알고 있습니다 (포인터 대신 직렬화 한 것입니다). 이 ID를 가진 항목을 저장된 파일에서 가져 와서 deserialize하십시오.
  3. 모든 항목을 저장된 파일에서 가져 와서 deserialize 할 때까지 반복적으로이 작업을 수행하십시오.
  4. 위의 3 단계에서 각 항목의 모든 종속성이 deserialize되므로 해당 항목을 개체로 인스턴스화하고지도에 추가합니다.
  5. 이렇게하면 항목의 ID가 지정된지도에서 포인터를 가져 와서이 항목에 종속 된 개체의 포인터 멤버를 설정할 수 있습니다.
  6. 재귀가 끝나면지도의 마지막 개체가 포인터를 모두 준비한 기본 "게임"개체가됩니다.
0

얕은 복사는 MyGame 클래스에 포인터가있는 경우 깊은 복사본이 반드시 필요합니다. MyGame 안에 함수 나 함수 세트를 구현하는 것이 좋습니다.이 함수는 자체 데이터를 파일에 저장하는 일을 처리하므로 호출하면됩니다.

0

빠른 답변과 좋은 답변을 주신 모든 분들께 감사 드리지만, 저에게 도움이되는 저의 친구는 우리가 다른 방식으로해야한다고 말했습니다.

개체의 기본 정보 만 저장하면 나머지는 함수에 다시 작성됩니다.

카드 게임이므로 카드 스택을 저장하려면 카드가 아닌 객체 ID 만 저장하고 파일에서 ID를 읽을 때 각 카드를 다시 초기화하면됩니다.