13장직렬화 김성영교수 금오공과대학교 컴퓨터공학부
도큐먼트 / 뷰구조 (1) 도큐먼트와뷰 디스크에저장된파일데이터를읽는경우 도큐먼트객체 뷰객체 파일 사용자 읽기 화면표시 2
도큐먼트 / 뷰구조 (2) 도큐먼트와뷰 사용자가데이터를입력하는경우 도큐먼트객체 뷰객체 파일 사용자 저장 입력 3
도큐먼트 / 뷰구조 (3) 도큐먼트와뷰 입력된데이터를디스크파일에저장하는경우 도큐먼트객체 뷰객체 파일 사용자 저장 4
도큐먼트 / 뷰구조 (4) 도큐먼트와뷰클래스의역할 클래스 도큐먼트 뷰 역할데이터를저장하거나읽어들인다. 데이터의변경사항이생기면뷰의화면을갱신한다. 데이터를화면에표시한다. 사용자와의상호작용을담당한다. 5
도큐먼트 / 뷰구조 (5) 도큐먼트 / 뷰구조의장점 서로다른기능을도큐먼트와뷰클래스로분리해서구현하므로개념적으로이해하기쉬움 도큐먼트하나에뷰가여러개존재하는모델을구현하기쉬움 예 ) 비주얼 C++ 편집창 MFC 에서도큐먼트 / 뷰구조를위해제공하는부가서비스이용가능 예 ) 직렬화 6
도큐먼트 / 뷰구조 (6) SDI 와 MDI 다룰수있는문서의개수에따라구분 7
도큐먼트 / 뷰구조 (7) 도큐먼트템플릿 도큐먼트, 프레임윈도우, 뷰클래스정보를유지 필요에따라해당객체를동적으로생성 MFC 클래스계층도 SDI MDI 8
도큐먼트객체에서의파일처리 어느메시지핸들러에서파일을열고닫을것인가! 실습 13.1 도큐먼트클래스는기본적으로다음두함수재정의 BOOL OnNewDocument(); void Serialize(CArchive& ar); // 초기화 // 파일입출력 9
BOOL CMyDoc::OnNewDocument( ) if (!CDocument::OnNewDocument()) return FALSE; AfxMessageBox( "OnNewDocument" ); return TRUE; void CMyDoc::Serialize( CArchive& ar ) if (ar.isstoring()) AfxMessageBox( "Serialize: Storeing" ); else AfxMessageBox( "Serialize: Loading" ); 10
BOOL CMyDoc::OnOpenDocument( LPCTSTR lpszpathname ) if (!CDocument::OnOpenDocument(lpszPathName)) return FALSE; AfxMessageBox("OnOpemDocument"); return TRUE; BOOL CMyDoc::OnSaveDocument( LPCTSTR lpszpathname ) AfxMessageBox("OnSaveDocument"); return CDocument::OnSaveDocument(lpszPathName); void CMyDoc::OnCloseDocument() AfxMessageBox("OnCloseDocument"); CDocument::OnCloseDocument(); void CMyDoc::DeleteContents() AfxMessageBox("DeleteContents"); CDocument::DeleteContents(); 11
프로그램실행전메모장에서 Hello 라고입력하고 C:/DATA.TXT 로저장 프로그램실행시프레임윈도우가나오기전에 DeleteContents 와 OnNewDocument 메시지박스출력 ( 파일메뉴에서새파일을클릭해도마찬가지 ) 12
열기를클릭하면파일대화상자가나온다 DATA.TXT 파일을선택하면 DeleteContents, Serialize:Loading, OnOpenDocument 메시지출력 13
저장을클릭하면 OnSaveDocument, Serialize:Storing 메시지박스차례대로출력 다른이름으로저장을클릭하면 OnSaveDocument, Serialize:Storing 메시지박스차례대로출력 프레임윈도우를닫으면 OnCloseDocument 메시지박스출력 14
파일입출력개요 (1) 파일입출력방법 일반파일입출력 CFile ( 파생 ) 클래스 바이너리모드로파일입출력을지원하는 MFC 클래스 Read(), Write() 등의함수이용 직렬화 CArchive 클래스 << 또는 >> 연산자이용 15
파일입출력개요 (2) 데이터읽기 - 일반파일입출력 CFile file; CFileException e; if(!file.open("mytest.dat", CFile::modeRead, &e) ) e.reporterror(); return; double a, b; file.read( &a, sizeof(a) ); file.read( &b, sizeof(b) ); TRACE( "a = %f, b = %f\n", a, b ); 16
파일입출력개요 (3) 데이터쓰기 - 일반파일입출력 CFile file; CFileException e; if(!file.open("mytest.dat", CFile::modeReadWrite CFile::modeCreate, &e) ) e.reporterror(); return double a = 1.23; double b = 4.56; file.write( &a, sizeof(a) ); file.write( &b, sizeof(b) ); 17
파일입출력개요 (4) 데이터읽기 - 직렬화 CFile file; CFileException e; if(!file.open( "mytest.dat", CFile::modeRead, &e) ) e.reporterror(); return; double a, b; CArchive ar( &file, CArchive::load ); ar >> a >> b; TRACE( "a = %f, b = %f\n", a, b ); 18
파일입출력개요 (5) 데이터쓰기 - 직렬화 CFile file; CFileException e; if(!file.open( "mytest.dat", CFile::modeReadWrite CFile::modeCreate, &e) ) e.reporterror(); return; double a = 1.23; double b = 4.56; CArchive ar( &file, CArchive::store ); ar << a << b; 19
직렬화기초 (1) 직렬화원리 CArchive ar(...); ar << a << b; CArchive ar(...); ar >> a >> b; a, b CArchive 객체 CFile 객체 디스크파일 20
직렬화기초 (2) CArchive 클래스생성자 CArchive::CArchive(CFile* pfile, UINT nmode, int nbufsize=4096, void* lpbuf=null); pfile CFile 객체 nmode CArchive::load 또는 CArchive::store nbufsize CArchive 클래스내부에서사용할버퍼크기 lpbuf 사용자정의버퍼의주소 21
직렬화기초 (3) 직렬화가능한데이터타입 구분 기본형 비기본형 데이터타입 BYTE, WORD, LONG, DWORD, float, double, int, short, char, wchar_t, unsigned RECT, POINT, SIZE, CRect, CPoint, CSize, CString, CTime, CTimeSpan, COleVariant, COleCurrency, COleDateTime, COleDataTimeSpan 22
CFile 클래스를이용한입출력 BOOL CMyDoc::OnOpenDocument(LPCTSTR lpszpathname) if (!CDocument::OnOpenDocument(lpszPathName)) return FALSE; MYDOC.CPP CFile f; f.open( lpszpathname, CFile::modeRead ); f.read( m_szmsg, 256 ); f.close( ); UpdateAllViews( NULL ); return TRUE; 23
BOOL CMyDoc::OnSaveDocument(LPCTSTR lpszpathname) char szmsg[] = "Hello, Part II"; CFile f; f.open( lpszpathname, CFile::modeWrite CFile::modeCreate ); f.write( szmsg, strlen(szmsg) ); f.close( ); return TRUE; 24
CArchive 클래스를이용한입출력 객체만처리가능 문자배열대신문자열클래스 CString 사용 실제로파일을처리하는주체는 CFile 객체 먼저 CFile 객체준비 BOOL CMyDoc::OnOpenDocument(LPCTSTR lpszpathname) if (!CDocument::OnOpenDocument(lpszPathName)) return FALSE; CString strmsg; CFile f; f.open( lpszpathname, CFile::modeRead ); CArchive ar( &f, CArchive::load ); ar >> strmsg; // 문자열객체로읽음 strcpy( m_szmsg, (LPCSTR)strMsg ); // 문자배열에복사 ar.close( ); f.close( ); MYDOC.CPP 25
UpdateAllViews(NULL); return TRUE; BOOL CMyDoc::OnSaveDocument(LPCTSTR lpszpathname) CString strmsg; strmsg = "Hello, Part III"; CFile f; f.open( lpszpathname, CFile::modeWrite CFile::modeCreate ); CArchive ar( &f, CArchive::store ); ar << strmsg; // 문자열객체로저장 ar.close(); f.close(); return TRUE; 프로그램실행결과 26
Serialize() 함수를이용한입출력 인자로넘어오는 CArchive 객체를이용해입출력작업 void CMyDoc::Serialize( CArchive& ar ) CString strmsg; if( ar.isstoring() ) strmsg = "Hello, Part IV"; ar << strmsg; else ar >> strmsg; strcpy( m_szmsg, (LPCSTR)strMsg ); UpdateAllViews( NULL ); MYDOC.CPP 27
프로그램실행결과 Serialize() 함수를사용하여처리될수있는데이터는 Serialize() 가가능한객체이어야함 28
OnopenDocument() 나 Serialize() 함수를사용하는경우드래그앤드롭 (Drag & Drop) 기능사용가능 CMyApp 클래스의 InitInstance() 함수에코드추가 BOOL CMyApp::InitInstance() m_pmainwnd->dragacceptfiles(true); return TRUE; MY.CPP 29
직렬화가능객체 실습 13.3 마우스클릭 원을그림 저장버튼 현재좌표를파일로저장 열기버튼 파일에저장해둔좌표를읽어와원을복원 마우스의좌표를직렬화가능한클래스로구현 직렬화가능한클래스는 CObject 클래스로부터상속받아기본적으로 Serialize() 함수를재정의하여구현 클래스정의와구현부분에직렬화처리를위한매크로추가 DECLARE_SERIAL(class_name ) // 헤더파일에추가 IMPLEMENT_SERIAL(class_name, base_class_name, wschema) // 구현파일에추가 직렬화를구현하려는클래스 기반클래스 버전정보 ( 보통 1) 30
마우수의좌표를저장할수있는 CMyPoint 클래스작성 Serialize() 함수에서는기반클래스의 Serialize() 함수를먼저호출 #ifndef MY_POINT #define MY_POINT MYPOINT.H class CMyPoint: public CObject public: CMyPoint(); ~CMyPoint(); long x; long y; void Serialize(CArchive& ar); DECLARE_SERIAL(CMyPoint) ; #endif 31
#include "stdafx.h" #include "mypoint.h" MYPOINT.CPP IMPLEMENT_SERIAL(CMyPoint, CObject, 1) CMyPoint::CMyPoint() x = 0; y = 0; CMyPoint::~CMyPoint() void CMyPoint::Serialize(CArchive& ar) CObject::Serialize(ar); // 기반클래스의 Serialize() 호출 if (ar.isstoring()) ar << x << y; else ar >> x >> y; 32
도큐먼트와뷰클래스에마우스이벤트와파일입출력처리추가 #include "mypoint.h" class CMyDoc : public CDocument public: CMyPoint m_mypt; // 직렬화가능객체 BOOL CMyDoc::OnNewDocument() if (!CDocument::OnNewDocument()) return FALSE; m_mypt.x = 0; m_mypt.y = 0; return TRUE; MYDOC.H MYDOC.CPP 33
void CMyDoc::Serialize(CArchive& ar) m_mypt.serialize(ar); //m_mypt 객체의파일입출력담당 if (ar.isstoring()) //CMyDoc 객체의자체데이터의저장처리 else //CMyDoc 객체의자체데이터읽기처리 UpdateAllViews(NULL); // 뷰갱신 void CMyView::OnDraw(CDC* pdc) CMyDoc* pdoc = GetDocument(); ASSERT_VALID(pDoc); CRect rt; rt.left = pdoc->m_mypt.x - 50; rt.top = pdoc->m_mypt.y - 50; rt.right = pdoc->m_mypt.x + 50; rt.bottom = pdoc->m_mypt.y + 50; pdc->ellipse(rt); MYVIEWCPP 34
void CMyView::OnLButtonDown(UINT nflags, CPoint point) CMyDoc* pdoc=getdocument(); pdoc->m_mypt.x = point.x; // 도큐먼트의멤버변수에마우스좌표기록 pdoc->m_mypt.y = point.y; pdoc->setmodifiedflag(); // 도큐먼트의내용이변경되었음을알림 Invalidate(); CView::OnLButtonDown(nFlags, point); SetModifiedFlag() 는도큐먼트내용변경을알림 도큐먼트내용이저장되지않고파괴되는경우대화상자출력 35
CMyDoc 클래스의 Serialize() 함수수정가능 void CMyDoc::Serialize(CArchive& ar) //m_mypt.serialize(ar); // 주석처리 if (ar.isstoring()) ar << &m_mypt; else CMyPoint* tmp; ar >> tmp; m_mypt.x = tmp->x; //tmp 가참조하고있는것복사 m_mypt.y = tmp->y; //tmp 가참조하고있는것복사 UpdateAllViews(NULL); MYDOC.CPP 36