Presentation is loading. Please wait.

Presentation is loading. Please wait.

Chap. 4 Utility Classes.

Similar presentations


Presentation on theme: "Chap. 4 Utility Classes."— Presentation transcript:

1 Chap. 4 Utility Classes

2 Contents Simple value types MFC collection classes CFile class family
일반적 목적 : CString, CTime, CTimeSpan 윈도우 구조체 : CPoint, CSize, Crect MFC collection classes array, linked list, map CFile class family CException class

3 Introduction 우리가 만드는 많은 class들 Utility class의 범주에 든다. 이 단원의 목적
MFC 개발자 들의 technique 앞으로의 project에 도움

4 Simple Value Types Simple value types
C++ type(char*, …), Windows type(RECT, …) 들을 encapsulating 한 클래스들 C++ interface 제공 더 안전하고 더 쉽게 이용할 수 있는 operator 제공 memory management internationalization

5 Class CString Character string을 encapsulate함 다음과 같은 특징이 있음
Internationalization Memory allocation/deallocation Operators(+, =, ==, …) String searching String manipulation(reverse, concat) Formatting(printf-like)

6 Using a CString 생성 // create a CString from a char *
CString myString1(“This is a string”); // create a CString from a CString CString myString2(myString1); // create a CString with 80 z characters CString myString3(‘z’, 80); // create a CString as empty CString CString myString4;

7 Using a CString (cont’d)
CString myString1(“string 1”); CString myString2(“string 2”); CString myString3(“string 3”); // addition (concatenation) & assignment myString1 += myStrig2; myString3 = “This is “ + myString2; myString2 += ‘Z’; // comparison : == , < , != , … if ( myString1 == myString2 ) // like strcmp // operator[] : 0-based if ( myString1[7] == ‘l’ ) // find : Find(), FindOneOf(), ReverseFind()

8 CString Internals CString declaration(AFX.H) class CString { public:
// Constructors CString(); CString(const CString& stringSrc); CString(TCHAR ch, int nRepeat = 1); CString(LPCSTR lpsz); CString(LPCWSTR lpsz); CString(LPCSTR lpch, int nLength); CString(LPCWSTR lpch, int nLength); CString(const unsigned char* psz);

9 CString Internals (cont’d)
// Attributes & Operations int GetLength() const; BOOL IsEmpty() const; void Empty(); TCHAR GetAt(int nIndex) const; TCHAR operator[](int nIndex) const; void SetAt(int nIndex, TCHAR ch); operator LPCTSTR() const; const CString& operator=(const CString& stringSrc); const CString& operator=(TCHAR ch); const CString& operator=(LPCSTR lpsz); const CString& operator=(LPCWSTR lpsz); const CString& operator=(const unsigned char* psz);

10 CString Internals (cont’d)
// concatenate const CString& operator+=(const CString& string); const CString& operator+=(TCHAR ch); const CString& operator+=(LPCTSTR lpsz); friend CString AFXAPI operator+(const CString& string1, const CString& string2); friend CString AFXAPI operator+(const CString& string, TCHAR ch); friend CString AFXAPI operator+(TCHAR ch, const CString& string); friend CString AFXAPI operator+(const CString& string, LPCTSTR lpsz); friend CString AFXAPI operator+(LPCTSTR lpsz, const CString& string); // string comparison int Compare(LPCTSTR lpsz) const; int CompareNoCase(LPCTSTR lpsz) const; int Collate(LPCTSTR lpsz) const; int CollateNoCase(LPCTSTR lpsz) const;

11 CString Internals (cont’d)
// simple sub-string extraction CString Mid(int nFirst, int nCount) const; CString Mid(int nFirst) const; CString Left(int nCount) const; CString Right(int nCount) const; CString SpanIncluding(LPCTSTR lpszCharSet) const; CString SpanExcluding(LPCTSTR lpszCharSet) const; // upper/lower/reverse conversion void MakeUpper(); void MakeLower(); void MakeReverse(); // advanced manipulation int Replace(TCHAR chOld, TCHAR chNew); int Replace(LPCTSTR lpszOld, LPCTSTR lpszNew); int Remove(TCHAR chRemove); int Insert(int nIndex, TCHAR ch); int Insert(int nIndex, LPCTSTR pstr); int Delete(int nIndex, int nCount = 1);

12 CString Internals (cont’d)
// searching int Find(TCHAR ch) const; int ReverseFind(TCHAR ch) const; int Find(TCHAR ch, int nStart) const; int FindOneOf(LPCTSTR lpszCharSet) const; int Find(LPCTSTR lpszSub) const; int Find(LPCTSTR lpszSub, int nStart) const; // simple formatting void AFX_CDECL Format(LPCTSTR lpszFormat, ...); void AFX_CDECL Format(UINT nFormatID, ...); void FormatV(LPCTSTR lpszFormat, va_list argList); // input and output friend CArchive& AFXAPI operator<<(CArchive& ar, const CString& string); friend CArchive& AFXAPI operator>>(CArchive& ar, CString& string); // load from string resource BOOL LoadString(UINT nID);

13 CString Internals (cont’d)
// get pointer to modifiable buffer at least as long as nMinBufLength LPTSTR GetBuffer(int nMinBufLength); // release buffer, setting length to nNewLength (or to first nul if -1) void ReleaseBuffer(int nNewLength = -1); // get pointer to modifiable buffer exactly as long as nNewLength LPTSTR GetBufferSetLength(int nNewLength); // release memory allocated to but unused by string void FreeExtra(); // turn refcounting back on LPTSTR LockBuffer(); // turn refcounting off void UnlockBuffer(); // Implementation public: ~CString(); int GetAllocLength() const;

14 CString Internals (cont’d)
protected: LPTSTR m_pchData; // pointer to ref counted string data // implementation helpers CStringData* GetData() const; void Init(); void AllocCopy(CString& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const; void AllocBuffer(int nLen); void AssignCopy(int nSrcLen, LPCTSTR lpszSrcData); void ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data); void ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData); void CopyBeforeWrite(); void AllocBeforeWrite(int nLen); static void PASCAL Release(CStringData* pData); static int PASCAL SafeStrlen(LPCTSTR lpsz); static void FASTCALL FreeData(CStringData* pData); };

15 CString Internals (cont’d)
CString declaration(AFX.H) m_pchData 유일한 멤버 변수 String에 대한 pointer CStringData CString의 기능 구현을 도와주는 structure struct CStringData { long nRefs; // reference count int nDataLength; // length of data (including terminator) int nAllocLength; // length of allocation TCHAR* data() // TCHAR* to managed data { return (TCHAR*)(this+1); } };

16 CString Internals (cont’d)
공간 할당 함수 AllocBuffer()(STRCORE.CPP) void CString::AllocBuffer(int nLen) { if (nLen == 0) Init(); else CStringData* pData = (CStringData*)newBYTE[sizeof(CStringData) + (nLen+1)*sizeof(TCHAR)]; pData->nAllocLength = nLen; pData->nRefs = 1; pData->data()[nLen] = '\0'; pData->nDataLength = nLen; m_pchData = pData->data(); }

17 CString Internals (cont’d)
AllocBuffer()함수에 의한 memory 할당 nRefs nDataLength nAllocLength CString::m_pchData CStringData Data(nlength + 1)

18 CString Internals (cont’d)
의문점 CString class는 string 이외의 데이터를 숨김 왜, 그들을 멤버변수로 추가하지 않았는가 CString object와 TCHAR *은 똑같아야 한다. same bits 그 이외의 부가적인 정보는 CStringData 에서 관리 이전 버전과의 호환성

19 CString Internals (cont’d)
Reference counting CStringData::nRefs 실제 데이터의 변경이 가해지면 copy CString myString1(“This is a string”); CString myString2(myString1); CString myString3 = myString1; myString2 myString1 “This is a string” myString3

20 CString Internals (cont’d)
Reference counting 과정 nRefs 변수의 값이 -1 으로 초기화 그 string에 새 참조가 발생하면 1 증가 참조가 줄어들면 1 감소 다시 0이 되면 삭제 가능 참조되는 string에 쓰기가 발생하면 새로운 string을 생성하고 원래의 string의 nRefs값을 1 감소 Critical resource 일종의 OS에서 발생하는 resource 공유 문제 두 thread 에서 동시에 참조를 증가 혹은 감소시키는 경우 문제 해결 InterlockedIncrement(), InterlockedDecrement()

21 CString Internals (cont’d)
예제 Line 1 Line 2 Line 3 Line 4 Line 5 CString myString1(“This is my string1”); CString myString2(myString1); CString myString3; myString3 = myString2; // Write somthing myString3.MakeUpper();

22 CString Internals (cont’d)
“Strcore.cpp” CString::CString(LPCTSTR lpsz) { int nLen = SafeStrlen(lpsz); if (nLen != 0) AllocBuffer(nLen); memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR)); } myString1 “This is my string1” 1

