Document object の置き換え †前節までで、command line で指定された file を表示することはできるようになった。しかしながら、 program 起動後に file を読みたいことや、更に別の file を表示させたいことも多い。Windows program では、むしろその用法の方が多い。これを実装するには、current document の置き換えが必要になる。起動後の表示は empty document との置き換えと考えればよい。 前節の実装では、current document は pointer として保持していたので、その思想をそのまま延長すると、
とすればよい。 しかしながら、例えば印刷とか、file への保存などを表示と同時に行いたい場合など単純に delete できない。どれが先に終了するかわからない複数の thread が同一 object に access するので、固定されたどれかの thread で消去するわけにはいかない。これを安全に実行するためには、各 thread 毎に object のコピーを持たせるのがよいが、画像などでは占有 memory が大きいことも想定され、非効率になることも考えられる。そこで、ここでは handle を使った方法で実装する。 ここでの source 類は Document の handle 化 †// mydoc.h
#ifndef MYDOC_H_INCLUDED
#define MYDOC_H_INCLUDED
#include "myconten.h"
namespace mylib
{
class CMyDoc
{
public:
CMyDoc();
CMyDoc(CMyContent* pContent);
CMyDoc(const CMyDoc& src);
virtual ~CMyDoc();
CMyDoc& operator=(const CMyDoc& src);
SIZE Size(HDC hdc);
int Draw(HDC hdc, int x, int y, PAINTSTRUCT& ps);
protected:
int* m_pCounter;
CMyContent* m_pContent;
};
} // namespace mylib
#endif // MYDOC_H_INCLUDED
以下に実装を示すが、代入演算子で、自己代入の検査を行う部分が、m_pContent の比較ではなく、m_pCounter の比較であることに注意する。m_pContent は 0(NULL) に成り得るので、同一 object でなくとも互いに 0 で一致する可能性があるからである。一方、m_pCounter は必ず new するので、0 で一致することはない。 // mydoc.cpp
#include "mydoc.h"
namespace mylib
{
CMyDoc::CMyDoc() :
m_pCounter(new int(1)), m_pContent(0)
{
}
CMyDoc::CMyDoc(CMyContent* pContent) :
m_pCounter(new int(1)), m_pContent(pContent)
{
}
CMyDoc::CMyDoc(const CMyDoc& src) :
m_pCounter(src.m_pCounter), m_pContent(src.m_pContent)
{
(*m_pCounter)++;
}
CMyDoc::~CMyDoc()
{
if(--(*m_pCounter) == 0)
{
delete m_pContent;
delete m_pCounter;
}
}
CMyDoc&
CMyDoc::operator=(const CMyDoc& src)
{
if(m_pCounter == src.m_pCounter)
return *this;
if(--(*m_pCounter) == 0)
{
delete m_pContent;
delete m_pCounter;
}
m_pContent = src.m_pContent;
m_pCounter = src.m_pCounter;
(*m_pCounter)++;
return *this;
}
SIZE
CMyDoc::Size(HDC hdc)
{
SIZE sz;
if(m_pContent)
sz = m_pContent->Size(hdc);
else
{
sz.cx = 0;
sz.cy = 0;
}
return sz;
}
int
CMyDoc::Draw(HDC hdc, int x, int y, PAINTSTRUCT& ps)
{
int n;
if(m_pContent)
n = m_pContent->Draw(hdc, x, y, ps);
else
n = 0;
return n;
}
} // namespace mylib
CMyApp class と CMyAppWin class の変更 †上記変更に伴い従来 myapp.h の中で public: CMyContent* m_pDoc; としていた部分を public: CMyDoc m_curDoc; とする。また、これに伴い destructor 中での delete m_pDoc; は不要となるので削除する。 Run 関数まわりは若干変更となるので全部示しておく。 int
CMyApp::Run(int nCmdShow)
{
// Create Window
CMyAppWin appwin(m_szAppTitle);
// Create the document
if(m_DocName.size() > 0)
{
std::vector<TCHAR>::const_iterator p = m_DocName.begin();
switch(ContentType(&*p))
{
case CMyContent::TEXT_PLAIN:
m_curDoc = CMyDoc((CMyContent*)(new CMyPlain(&*p)));
break;
case CMyContent::IMAGE_JPEG:
m_curDoc = CMyDoc((CMyContent*)(new CMyDjpeg(&*p)));
break;
case CMyContent::IMAGE_BMP:
m_curDoc = CMyDoc((CMyContent*)(new CMyBMP(&*p)));
break;
default:
break;
}
}
appwin.Show(nCmdShow);
appwin.Update();
POINT pt;
pt.x = 0;
pt.y = 0;
HDC hdc = appwin.GetDC();
SIZE sz = m_curDoc.Size(hdc);
appwin.ReleaseDC(hdc);
appwin.SetDrawSize(pt, sz);
// Enter Message Loop
MSG msg;
while(GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
一方、CMyAppWin の方は、OnPaint 関数が BOOL
CMyAppWin::OnPaint()
{
PAINTSTRUCT ps;
HDC hdc = CMyWin::BeginPaint(&ps);
g_pApp->m_curDoc.Draw(hdc, -m_ptOffset.x, -m_ptOffset.y, ps);
CMyWin::EndPaint(&ps);
return TRUE;
}
となるだけである。 |