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 類は myapp08.tgz として固めてある。 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; } となるだけである。 |