Figure 1 Const
CONST.CPP
////////////////////////////////////////////////////////////////
// Copyright 1996 Microsoft Systems Journal.
//
// This program illustrates how to cast away const-ness
// When you compile, you will get three errors (See below).
//
// Microsoft: cl /c const.cpp
// Borland: bcc /c const.cpp
//////////////////
// A class where functions that are really const are not declared such.
//
class Size {
private:
int cx;
int cy;
public:
Size(int w, int h) { cx=w; cy=h; }
int Width() { return cx; } // should be const
int Height() { return cy; } // should be const
};
//////////////////
// A class that tries to use const where appropriate
//
class Window {
private:
int m_bSizeKnown; // whether size is known or not
Size m_size; // known size
void FigureOutSize() { /* pretend this actually does something */ }
public:
Window();
int Width() const; // getting the width doesn't change the object
int Height() const; // ..ditto for height
};
Window::Window() : m_size(0,0)
{
m_bSizeKnown = 0;
}
//////////////////
// This function generates two compiler errors.
//
int Window::Width() const
{
if (!m_bSizeKnown) {
FigureOutSize(); // Error #1: FigureOutSize is non-const
m_bSizeKnown = 1; // Error #2: I'm modifying a member!
// I don't want either of these to "count" as a change, since I'm
// not really modifying the Window. m_bSizeKnown is just an
// implementation hack so I only have to compute the size once.
//
}
// Error #3: The author of the Size class forgot to declare Size::Width
// const. It really is const, but the compiler doesn't know that, so it
// complains:
return m_size.Width();
}
//////////////////
// This similar function shows how to work around the errors.
// Window::Height compiles with no errors.
//
int Window::Height() const
{
if (!m_bSizeKnown) {
// Error #1,#2 1 workaround: The way the compiler handles const
// functions is by effectively declaring "this" a const pointer.
// In other words, "this" is really "const Window*", not "Window*".
// So all you have to do is cast away the const-ness.
//
((Window*)this)->FigureOutSize();
((Window*)this)->m_bSizeKnown = 1;
}
// Error# 3 workaround: Likewise, in a const member function, the
// compiler considers all class members const, so it's as if m_size
// were "const Size m_size" instead of "Size m_size". So to defeat the
// compiler, you can again cast the const-ness away:
return ((Size&)m_size).Height(); // cast to (non-const) reference
}
Figure 2 Scribble
Figure 2 Scribble//////////////////
SCRIBBLE.H
#include "resource.h" // main symbols
class CScribbleApp : public CWinApp {
public:
.
.
.
//{{AFX_MSG(CScribbleApp)
.
.
.
afx_msg void OnFileSample(); // new command handler
//}}AFX_MSG
.
.
.
};
SCRIBBLE.RC
.
.
.
/////////////////////////////////////////////////////////////////////////////
//
// SCRIBDOC
//
IDR_MAINFRAME SCRIBDOC MOVEABLE PURE "res\\sample.scb"
.
.
.
SCRIBBLE.CPP
////////////////////////////////////////////////////////////////
// Modified SCRIBBLE Copyright 1996 Microsoft Systems Journal.
//
// This modified SCRIBBLE program shows how to store a document in the
// resource file and then load it using the normal serialization mechanism.
// For brevity, only the changes to the original Scribble files are shown.
//
#include "stdafx.h"
.
.
.
BEGIN_MESSAGE_MAP(CScribbleApp, CWinApp)
.
.
.
ON_COMMAND(ID_FILE_SAMPLE, OnFileSample) // New command
END_MESSAGE_MAP()
.
.
.
//////////////////
// Handle "File New Sample"
//
void CScribbleApp::OnFileSample()
{
OnFileNew(); // create new (empty) doc
CScribbleDoc* pDoc = (CScribbleDoc*)
((CFrameWnd*)m_pMainWnd)->GetActiveFrame()->GetActiveDocument();
ASSERT_VALID(pDoc);
ASSERT_KINDOF(CScribbleDoc, pDoc);
// Use new fn to load resource doc IDR_MAINFRAME
VERIFY(pDoc->OnOpenDocument(IDR_MAINFRAME));
// give it a name
pDoc->SetPathName(_T("Sample.SCB"));
}
SCRIBDOC.H
.
.
.
class CScribbleDoc : public CDocument {
protected:
.
.
.
virtual BOOL OnOpenDocument(UINT uIDRes); // new overloaded function
.
.
.
};
.
.
.
SCRIBDOC.CPP
#include "stdafx.h"
.
.
.
//////////////////
// Overloaded version of OnOpenDocument loads SCRIBBLE doc from
// resource data instead of disk file. Arg is ID of resource.
//
BOOL CScribbleDoc::OnOpenDocument(UINT uID)
{
// Load the resource into memory
//
HINSTANCE hinst = AfxGetInstanceHandle();
HRSRC hRes = FindResource(hinst, (LPCSTR)uID, "SCRIBDOC");
if (hRes==NULL) {
TRACE("Couldn't find SCRIBDOC resource %d!\n", uID);
return FALSE;
}
DWORD len = SizeofResource(hinst, hRes);
// Note: Under Win16, you have to do Lock/UnlockResource, but
// in Win32 these are no-ops and you can just cast the HGLOBAL
// returned by LoadResource to a BYTE*.
//
BYTE* lpRes = (BYTE*)LoadResource(hinst, hRes);
ASSERT(lpRes);
// Create mem file
//
CMemFile file(lpRes, len);
// Create archive and load from it.
// This is just like CDocument::OnOpenDocument
//
BOOL bRet = FALSE; // assume failure
DeleteContents();
CArchive ar(&file, CArchive::load);
ar.m_pDocument = this;
ar.m_bForceFlat = FALSE;
TRY {
CWaitCursor wait; // hourglass
Serialize(ar); // do normal Serialize thing
ar.Close(); // done
bRet = TRUE; // success
SetModifiedFlag(FALSE);
} CATCH_ALL(e) {
TRACE("Unexpected exception loading SCRIBDOC %d\n", uID);
DeleteContents();
} END_CATCH_ALL
FreeResource((HANDLE)lpRes);
return bRet;
}
.
.
.
Figure 4 How to use CMemFile
BOOL CScribbleDoc::OnOpenDocument(UINT uID)
{
BYTE* lpRes = // load resource uID
.
.
.
// Create memfile. lpRes points to resource
// in memory; len is size in bytes.
//
CMemFile file(lpRes, len);
// Create archive and Serialize. This is just
// like CDocument::OnOpenDocument(LPCSTR).
//
BOOL bRet = FALSE; // assume failure
DeleteContents();
CArchive ar(&file, CArchive::load);
ar.m_pDocument = this;
ar.m_bForceFlat = FALSE;
Serialize(ar); // do it
}
Figure 5 Changes for SCBM
////////////////////////////////////////////////////////////////
// Modifications to CScribbleDoc for solution #1
// (In SCRIBBLE\ScribDoc.cpp)
//////////////////
// **MOD**
// Override SetModified flag to notify view when doc has changed,
// so the view can tell the frame to update the title.
//
void CScribbleDoc::SetModifiedFlag(BOOL bModified)
{
if (IsModified() != bModified) {
CDocument::SetModifiedFlag(bModified);
UpdateFrameCounts();
CString title = GetTitle();
int asterisk = title.GetLength()-1;
if (asterisk >=0 && title.GetAt(asterisk)=='*') {
if (!bModified)
title.SetAt(asterisk,0);
} else if (bModified)
title += '*';
SetTitle(title);
}
}
OD1
Figure 6 Changes for SCBMOD2
////////////////////////////////////////////////////////////////
// Modifications to CChildFrame for solution #2
// (In SCRIBBLE\ChildFrm.cpp)
#include <afxpriv.h> // for WM_IDLEUPDATECMDUI
.
.
.
BEGIN_MESSAGE_MAP(CChildFrame, CMDIChildWnd)
//{{AFX_MSG_MAP(CChildFrame)
ON_MESSAGE(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI) // handle idle UI msg
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
CChildFrame::CChildFrame()
{
m_bModLast = -1; // initialize modified-state memory
}
//////////////////
// Override to add asterisk if doc is modified.
//
void CChildFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
{
CMDIChildWnd::OnUpdateFrameTitle(bAddToTitle);
CDocument* pDoc = GetActiveDocument();
if (pDoc && pDoc->IsModified()) {
// if doc modified, append * to normal title
CString title;
GetWindowText(title);
title += '*';
SetWindowText(title);
}
}
//////////////////
// **MOD**
// Handle WM_IDLEUPDATECMDUI to update modified indicator if necessary.
//
LRESULT CChildFrame::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM)
{
// Only update the title if the modified state has changed.
// Otherwise, the title bar will flicker.
//
CDocument* pDoc = GetActiveDocument();
if (m_bModLast != pDoc->IsModified()) {
// mod state has changed: set flag telling MFC to update title
m_nIdleFlags |= idleTitle;
m_bModLast = pDoc->IsModified();
}
// do the default thing
CMDIChildWnd::OnIdleUpdateCmdUI();
return 0L;
}