23 CString Internals (cont’d)
CString::CString(const CString& stringSrc) { if (stringSrc.GetData()->nRefs >= 0) m_pchData = stringSrc.m_pchData; InterlockedIncrement(&GetData()->nRefs); } else { Init(); *this = stringSrc.m_pchData; } myString1 “This is my string1” 2 myString2

24 CString Internals (cont’d)
const CString& CString::operator=(const CString& stringSrc) { if (m_pchData != stringSrc.m_pchData) { if ((GetData()->nRefs < 0 && GetData() != _afxDataNil) || stringSrc.GetData()->nRefs < 0) { // actual copy necessary since one of the strings is locked AssignCopy(stringSrc.GetData()->nDataLength, stringSrc.m_pchData); } else { // can just copy references around Release(); m_pchData = stringSrc.m_pchData; InterlockedIncrement(&GetData()->nRefs); } return *this; myString2 myString1 “This is my string1” 3 myString3

25 CString Internals (cont’d)
void CString::MakeUpper() { CopyBeforeWrite(); _tcsupr(m_pchData); } void CString::CopyBeforeWrite() { if (GetData()->nRefs > 1) { CStringData* pData = GetData(); Release(); AllocBuffer(pData->nDataLength); memcpy(m_pchData,pData->data(), (pData->nDataLength+1)*sizeof (TCHAR)); } ASSERT(GetData()->nRefs <= 1);

26 CString Internals (cont’d)
void CString::Release() { if (GetData() != _afxDataNil) ASSERT(GetData()->nRefs != 0); if (InterlockedDecrement(&GetData()->nRefs) <= 0) FreeData(GetData()); Init(); } myString1 “This is my string1” 2 myString2 myString3 “THIS IS MY STRING1” 1

27 CString Internals (cont’d)
Internationalization _t internationalized string 함수를 이용하여 구현 _tcschr() => strchr() mapping int CString::Find(TCHAR ch, int nStart) const { int nLength = GetData()->nDataLength; if (nStart >= nLength) return -1; LPTSTR lpsz = _tcschr(m_pchData + nStart, (_TUCHAR)ch); return (lpsz == NULL) ? -1 : (int)(lpsz - m_pchData); }

28 Other Simple Value Types
Structure Source File CPoint POINT(struct tagPOINT) afxwin1.inl CRect RECT(struct tagRECT) afxwin1.inl, wingdix.cpp CSize SIZE(struct tagSIZE) CTime time_t operations afx.inl, timecore.cpp CTimeSpan time_t math

29 Class CRect Declaration (AFXWIN.H) class CRect : public tagRECT { …
typedef struct tagRECT LONG left; LONG top; LONG right; LONG bottom; } RECT;

30 Class CRect (cont’d) void CRect::DeflateRect(LPCRECT lpRect) {
left += lpRect->left; top += lpRect->top; right -= lpRect->right; bottom -= lpRect->bottom; }

31 MFC Collection Classes
Array 일반적인 C++에서의 array임 단 모든 메모리관련 기능 추가 List Doubly linked list Map 일종의 dictionary Key, value 쌍으로 데이터를 저장

32 Type of Collection Classes
Template-based Nontemplate-based Array : CObArray, CByteArray, … List : CObList, CPtrList, CStringList Map : CMapPtrToWord, … Collection contents Array List Map Collections of objects of any type CArray CList Cmap Collections of pointers to objects of any type CTypedPtrArray CTypedPtrList CTypedPtrMap

33 Shape Features Shape Ordered? Indexed? Insert Search Duplicate List
Yes No Fast Slow Array By int Map By key No(key) Yes(value)

34 Type-safe Collection Template-based classes 변수 선언 멤버 함수 호출
필요할 경우, helper function 구현 (CArray, CList, CMap 에서) CompareElements CopyElements Dumpelements HashKey SerializeElements

35 Type-safe Collection (cont’d)
// 변수 선언 CList<int, int> m_intList; // 함수 호출 m_intList.AddTail( 100 ); m_intList.RemoveAll( ); // helper function 구현 class CPerson : public CObject { }; CArray< CPerson, CPerson& > personArray; template <> void AFXAPI SerializeElements <CPerson> ( CArchive& ar, CPerson* pNewPersons, int nCount ) { for ( int i = 0; i < nCount; i++, pNewPersons++ ) // Serialize each CPerson object pNewPersons->Serialize( ar ); }

36 Type-safe Collection (cont’d)
Nontemplate-based classes 방법 1 : Type-casting 활용 방법 2 : 파생클래스 생성 class CPerson : public CObject {...}; CPerson* p1 = new CPerson(...); CObList myList; myList.AddHead( p1 ); // No cast needed CPerson* p2 = ( CPerson* )myList.GetHead(); class CPersonList : public CObList { public: void AddHeadPerson( CPerson* person ) {AddHead( person );} const CPerson* GetHeadPerson() {return (CPerson*)GetHead();} };

37 Iteration Array List CTypedPtrArray<CObArray, CPerson*> myArray;
for( int i = 0; i < myArray.GetSize();i++ ){ CPerson* thePerson = myArray.GetAt( i ); // CPerson* thePerson = myArray[ i ]; } CTypedPtrList<CObList, CPerson*> myList; POSITION pos = myList.GetHeadPosition(); while( pos != NULL ) { CPerson* thePerson = myList.GetNext( pos ); ... }

38 Iteration (cont’d) Map
CMap<CString, LPCTSTR, CPerson*, CPerson*> myMap; POSITION pos = myMap.GetStartPosition(); while( pos != NULL ) { CPerson* pPerson; CString string; myMap.GetNextAssoc( pos, string, pPerson ); } CMapStringToOb myMap; // A nontemplate collection class POSITION pos = myMap.GetStartPosition( ); while( pos != NULL ) { CPerson* pPerson; CString string; myMap.GetNextAssoc( pos, string, (CObject*&)pPerson ); ASSERT( pPerson->IsKindOf( RUNTIME_CLASS( CPerson ) ) ); }

39 Additional Structures
Stack class CTray : public CObject { ... }; class CStack : public CTypedPtrList< CObList, CTray* > { public: // Add element to top of stack void Push( CTray* newTray ) { AddHead( newTray ); } // Peek at top element of stack CTray* Peek() { return IsEmpty() ? NULL : GetHead(); } // Pop top element off stack CTray* Pop() { return RemoveHead(); } };

40 Additional Structures (cont’d)
Queue class CPerson : public CObject { ... }; class CQueue : public CTypedPtrList< CObList, CPerson* > { public: // Go to the end of the line void AddToEnd( CPerson* newPerson ) { AddTail( newPerson ); } // End of the queue // Get first element in line CPerson* GetFromFront() { return IsEmpty() ? NULL : RemoveHead(); } };

41 Array Collections Array collections
CByteArray, CDWordArray, CUintArray CObArray, CStringArray, CWordArray AFXCOLL.H ARRAY_X.CPP ( X : type of array ) void ArrayExample() { CUintArray myIntArray; myIntArray.SetSize(100); for ( int I=0; I<100; I++ ) myIntArray.SetAt(I, 2*I); UINT bogus = myIntArray[50]; bogus = myIntArray.GetAt(50); myIntArray.DeleteAt(50); }

42 CWordArray Class class CWordArray : public CObject {
DECLARE_SERIAL(CWordArray) public: // Attributes int GetSize() const; int GetUpperBound() const; void SetSize(int nNewSize, int nGrowBy = -1); // Operations void FreeExtra(); void RemoveAll(); WORD GetAt(int nIndex) const; void SetAt(int nIndex, WORD newElement); WORD& ElementAt(int nIndex);

43 CWordArray Class (cont’d)
// Direct Access to the element data (may return NULL) const WORD* GetData() const; WORD* GetData(); // Potentially growing the array void SetAtGrow(int nIndex, WORD newElement); int Add(WORD newElement); int Append(const CWordArray& src); void Copy(const CWordArray& src); // overloaded operator helpers WORD operator[](int nIndex) const; WORD& operator[](int nIndex); // Operations that move elements around void InsertAt(int nIndex, WORD newElement, int nCount = 1); void RemoveAt(int nIndex, int nCount = 1); void InsertAt(int nStartIndex, CWordArray* pNewArray);

44 CWordArray Class (cont’d)
// Implementation protected: WORD* m_pData; // the actual array of data int m_nSize; // # of elements (upperBound - 1) int m_nMaxSize; // max allocated int m_nGrowBy; // grow amount public: ~CWordArray(); void Serialize(CArchive&); #ifdef _DEBUG void Dump(CDumpContext&) const; void AssertValid() const; #endif // local typedefs for class templates typedef WORD BASE_TYPE; typedef WORD BASE_ARG_TYPE; };

45 CWordArray Class (cont’d)
CWordArray(ARRAY_W.CPP) 멤버 변수 m_pData 저장된 데이터의 포인터 m_nSize Current size of array(“logical” size) m_nMaxSize “Physical” size M_nGrowBy 추가 메모리 할당시 추가 할당 크기 SetSize()를 통한 이해

46 CWordArray Class (cont’d)
void CWordArray::SetSize(int nNewSize, int nGrowBy) { if (nGrowBy != -1) m_nGrowBy = nGrowBy; // set new size if (nNewSize == 0) // shrink to nothing delete[] (BYTE*)m_pData; m_pData = NULL; m_nSize = m_nMaxSize = 0; } else if (m_pData == NULL) // create one with exact size m_pData = (WORD*) new BYTE[nNewSize * sizeof(WORD)]; memset(m_pData, 0, nNewSize * sizeof(WORD)); // zero fill m_nSize = m_nMaxSize = nNewSize;

47 CWordArray Class (cont’d)
else if (nNewSize <= m_nMaxSize) { // it fits if (nNewSize > m_nSize) { // initialize the new elements memset(&m_pData[m_nSize], 0, (nNewSize-m_nSize) * sizeof(WORD)); } m_nSize = nNewSize; } else { // otherwise, grow array int nGrowBy = m_nGrowBy; if (nGrowBy == 0) nGrowBy = min(1024, max(4, m_nSize / 8)); int nNewMax; if (nNewSize < m_nMaxSize + nGrowBy) nNewMax = m_nMaxSize + nGrowBy; // granularity else nNewMax = nNewSize; // no slush WORD* pNewData = (WORD*) new BYTE[nNewMax * sizeof(WORD)];

48 CWordArray Class (cont’d)
// copy new data from old memcpy(pNewData, m_pData, m_nSize * sizeof(WORD)); memset(&pNewData[m_nSize], 0, (nNewSize-m_nSize)*sizeof(WORD)); // get rid of old stuff (note: no destructors called) delete[] (BYTE*)m_pData; m_pData = pNewData; m_nSize = nNewSize; m_nMaxSize = nNewMax; }

49 CWordArray Class (cont’d)
SetSize()(ARRAY_W.CPP) 5개의 section으로 구성 Initialization section Shrink-to-nothing section First allocation section Allocate-from-extra-space section Grow-the-array section

50 CWordArray Class (cont’d)
SetAt()(AFXCOLL.INL) m_pData[nIndex] = newElement; InsertAt()(ARRAY_W.CPP) Array마지막에 insert하는 경우 메모리 할당 후 insert Array 중간에 insert하는 경우 메모리 할당 후 데이터 이동작업 Expensive  무분별한 사용을 삼가

51 CWordArray Class (cont’d)
void CWordArray::InsertAt(int nIndex, WORD newElement, int nCount) { if (nIndex >= m_nSize) // adding after the end of the array SetSize(nIndex + nCount); // grow so nIndex is valid else { // inserting in the middle of the array int nOldSize = m_nSize; SetSize(m_nSize + nCount); // grow it to new size // shift old data up to fill gap memmove(&m_pData[nIndex+nCount], &m_pData[nIndex], (nOldSize-nIndex) * sizeof(WORD)); // re-init slots we copied from memset(&m_pData[nIndex], 0, nCount * sizeof(WORD)); } // insert new value in the gap while (nCount--) m_pData[nIndex++] = newElement;

52 CWordArray Class (cont’d)
GetAt() and operator[] (AFXCOLL.INL) 많은 데이터 access함수가 GetAt()을 내부적으로 호출 내부 구현 변경시 GetAt()만 수정하면 됨 Data abstraction 기술을 배울만함 _AFXCOLL_INLINE WORD CWordArray::GetAt(int nIndex) const { ASSERT(nIndex >= 0 && nIndex < m_nSize); return m_pData[nIndex]; } _AFXCOLL_INLINE WORD CWordArray::operator[](int nIndex) const { return GetAt(nIndex); }

53 List Collections List collections CObList, CPtrList, CStringList
Doubly linked list Supporting operator as follows AddHead(), Find(), GetCount() GetHEad(), GetNext(), GetTail(), IsEmpty() RemoveAll(), RemoveHead(), RemoveTail() InsertAfter(), InsertBefore() POSITION pos = m_strList.GetHeadPositon(); while ( pos != NULL ) CString tmpString = m_strList.GetNext(pos);

54 CStringList Class class CStringList : public CObject {
DECLARE_SERIAL(CStringList) protected: struct CNode CNode* pNext; CNode* pPrev; CString data; }; public: // Construction CStringList(int nBlockSize = 10); // Attributes (head and tail) int GetCount() const; BOOL IsEmpty() const; // peek at head or tail CString& GetHead();

55 CStringList Class (cont’d)
// Operations CString RemoveHead(); CString RemoveTail(); POSITION AddHead(LPCTSTR newElement); POSITION AddTail(LPCTSTR newElement); POSITION AddHead(const CString& newElement); POSITION AddTail(const CString& newElement); void AddHead(CStringList* pNewList); void AddTail(CStringList* pNewList); void RemoveAll(); POSITION GetHeadPosition() const; POSITION GetTailPosition() const; CString& GetNext(POSITION& rPosition); // return *Position++ CString GetNext(POSITION& rPosition) const; // return *Position++ CString& GetPrev(POSITION& rPosition); // return *Position-- CString GetPrev(POSITION& rPosition) const; // return *Position-- CString& GetAt(POSITION position); CString GetAt(POSITION position) const; void SetAt(POSITION pos, LPCTSTR newElement); void SetAt(POSITION pos, const CString& newElement); void RemoveAt(POSITION position);

56 CStringList Class (cont’d)
POSITION InsertBefore(POSITION position, LPCTSTR newElement); POSITION InsertAfter(POSITION position, LPCTSTR newElement); POSITION InsertBefore(POSITION position, const CString& newElement); POSITION InsertAfter(POSITION position, const CString& newElement); POSITION Find(LPCTSTR searchValue, POSITION startAfter = NULL) const; POSITION FindIndex(int nIndex) const; // Implementation protected: CNode* m_pNodeHead; CNode* m_pNodeTail; int m_nCount; CNode* m_pNodeFree; struct CPlex* m_pBlocks; int m_nBlockSize; CNode* NewNode(CNode*, CNode*); void FreeNode(CNode*); typedef CString BASE_TYPE; typedef LPCTSTR BASE_ARG_TYPE; };

57 CStringList Class (cont’d)
CStringList(AFXCOLL.H) 멤버 CNode structure 실제 list에서의 한 node를 위한 structure m_pNodeHead List에서의 처음 element에 대한 node pointer m_pNodeTail List에서의 마지막 element에 대한 node pointer m_nCount List에서의 element의 수

58 CStringList Class (cont’d)
CPlex structure(AFXPLEX_.H) struct CPlex { CPlex* pNext; void* data() { return this+1; } static CPlex* PASCAL Create(CPlex*& head, UINT nMax, UINT cbElement); void FreeDataChain(); // free this one and links };

59 CStringList Class (cont’d)
NewNode()의 이해 CStringList::CNode* CStringList::NewNode(CStringList::CNode* pPrev, CStringList::CNode* pNext) { if (m_pNodeFree == NULL) { // add another block CPlex* pNewBlock = CPlex::Create(m_pBlocks, m_nBlockSize, sizeof(CNode)); // chain them into free list CNode* pNode = (CNode*) pNewBlock->data(); // free in reverse order to make it easier to debug pNode += m_nBlockSize - 1; for (int i = m_nBlockSize-1; i >= 0; i--, pNode--) pNode->pNext = m_pNodeFree; m_pNodeFree = pNode; }

60 CStringList Class (cont’d)
ASSERT(m_pNodeFree != NULL); // we must have something CStringList::CNode* pNode = m_pNodeFree; m_pNodeFree = m_pNodeFree->pNext; pNode->pPrev = pPrev; pNode->pNext = pNext; m_nCount++; ASSERT(m_nCount > 0); // make sure we don't overflow ConstructElement(&pNode->data); return pNode; }

61 CStringList Class (cont’d)
CStringList::NewNode()(LIST_S.CPP) CStringList의 memory 관리의 핵심 Add(), Insert() 등의 함수가 위 함수를 내부적으로 이용 MFC list는 free CNode element로 이루어진 singly linked list를 유지하고 있다.(m_pNodeFree) Free node가 없으면 CPlex::Create()함수를 이용하여 메모리 할당 할당된 메모리를 list형태로 변환 후 head를 m_pNodeFree가 가리키게 함 POSITION 내부적으로 CNode에 대한 pointer 역할

62 CStringList Class (cont’d)
CPlex* PASCAL CPlex::Create(CPlex*& pHead, UINT nMax, UINT cbElement) { CPlex* p = (CPlex*) new BYTE[sizeof(CPlex) + nMax * cbElement]; p->pNext = pHead; pHead = p; // change head (adds in reverse order for simplicity) return p; }

63 CStringList Class (cont’d)
Delete시의 memory관리 Element가 delete되면 memory는 그대로 두고 free node list에 추가 CStringList::RemoveAll()함수에서 실제 memory를 free함(Data list & free list를 free) void CStringList::RemoveAll() { ASSERT_VALID(this); // destroy elements CNode* pNode; for (pNode = m_pNodeHead; pNode != NULL; pNode = pNode->pNext) DestructElement(&pNode->data); m_nCount = 0; m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL; m_pBlocks->FreeDataChain(); m_pBlocks = NULL; }

64 CStringList Class (cont’d)
List memory 관리 요약 세개의 list를 관리한다. Actual in-use list Doubly linked list of CNodes m_pNodeHead, m_pNodeTail List of free nodes Singly linked list of CNodes(m_pNodeFree) All allocated memory blocks Singly linked list of CPlexes(m_pBlocks)

65 Map Collections Map collections 특징 CMapPtrToPtr, CMapStringToOb
CMapStringToPtr, CMapStringToString CMapWordToOb, CMapWordToPtr 특징 Dictionary라고 불리기도 함 Key, value 쌍을 하나의 단위(association)로 저장함 Hash table을 이용함

66 Map Collections (cont’d)
예제 CMapStringToString myStringMap; myStringMap.SetAt(“one”, “uno”); myStringMap.SetAt(“two”, “dos”); myStringMap.SetAt(“three”, “tres”); Cstring strAnswer; if ( myStringMap.LookUp(“one”, answer) ) // got it, should be uno else …. // Iterative POSITION pos = GetStartPosition(); while ( pos != NULL ) { CString key, value; GetNextAssoc(pos, key, value); }

67 Map Collections (cont’d)
class CMapWordToPtr : public CObject { DECLARE_DYNAMIC(CMapWordToPtr) protected: // Association struct CAssoc { CAssoc* pNext; WORD key; void* value; }; public: CMapWordToPtr(int nBlockSize = 10); int GetCount() const; BOOL IsEmpty() const; // Lookup BOOL Lookup(WORD key, void*& rValue) const; // Operations // Lookup and add if not there void*& operator[](WORD key);

68 Map Collections (cont’d)
void SetAt(WORD key, void* newValue); BOOL RemoveKey(WORD key); void RemoveAll(); POSITION GetStartPosition() const; void GetNextAssoc(POSITION& rNextPosition, WORD& rKey, void*& rValue) const; UINT GetHashTableSize() const; void InitHashTable(UINT hashSize, BOOL bAllocNow = TRUE); protected: CAssoc** m_pHashTable; UINT m_nHashTableSize; int m_nCount; CAssoc* m_pFreeList; struct CPlex* m_pBlocks; int m_nBlockSize; CAssoc* NewAssoc(); void FreeAssoc(CAssoc*); CAssoc* GetAssocAt(WORD, UINT&) const; };

69 Map Collections (cont’d)
CMapWordToPtr(AFXCOLL.H) 멤버 CAssoc structure Map의 element를 나타내는 structure Memory management List와 동일(CPlex 이용) Map Hashing 내부적으로 hash table을 생성(Array of CAssoc pointer) InitHashTable()(MAP_WP.CPP) m_pHashTable, m_nHashTableSize

70 Map Collections (cont’d)
Word와 string에 대한 각각의 hash function이 존재(HashKey()함수) Collision의 해결 Linked list of collided items CAssoc의 pNext멤버 변수 거의 모든 insert는 operator[]를 이용한다. operator[] Collision해결 코드가 들어가 있다. _AFXCOLL_INLINE void CMapWordToPtr::SetAt(WORD key, void* newValue) { (*this)[key] = newValue; }

71 CFile Class Family CFile class family CFile CStdioFile
Nonbuffered file access (Windows API) CStdioFile Buffered I/O로 CFile을 발전시킴 CMemFile, CSharedFile Shared memory를 CFile interface로 구현

72 CFile Class class CFile : public CObject { DECLARE_DYNAMIC(CFile)
enum OpenFlags { modeRead = x0000, modeWrite = x0001, modeReadWrite = 0x0002, shareCompat = x0000, shareExclusive = 0x0010, shareDenyWrite = 0x0020, shareDenyRead = 0x0030, shareDenyNone = 0x0040, modeNoInherit = 0x0080, modeCreate = x1000, modeNoTruncate = 0x2000, typeText = x4000, typeBinary = (int)0x8000 }; enum Attribute { normal = 0x00, readOnly = 0x01, hidden = 0x02, system = 0x04, volume = 0x08, directory = 0x10, archive = 0x20 }; enum SeekPosition { begin = 0x0, current = 0x1, end = 0x2 }; enum { hFileNull = -1 }; // Constructors CFile(); CFile(int hFile); CFile(LPCTSTR lpszFileName, UINT nOpenFlags);

73 CFile Class (cont’d) // Attributes UINT m_hFile;
operator HFILE() const; virtual DWORD GetPosition() const; BOOL GetStatus(CFileStatus& rStatus) const; virtual CString GetFileName() const; virtual CString GetFileTitle() const; virtual CString GetFilePath() const; virtual void SetFilePath(LPCTSTR lpszNewName); // Operations virtual BOOL Open(LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError = NULL); static void PASCAL Rename(LPCTSTR lpszOldName, LPCTSTR lpszNewName); static void PASCAL Remove(LPCTSTR lpszFileName); static BOOL PASCAL GetStatus(LPCTSTR lpszFileName, CFileStatus& rStatus); static void PASCAL SetStatus(LPCTSTR lpszFileName, const CFileStatus& status);

74 CFile Class (cont’d) DWORD SeekToEnd(); void SeekToBegin();
virtual LONG Seek(LONG lOff, UINT nFrom); virtual void SetLength(DWORD dwNewLen); virtual DWORD GetLength() const; virtual UINT Read(void* lpBuf, UINT nCount); virtual void Write(const void* lpBuf, UINT nCount); virtual void Abort(); virtual void Flush(); virtual void Close(); enum BufferCommand { bufferRead, bufferWrite, bufferCommit, bufferCheck }; virtual UINT GetBufferPtr(UINT nCommand, UINT nCount = 0, void** ppBufStart = NULL, void** ppBufMax = NULL); protected: BOOL m_bCloseOnDelete; CString m_strFileName; };

75 CFile Class (cont’d) Declaration of CFile(AFX.H) 멤버 m_hFile
Windows file handle BufferCommand, GetBufferPtr() CMemFile의 기능을 위한 멤버 m_bCloseOnDelete CFile object가 destroy될 때 file handle을 close할 지를 나타냄 m_strFileName File name

76 CFile Class (cont’d) Operations
거의 모든 operator들이 window API를 내부적으로 그대로 호출하는 수준 Open()  ::Createfile() Read()  ::ReadFile() Write()  ::WriteFile()

77 CFile Operation (cont’d)
Opening Reading & Writing char* pszFileName = "c:\\test\\myfile.dat"; CFile myFile; CFileException fileException; if(!myFile.Open(pszFileName, CFile::modeCreate|CFile::modeReadWrite, &fileException)){ TRACE( "Can't open file %s, error = %u\n", pszFileName, fileException.m_cause ); } char szBuffer[256]; UINT nActual = 0; CFile myFile; myFile.Write( szBuffer, sizeof( szBuffer ) ); myFile.Seek( 0, CFile::begin ); nActual = myFile.Read( szBuffer, sizeof( szBuffer ) );

78 CFile Operation (cont’d)
File status CFile theFile; char* szFileName = "c:\\test\\myfile.dat"; BOOL bOpenOK; CFileStatus status; if( CFile::GetStatus( szFileName, status ) ) { // Open the file without the Create flag bOpenOK = theFile.Open( szFileName, CFile::modeWrite ); } else // Open the file with the Create flag CFile::modeCreate | CFile::modeWrite );

79 CStdioFile CFile과의 차이점 Wrapping C run-time library(fopen,…)
Buffered I/O ReadString()과 WriteString()함수 지원 Text mode로 파일을 열면 string의 read/write를 지원 Buffering지원으로 인해 몇 개의 function은 쓸 수 없다.

80 CStdioFile(contd.) Declaration of CStdioFile(AFX.H) 멤버 m_pStream
FILE pointer ReadString(), WriteString()(FILETXT.CPP) LPTSTR CStdioFile::ReadString(LPTSTR lpsz, UINT nMax) { LPTSTR lpszResult = _fgetts(lpsz, nMax, m_pStream); if (lpszResult == NULL && !feof(m_pStream)) clearerr(m_pStream); AfxThrowFileException(CFileException::generic, _doserrno, m_strFileName); } return lpszResult;

81 CMemFile 존재 이유 Declaration of CMemFile(AFX.H)
MFC의 serialization기능을 memory에 이용하기 위해 Declaration of CMemFile(AFX.H) 멤버 m_nGrowBytes 내부 buffer의 증가 단위 m_nPosition Memory block에서의 file pointer

82 CMemFile(contd.) m_nBufferSize m_nFileSize m_nlpBuffer m_bAutoDelete
Logical memory file의 크기 m_nFileSize <= m_nBufferSize m_nlpBuffer Buffer에 대한 pointer m_bAutoDelete Destructor가 호출될 때 memory를 free할 건지를 나타냄

83 CMemFile(contd.) Memory handling CArray memory handling과 유사
GrowFile()(FILEMEM.CPP) Memory 할당의 단위는 m_nGrowBytes 크기 이미 할당되어 있으면 realloc Read()(FILEMEM.CPP) 지정한 크기 만큼 buffer에서 읽어서 넘겨줌 File에 대한 작업과 interface는 동일

84 CSharedFile 특징 CMemFile에서 상속을 받음 Windows global memory API를 사용
GlobalAlloc(), GlobalReAlloc(), … OLE 기능 구현에 이용됨 AFXPRIV.H

85 4. CException class CException 모든 exception처리 class의 부모 종류
CArchiveException – Serialization exception CDaoException – Data access object exception CDBException – Database exception CFileException – File exception CMemoryException – Memory exception CNotSupportedException – Something is not supported

86 CException Declaration of CException(AFX.H) 예:CFileException(AFX.H) 멤버
GetErrorMessage() Exception에 해당하는 message string을 가져옴 ReportError() Message 출력 예:CFileException(AFX.H) 가능한 exception을 enumeration type으로 가지고 있다.

87 CException(contd.) Exception발생시 AfxThrowFileException()함수를 호출
AfxThrowFileException()(FILEX.CPP) 적절한 처리후 throw 함 해당 catch 내부의 문이 수행됨 멤버 m_cause Enumerated Cfile exception m_strFileName Exception을 발생시킨 file name

88 CException(contd.) //… CMemFile myMemFile; try { myMemFile.Seek(2043);
} catch (CFileException, e) if (e->m_cause == CFileException::badSeek) e->Delete(); END_CATCH

89 Chap. 5 CObject Class

90 Contents Introduction CObject features RTCI Dynamic creation
Persistence Diagnostics

91 Introduction CObject Class 대부분의 MFC class들의 최상위 부모 class
singly rooted hierarchy 대부분의 MFC class들에게 필요한 기능 구현 문제점 가상 테이블의 거대화와 성능 저하 Poor object-oriented design

92 CObject Features Run-time class information (RTCI) Dynamic creation
Persistence (serialization) Diagnostic support

93 CObject Class AFX.H class CObject { public:
// Object model (types, destruction, allocation) virtual CRuntimeClass* GetRuntimeClass() const; virtual ~CObject(); // virtual destructors are necessary // Diagnostic allocations void* PASCAL operator new(size_t nSize); void* PASCAL operator new(size_t, void* p); void PASCAL operator delete(void* p); protected: CObject();

94 CObject Class (cont’d)
private: CObject(const CObject& objectSrc); void operator=(const CObject& objectSrc); // Attributes public: BOOL IsSerializable() const; BOOL IsKindOf(const CRuntimeClass* pClass) const; virtual void Serialize(CArchive& ar); // Diagnostic Support virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; static const AFX_DATA CRuntimeClass classCObject; static CRuntimeClass* PASCAL _GetBaseClass(); };

95 Macro DECLARE macro IMPLEMENT macro 종류 Class에 멤버 변수와 함수를 선언
Header(Interface) file에 존재 IMPLEMENT macro Class의 멤버 변수와 함수를 구현 C++ file에 존재 종류 DECLARE_DYNAMIC / IMPLEMENT_DYNAMIC DECLARE_DYNCREATE / IMPLEMENT_DYNCREATE DECLARE_SERIAL / IMPLEMENT_SERIAL

96 Macro (cont’d) 세 Macro의 포함 관계 RTCI Dynamic Creation Serialization
X “DYNCREATE” “SERIAL”

97 Run-Time Class Information (RTCI)
정의 개발자는 어떤 object의 class name, parent 같은 정보를 run time에 알 수 있다. Run-Time Type Information (RTTI) C++ language의 특성 RTCI와 동일한 기능 향후 통합 가능성

98 RTCI 사용 예 // Header file Class CMyClass : public CObject {
DECLARE_DYNAMIC(CMyClass) // Macro 1 Public: CMyClass(); // … } // Implementation file IMPLEMENT_DYNAMIC(CMyClass, CObject) // Macro 2 DemoRTCI() CObject* pObject = new CMyClass; // Macro 3 if (pObject->IsKindOf(RUNTIME_CLASS(CMyClass))) { CMyClass* pMyObject = (CMyClass*)pObject;

99 RTCI 과정 AFX.H #define DECLARE_DYNAMIC(class_name) \ public: \
static const AFX_DATA CRuntimeClass class##class_name; \ virtual CRuntimeClass* GetRuntimeClass() const; \ Preprocessor Class_name : CMyClass public: static const AFX_DATA CRuntimeClass classCMyClass; virtual CRuntimeClass* GetRuntimeClass() const;

100 RTCI 과정 (cont’d) #define IMPLEMENT_DYNAMIC(class_name, base_class_name) \ IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL) #define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, \ wSchema, pfnNew) \ AFX_COMDAT const AFX_DATADEF CRuntimeClass \ class_name::class##class_name = { \ #class_name, sizeof(class class_name), wSchema, pfnNew, \ RUNTIME_CLASS(base_class_name), NULL }; \ CRuntimeClass* class_name::GetRuntimeClass() const \ { return RUNTIME_CLASS(class_name); } \ #define RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))

101 RTCI 과정 (cont’d) m_lpszClassName = “CMyClass”
AFX_COMDAT const AFX_DATADEF CRuntimeClass CMyClass::classCMyClass = { “CMyClass”, sizeof(CMyClass), 0xFFFF, NULL, &CObject::classCObject, NULL }; CRunTimeClass* CMyClass::GetRuntimeClass() const return &CMyClass::classCMyClass; } m_lpszClassName = “CMyClass” m_nObjectSize = sizeof(CMyClass) m_wSchema = 0xFFFF m_pfnCreateObject = NULL m_pBaseClass = RUNTIME_CLASS(CObject)

102 CRuntimeClass AFX.H struct CRuntimeClass { // Attributes
LPCSTR m_lpszClassName; int m_nObjectSize; UINT m_wSchema; // schema number of the loaded class CObject* (PASCAL* m_pfnCreateObject)(); // NULL=>abstract class CRuntimeClass* m_pBaseClass; // Operations CObject* CreateObject(); BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const; // Implementation void Store(CArchive& ar) const; CRuntimeClass* m_pNextClass; // linked list of registered classes };

103 CObject::IsKindOf() OBJCORE.CPP VC 4.0까지는 다른 내용이었음
BOOL CObject::IsKindOf(const CRuntimeClass* pClass) const { ASSERT(this != NULL); // it better be in valid memory, at least for CObject size ASSERT(AfxIsValidAddress(this, sizeof(CObject))); // simple SI case CRuntimeClass* pClassThis = GetRuntimeClass(); return pClassThis->IsDerivedFrom(pClass); }

104 CRuntimeClass::IsDerivedFrom()
OBJCORE.CPP BOOL CRuntimeClass::IsDerivedFrom(const CRuntimeClass* pBaseClass) const { // simple SI case const CRuntimeClass* pClassThis = this; while (pClassThis != NULL) if (pClassThis == pBaseClass) return TRUE; pClassThis = pClassThis->m_pBaseClass; } return FALSE; // walked to the top, no match

105 Dynamic Creation Object의 동적 생성 RTCI macro
CRuntimeClass의 정보를 가지고 object를 동적으로 생성할 수 있다.(CreateObject()) RTCI macro DECLARE_DYNCREATE IMPLEMENT_DYNCREATE DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC보다 상위에 있는 macro 따라서 위 macro는 자동 포함됨

106 Dynamic Creation 사용 예 CMyClass가 DYNCREATE macro를 사용
CRuntimeClass* pRuntimeClass = RUNTIME_CLASS(CMyClass); Cobject* pObject = pRuntimeClass->CreateObject(); ASSERT(pObject->IsKindOf(RUNTIME_CLASS(CMyClass))); pDocTemplate = new CMultiDocTemplate(IDR_SCRIBTYPE, RUNTIME_CLASS(CScribDoc), RUNTIME_CLASS(CMDIChildWnd), RUNTIME_CLASS(CScribView));

107 Dynamic Creation 과정 AFX.H #define DECLARE_DYNCREATE(class_name) \
DECLARE_DYNAMIC(class_name) \ static CObject* PASCAL CreateObject(); Preprocessor Class_name : CMyClass public: static const AFX_DATA CRuntimeClass classCMyClass; virtual CRuntimeClass* GetRuntimeClass() const; static CObject* PASCAL CreateObject();

108 Dynamic Creation 과정 (cont’d)
#define IMPLEMENT_DYNCREATE(class_name, base_class_name) \ CObject* PASCAL class_name::CreateObject() \ { return new class_name; } \ IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, \ class_name::CreateObject) m_lpszClassName = “CMyClass” m_nObjectSize = sizeof(CMyClass) m_wSchema = 0xFFFF m_pfnCreateObject = CMyClass::CreateObject m_pBaseClass = RUNTIME_CLASS(CObject)

109 CRuntimeClass::CreateObject()
OBJCORE.CPP CObject* CRuntimeClass::CreateObject() { if (m_pfnCreateObject == NULL) return NULL; CObject* pObject = NULL; TRY pObject = (*m_pfnCreateObject)(); } END_TRY return pObject;

110 Persistence Persistence란?
Object의 state정보를 저장(store)했다가 나중에 복원(recover)할 수 있는 능력 MFC에서 말하는 serialization Format에 신경쓰지 않고 object자체를 파일에 읽고 쓸 수 있게 한다. 모든 일은 내부적으로 처리됨 관련 멤버함수 IsSerializable() Serialize()

111 Serialization의 지원 다음의 두 가지 작업
DECLARE_SERIAL / IMPLEMENT_SERIAL macro를 이용한다. 이 때, schema(버전 정보)를 반드시 넣는다. CObject::Serialize()함수를 override한다. 자신의 class data member에 맞게

112 Serialization 예 // Header file class CMyClass : public CObject {
DECLARE_SERIAL(CMyClass) public: CMyClass(); WORD m_wType; DWORD m_dwData; CPoint m_ptMiddle; void Serialize(CArchive &); }

113 Serialization 예(cont’d)
// Implementation file IMPLEMENT_SERIAL(CMyClass, CObject, 0xabcd) void CMyClass::Serialize(CArchive& ar) { if (ar.IsStoring()) { ar << m_wType; ar << m_dwData; ar << m_ptMiddle; } else { ar >> m_wType; ar >> m_dwData; ar >> m_ptMiddle;

114 CArchive Class AFX.H 목적 Binary stream ( not ASCII stream )
Tied to CFile pointer Implementing buffering of data

115 CArchive Class (cont’d)
class CArchive { public: // Flag values enum Mode{store = 0, load = 1, bNoFlushOnDelete = 2, bNoByteSwap = 4}; CArchive(CFile* pFile, UINT nMode, int nBufSize = 4096, void* lpBuf=NULL); ~CArchive(); // Attributes BOOL IsLoading() const; BOOL IsStoring() const; BOOL IsByteSwapping() const; BOOL IsBufferEmpty() const; CFile* GetFile() const; UINT GetObjectSchema(); // only valid when reading a CObject* void SetObjectSchema(UINT nSchema);

116 CArchive Class (cont’d)
CDocument* m_pDocument; UINT Read(void* lpBuf, UINT nMax); void Write(const void* lpBuf, UINT nMax); void Flush(); void Close(); void WriteString(LPCTSTR lpsz); LPTSTR ReadString(LPTSTR lpsz, UINT nMax); BOOL ReadString(CString& rString); public: friend CArchive& AFXAPI operator<<(CArchive& ar, const CObject* pOb); friend CArchive& AFXAPI operator>>(CArchive& ar, CObject*& pOb); friend CArchive& AFXAPI operator>>(CArchive& ar, const CObject*& pOb); // insertion operations CArchive& operator<<(BYTE by); CArchive& operator<<(WORD w); CArchive& operator<<(LONG l); CArchive& operator<<(DWORD dw); CArchive& operator<<(float f); CArchive& operator<<(double d);

117 CArchive Class (cont’d)
CArchive& operator<<(int i); CArchive& operator<<(short w); CArchive& operator<<(char ch); CArchive& operator<<(unsigned u); // extraction operations CArchive& operator>>(BYTE& by); CArchive& operator>>(WORD& w); CArchive& operator>>(DWORD& dw); CArchive& operator>>(LONG& l); CArchive& operator>>(float& f); CArchive& operator>>(double& d); CArchive& operator>>(int& i); CArchive& operator>>(short& w); CArchive& operator>>(char& ch); CArchive& operator>>(unsigned& u); // object read/write CObject* ReadObject(const CRuntimeClass* pClass); void WriteObject(const CObject* pOb); // advanced object mapping (used for forced references) void MapObject(const CObject* pOb);

118 CArchive Class (cont’d)
void WriteClass(const CRuntimeClass* pClassRef); CRuntimeClass* ReadClass(const RuntimeClass*pClassRefRequested=NULL, UINT* pSchema = NULL, DWORD* pObTag = NULL); void SerializeClass(const CRuntimeClass* pClassRef); void SetStoreParams(UINT nHashSize = 2053, UINT nBlockSize = 128); void SetLoadParams(UINT nGrowBy = 1024); // Implementation public: BOOL m_bForceFlat; // for COleClientItem implementation (default TRUE) BOOL m_bDirectBuffer; // TRUE if m_pFile supports direct buffering void FillBuffer(UINT nBytesNeeded); void CheckCount(); // throw exception if m_nMapCount is too large DWORD ReadCount(); void WriteCount(DWORD dwCount); UINT m_nObjectSchema; CString m_strFileName; protected: CArchive(const CArchive& arSrc); void operator=(const CArchive& arSrc); BOOL m_nMode; BOOL m_bUserBuf;

119 CArchive Class (cont’d)
int m_nBufSize; CFile* m_pFile; BYTE* m_lpBufCur; BYTE* m_lpBufMax; BYTE* m_lpBufStart; // array/map for CObject* and CRuntimeClass* load/store UINT m_nMapCount; union { CPtrArray* m_pLoadArray; CMapPtrToPtr* m_pStoreMap; }; // map to keep track of mismatched schemas CMapPtrToPtr* m_pSchemaMap; // advanced parameters (controls performance with large archives) UINT m_nGrowSize; UINT m_nHashSize;

120 CArchive Class (cont’d)
Members m_nMode 읽기인지 쓰기인지 결정 IsLoading(), IsStoring(), IsByteSwapping(), IsBufferEmpty() CArchive object의 state access함수

121 CArchive Class (cont’d)
Operators CObject, Windows data type, C++ data type에 대한 insertion, extraction operator Class member functions WriteClass(), ReadClass(), SerializeClass() CRuntimeClass structure의 정보를 읽고 쓰기 위한 함수 Map members 이미 저장되었거나 읽혀진 object의 정보를 빠르게 얻기 위한 mapping정보 관리

122 CArchive Class (cont’d)
WORD type에 대한 함수 예 (INCLUDE\AFX.INL) _AFX_INLINE CArchive& CArchive::operator<<(WORD w) { if (m_lpBufCur + sizeof(WORD) > m_lpBufMax) Flush(); *(UNALIGNED WORD*)m_lpBufCur = w; m_lpBufCur += sizeof(WORD); return *this; } _AFX_INLINE CArchive& CArchive::operator>>(WORD& w) FillBuffer(sizeof(WORD) - (UINT)(m_lpBufMax - m_lpBufCur)); w = *(UNALIGNED WORD*)m_lpBufCur;

123 Serialization 과정 Serialization macro(AFX.H) public:
#define DECLARE_SERIAL(class_name) \ _DECLARE_DYNCREATE(class_name) \ AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb); Preprocessor Class_name : CMyClass public: static const AFX_DATA CRuntimeClass classCMyClass; virtual CRuntimeClass* GetRuntimeClass() const; static CObject* PASCAL CreateObject(); AFX_API friend Carchive& AFXAPI operator>>(Carchive& ar, CMyClass* &pOb);

124 Serialization 과정(cont’d)
#define IMPLEMENT_SERIAL(class_name, base_class_name, wSchema) \ CObject* PASCAL class_name::CreateObject() \ { return new class_name; } \ _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, \ class_name::CreateObject) \ AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name)); \ CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb) \ { pOb = (class_name*)ar.ReadObject(RUNTIME_CLASS(class_name)); \ return ar; } \ Preprocessor Class_name : CMyClass CArchive& AFXAPI operator>>(CArchive& ar, CMyClass* &pOb) { pOb = (CMyClass*)ar.ReadObject(RUNTIME_CLASS(CMyClass)); return ar; }

125 Serialization 과정(cont’d)
CArchive::ReadObject()(ARCOBJ.CPP) CObject* CArchive::ReadObject(const CRuntimeClass* pClassRefRequested) { UINT nSchema; DWORD obTag; CRuntimeClass* pClassRef = ReadClass(pClassRefRequested, &nSchema, &obTag); pOb = pClassRef->CreateObject(); UINT nSchemaSave = m_nObjectSchema; m_nObjectSchema = nSchema; pOb->Serialize(*this); m_nObjectSchema = nSchemaSave; return pOb; }

126 Serialization 과정(cont’d)
Macro에 insertion operator가 없는 이유 Global insertion operator가 존재함(AFX.INL) 특별히 CRuntimeClass정보를 필요로 하지 않기 때문 CObject에 대한 insertion operator로 충분함 _AFX_INLINE CArchive& AFXAPI operator<<(CArchive& ar, const CObject* pOb) { ar.WriteObject(pOb); return ar; }

127 Serialization 과정(cont’d)
void CArchive::WriteObject(const CObject* pOb) { if (pOb == NULL) *this << wNullTag; else if ((nObIndex = (DWORD)(*m_pStoreMap)[(void*)pOb]) != 0) { // save out index of already stored object if (nObIndex < wBigObjectTag) *this << (WORD)nObIndex; else { *this << wBigObjectTag; *this << nObIndex; } } else { // write class of object first CRuntimeClass* pClassRef = pOb->GetRuntimeClass(); WriteClass(pClassRef); // enter in stored object table, checking for overflow CheckCount(); (*m_pStoreMap)[(void*)pOb] = (void*)m_nMapCount++; // cause the object to serialize itself ((CObject*)pOb)->Serialize(*this);

128 CObject and Serialization
CObject::IsSerializable() OBJCORE.CPP CObject::Serialize() AFX.INL BOOL CObject::IsSerializable() const { return (GetRuntimeClass()->m_wSchema != 0xffff); } _AFX_INLINE void CObject::Serialize(CArchive&) /* CObject does not serialize anything by default */

129 Read & Write 추적 목적 및 가정 Serialization의 진행 과정 이해
Document/View architecture 가정 우리의 document class가 다음의 멤버 변수를 가짐 CMyClass* m_pMyClass; class CMyClass : public CObject { DECLARE_SERIAL(CMyClass) public: CMyClass(); WORD m_wType; DWORD m_dwData; CPoint m_ptMiddle; void Serialize(CArchive &); }

130 Read & Write 추적 (cont’d)
void CMyDocument::Serialize(CArchive& ar) { if (ar.IsStoring()) ar << m_pMyClass; } else ar >> m_pMyClass;

131 Read 과정 “DOCCORE.cpp” BOOL CDocument::OnOpenDocument(LPCTSTR lpszPathName) { CFile* pFile=GetFile(…); DeleteContents(); SetModifiedFlag(); // dirty during de-serialize CArchive loadArchive(pFile, CArchive::load | CArchive::bNoFlushOnDelete); loadArchive.m_pDocument = this; loadArchive.m_bForceFlat = FALSE; CWaitCursor wait; if (pFile->GetLength() != 0) Serialize(loadArchive); // load me loadArchive.Close(); ReleaseFile(pFile, FALSE); SetModifiedFlag(FALSE); // start off with unmodified return TRUE;

132 Read 과정 (cont’d) void CMyDocument::Serialize(CArchive& ar) {
if (ar.IsStoring()) ar << m_pMyClass; } else ar >> m_pMyClass; CArchive& AFXAPI operator>>(CArchive& ar, CMyClass* &pOb) { pOb = (CMyClass*)ar.ReadObject(RUNTIME_CLASS(CMyClass)); return ar; }

133 Read 과정 (cont’d) CObject* CArchive::ReadObject(const CRuntimeClass* pClassRefRequested) { UINT nSchema; DWORD obTag; CRuntimeClass* pClassRef = ReadClass(pClassRefRequested, &nSchema, &obTag); pOb = pClassRef->CreateObject(); UINT nSchemaSave = m_nObjectSchema; m_nObjectSchema = nSchema; pOb->Serialize(*this); m_nObjectSchema = nSchemaSave; return pOb; }

134 Read 과정 (cont’d) void CMyClass::Serialize(CArchive& ar) {
if (ar.IsStoring()) { ar << m_wType; ar << m_dwData; ar << m_ptMiddle; } else { ar >> m_wType; ar >> m_dwData; ar >> m_ptMiddle;

135 Read 과정 (cont’d) “AFXWIN1.INL”
_AFXWIN_INLINE CArchive& AFXAPI operator>>(CArchive& ar, POINT& point) { ar.Read(&point, sizeof(POINT)); return ar; }

136 Store Operation Write case CDocument::OnSaveDocument()
CArchive::CArchive() CMyClass::Serialize() CMyDocument::Serialize() CArchive::IsStoring() CArchive::IsStoring() CArchive::operator<<(WORD) operator<<(CArchive&, CObject*) CArchive::operator<<(DWORD) CArchive::WriteObject() operator<<(CArchive&, point) CArchive::WriteClass() CArchive::Write() CArchive::Close()

137 Read Operation Read case CDocument::OnOpenDocument()
CArchive::CArchive() CMyClass::Serialize() CMyDocument::Serialize() CArchive::IsStoring() CArchive::IsStoring() CArchive::operator>>(WORD) operator>>(CArchive&, CMyClass*) CArchive::operator>>(DWORD) CArchive::ReadObject() Operator>>(CArchive&, point) CArchive::WriteClass() CArchive::Read() CArchive::Close()

138 Serialization Performance
Data structure Maps and arrays 어떤 type의 데이터를 여러 번 읽고 쓰는 경우 그 때마다 class 정보를 읽고 쓰는 것은 비효율적 Write Map : reference no. + class name MFC는 reference no.만 write Read Array : CArchive는 array를 유지하며 reference no. 를 decoding하여 해당 class 정보를 획득 Serializing large data SetLoadParam(), SetStoreParam() Storing map의 hash table size나 reading array의 grow-by size 조절

139 CRuntimeClass member 저장
State serialization LPCSTR m_lpszClassName Class의 이름 UINT m_wSchema Class version 정보 Store() CRuntimeClass 의 state를 실제 저장하는 함수 Load() CRuntimeClass의 state를 실제 읽어 들이는 함수 m_nObjectSize, m_pNextClass debugging을 위해 필요 AfxAssertValidObject(), AfxDoForAllClasses()

140 CObject Diagnostic Support
크게 2가지로 분류됨 Basic diagnostics Diagnostic output TRACE macro Dump() Run-time checking ASSERT macro AssertValid() ASSERT_VALID macro Advanced memory diagnostics Memory leak detection Memory statistics

141 Enabling Diagnostics AfxEnableMemoryTracking
Debug 모드에서는 diagnostic 이 적용된다. 그러나, 임시로 조절 가능 BOOL CWinMyApp::InitInstance() { #ifdef _DEBUG // Disable tracking of memory for the scope of the InitInstance() AfxEnableMemoryTracking(FALSE); #endif // _DEBUG ... // Re-enable tracking of memory AfxEnableMemoryTracking(TRUE); }

142 Diagnostic Output - TRACE
TRACE macro Printf-like output MFC tracer utility를 이용 VC++ debugger window에 결과물 예) TRACE(“At this point, my Cpoint is : %d %d\n”, mypoint.x, mypoint.y);

143 #define TRACE ::AfxTrace
Inside TRACE TRACE(AFX.H) 단순히 AfxTrace()라는 전역함수를 부름 #define TRACE ::AfxTrace AfxTrace()(DUMPOUT.CPP) afxDump 를 이용

144 Inside TRACE (cont’d) void AFX_CDECL AfxTrace(LPCTSTR lpszFormat, ...)
{ #ifdef _DEBUG // all AfxTrace output is controlled by afxTraceEnabled if (!afxTraceEnabled) return; #endif va_list args; va_start(args, lpszFormat); int nBuf; TCHAR szBuffer[512]; nBuf = _vsntprintf(szBuffer, _countof(szBuffer), lpszFormat, args); ASSERT(nBuf >= 0); if ((afxTraceFlags & traceMultiApp) && (AfxGetApp() != NULL)) afxDump << AfxGetApp()->m_pszExeName << ": "; afxDump << szBuffer; va_end(args); }

145 Inside TRACE (cont’d) afxDump(AFX.H) CDumpContext의 global instance
최종적으로 CDumpContext의 OutputString() (DUMPCONT.CPP) 가 출력 담당 특정 파일에 출력도 가능 #ifdef _DEBUG extern AFX_DATA CDumpContex afxDump; #endif

146 Inside TRACE (cont’d) CDumpContext& CDumpContext::operator<<(WORD w) { TCHAR szBuffer[32]; wsprintf(szBuffer, _T("%u"), (UINT) w); OutputString(szBuffer); return *this; } void CDumpContext::OutputString(LPCTSTR lpsz) { if (!afxTraceEnabled) return; if (m_pFile == NULL) { AfxOutputDebugString(lpsz); return; } m_pFile->Write(lpsz, lstrlen(lpsz)*sizeof(TCHAR));

147 Diagnostic Output – Dump()
Object dump CObject의 하위 클래스들이 가진 멤버함수 클래스의 state를 print하기 위해 호출 Debug build 에서만 유효 예)

148 Diagnostic Output – Dump() (cont’d)
#ifdef _DEBUG void CMyClass::Dump(CDumpContext& dc) { CObject::Dump(dc); dc << “Type is: “ << m_wType << “\n” << “Data is: “ << m_dwData << “\n” << “Point is: “ << m_ptMiddle.x << “ “ << m_ptMiddle.y << “\n”; } #endif // end _DEBUG CMyClass* pMyClass = new CMyClass; // set some fields of the CPerson object... //... // now dump the contents #ifdef _DEBUG pMyClass->Dump( afxDump ); #endif

149 Inside Dump() Object dumping(OBJCORE.CPP) 단지 class이름과 주소만 출력
#ifdef _DEBUG void CObject::Dump(CDumpContext& dc) const { dc << "a " << GetRuntimeClass()->m_lpszClassName << " at " << (void*)this << "\n"; } #endif //_DEBUG 단지 class이름과 주소만 출력

150 Run-Time Checking - ASSERT
Assertions ASSERT macro Run-time시의 어떤 상태를 체크하고 실패하면 warning message box가 나타난다. MFC도 5000개 이상의 ASSERT문을 포함(VC++ 4) 사용 권유 예) window handle이 유효한지 미리 체크 // example for ASSERT CAge* pcage = new CAge( 21 ); // CAge is derived from CObject. ASSERT( pcage!= NULL ) ASSERT( pcage->IsKindOf( RUNTIME_CLASS( CAge ) ) ) // Terminates program only if pcage is NOT a CAge*.

151 Inside ASSERT #define ASSERT(f) \ do \ { \
if (!(f) && AfxAssertFailedLine(THIS_FILE, __LINE__)) \ AfxDebugBreak(); \ } while (0) \

152 Run-Time Checking – AssertValid()
Object-validity checking Run-time시에 object의 멤버들의 유효성 체크 #ifdef _DEBUG void CMyClass::AssertValid() { // Call inherited first CObject::AssertValid(); ASSERT(m_wType > 0xff00); ASSERT(m_dwData != 0); ASSERT(m_ptMiddle.x != 0 && m_ptMiddle.y != 0); } #endif // End _DEBUG

153 Inside AssertValid() void CObject::AssertValid() const {
ASSERT(this != NULL); }

154 Run-Time Checking – ASSERT_VALID
ASSERT_VALID macro CObject의 하위 클래스에 대해 호출 가능 AssertValid()에 대한 호출 + 부가적인 체크 예를 들어 document class가 CMyClass 멤버변수를 가지면 다음과 같이 체크할 수 있다. CMyDocument::AssertValid() { ASSERT_VALID(m_myClass); }

155 Inside ASSERT_VALID #define ASSERT_VALID(pOb)
(::AfxAssertValidObject(pOb, THIS_FILE, __LINE__)) void AFXAPI AfxAssertValidObject(const CObject* pOb, LPCSTR lpszFileName, int nLine) { if (pOb == NULL) { if (AfxAssertFailedLine(lpszFileName, nLine)) AfxDebugBreak(); return; // quick escape } if (!AfxIsValidAddress(pOb, sizeof(CObject))) // … if (!AfxIsValidAddress(*(void**)pOb, sizeof(void*), FALSE)) // … if (!AfxIsValidAddress(pOb, pOb->GetRuntimeClass()->m_nObjectSize, FALSE)) // … pOb->AssertValid();

156 Detect a Memory Leak Memory leak Heap에 할당된 영역을 다시 deallocate 하지 않을 때
할당된 메모리 영역을 이용하려 할 때 발견하기 무척 어려움 #ifdef _DEBUG CMemoryState startMemState, stopMemState, diffMemState; startMemState.Checkpoint(); #endif _DEBUG Cstring abc = “Free me!”; // … stopMemState.Checkpoint(); if ( diffMemState.Difference(startMemState, stopMemState)) { TRACE(“Memory leak!”); }

157 Specific Memory Diagnostic
afxMemDF memory diagnostic 의 기능을 선택적으로 하기 위한 global variable value allocMemDF : Turn on memory allocator (default) delayFreeMemDF : 프로그램이 종료할 때까지 메모리를 free하지 않음. 이를 통해 프로그램이 최대로 할당 받을 수 있는 메모리 체크 가능. checkAlwaysMemDF : 메모리가 allocate되거나 free될 때마다 메모리 상태 체크 (AfxCheckMemory 호출) AfxMemDF = allocMemDF | delayFreeMemDF | checkAlwaysMemDF;

158 Review Memory Statics CMemoryState class DumpStatistics()
delay 된 메모리의 양 heap에 남아 있는 양 program에 필요한 메모리 양 등등… DumpAllObjectsSince() 지금까지 할당된 모든 object들의 기록 표시 가장 최근의 CMemoryState::Checkpoint() 부터 기록

159 예 // Frame variable : do your memory allocations and deallocations
CString s = "This is a frame variable"; // Heap variable : the next object is a heap object CPerson* p = new CPerson( "Smith", "Alan", " " ); if( diffMemState.Difference( oldMemState, newMemState ) ) { TRACE( "Memory leaked!\n" ); diffMemState.DumpAllObjectsSince(); } Dumping objects -> ////// Cperson::Dump() {5} strcore.cpp(80) : non-object block at $00A7521A, 9 bytes long {4} strcore.cpp(80) : non-object block at $00A751F8, 5 bytes long {3} strcore.cpp(80) : non-object block at $00A751D6, 6 bytes long {2} a CPerson at $51A4 Last Name: Smith First Name: Alan Phone #: {1} strcore.cpp(80) : non-object block at $00A7516E, 25 bytes long

160 Inside Memory Diagnostics
CMemoryState class AFX.H 모든 memory allocation에 대한 개수와 크기 정보를 가지고 있다. new operator 를 override함으로 인해서 가능 Debugging상태에서는 new 를 DEBUG_NEW로 대체함 #ifdef _DEBUG #define new DEBUG_NEW #endif //_DEBUG

161 Inside Memory Diagnostics (cont’d)
AFX.H DEBUG_NEW declaration 파일 이름과 라인을 저장함 Global new operator CObject::operator new 또 다른 debug new operator 객체인지 체크 내부적으로 호출하는 AfxAllocMemoryDebug()함수의 argument차이 #define DEBUG_NEW new(THIS_FILE, __LINE__) void* operator new(size_t, nSize, PCSTR lpszFileName, int nLine)

162 Advanced memory…(contd.)
AfxAllocMemoryDebug() (AFXMEM.CPP) Malloc(sizeof(CBlockHeader) + nSize + nNoMansLandSize); CBlockHeader부분에 debugging시 필요한 모든 정보를 저장함 No mans land부분에 overwrite 체크를 위한 정보를 저장함 할당하는 모든 메모리 block들을 리스트로 연결 CBlockHeader Gap 데이터를 위한 부분 No Mans Land


Download ppt "Chap. 4 Utility Classes."

Similar presentations


Ads by Google