fw: 侯捷--MFCLite

原创 2004年08月10日 14:27:00

汗如雨下

寄件者: xioax
收件者: 侯 捷
傳送日期: 2002年1月22日 AM 09:36
主旨: 關於MFCLite的一些問題﹗

候老師﹕
您好﹐我是一名剛從浙大畢業的學生﹐也是您的忠實讀者。最近我在閱讀您的MFCLite﹐發現了些問題﹐列舉如下(如有不對的地方﹐還請老師指點)﹕

1﹑對象持久性。由於兩年前我曾仔細研究過Turbo Vision的持久性﹐對這個問題比較熟悉。所以一開始我就發現了您的代碼中所存在的問題﹐下面是我的測試程序(部份)﹐還有輸出文件的內容﹕
...
CSquare *psqr1 = new CSquare () ;
CSquare *psqr2 = psqr1 ; // psqr1 and psqr2 point to same object
assert (psqr1 == psqr2) ;
{
? CFile write ("test.tmp", CFile::modeWrite) ;
? CArchive store (&write, CArchive::store) ;
? store << psqr1 << psqr2 ;
}
delete psqr1 ;
psqr1 = psqr2 = NULL ;
{
? CFile read ("test.tmp", CFile::modeRead) ;
? CArchive load (&read, CArchive::load) ;
? load >> psqr1 >> psqr2 ;
}
assert (psqr1 == psqr2) ; // here is an assert failure, not point to same object!
...
00 00 07 00 43 53 71 75 61 72 65 00 00 00 00 00 ....CSquare.....
00 00 00 00 00 00 00 00 00 07 00 43 53 71 75 61 ...........CSqua
72 65 00 00 00 00 00 00 00 00 00 00 00 00 re..............

psqr1和psqr2指向同一對象﹐寫入文件時應該只有一份﹐但是在您的實現中卻寫了兩次﹗導致了讀出時﹐psqr1和psqr2指向了不同的對象。顯然這是不正確的。我覺得對於C++ 對象持久性而言﹐最重要的問題﹕一個是如何保存相關的類信息﹐另一個就是如何解決上述問題﹗在您的兩本著作《多形與虛擬》﹑《深入淺出MFC》中對前者都有很精闢的論述﹐唯獨後者一點也沒有提及﹐不能不說是一個很大的瑕疵。對於如何解決這個問題也不是很困難﹐只要先實現CMapPtrtoPtr和CPtrArray﹐在寫入時先查map如果已寫過﹐就只把輸出序號寫入文件﹐如果沒有就把對象的地址和輸出序號插入map﹐再把數據寫入文件。讀出時﹐遇到第二種情況(即文件中有實際數據,而非只是序號)﹐就先創建一個對象把數據讀出﹐接著再把新建對象的
地址加到數組array尾端﹐遇到第一種情況﹐就以輸出序號為索引直接從數組中得到對象(由於寫入和讀出的順序一樣﹐僅用輸出序號就可以完全解決問題)。

2﹑CPtrList的實現有內存泄漏﹐下面是相關代碼和我的修改(紅色)﹕
...
我覺得就MFCLite的目的而言﹐完全沒有必要象MFC那樣實現一些比較高級的內存管理技巧。因為一來這些內存管理與MFC的應用程序架構完全沒有關係﹐二來加大了讀者的閱讀難度﹐三來加大了MFCLite的出錯機會。如果CPtrList使用CObList的實現方法就沒有這麼多問題﹗
另外﹐您說在下面的代碼中﹐刪除CPtrList中的元素程序會崩潰﹐不知是不是上面的原因﹖

void CMultiDocTemplate::RemoveDocument(CDocument* pDoc)
{
CDocTemplate::RemoveDocument(pDoc);
// m_docList.RemoveAt(m_docList.Find(pDoc)); // 把 doc 節點移走 <- 有問題. crash!
}

3﹑CDocument::OnOpenDocument和CDocument::OnSaveDocument有內存泄漏﹕

BOOL CDocument::OnOpenDocument(const string& strFileName)
{
? CFile* pFile = new CFile(strFileName.c_str(), CFile::modeRead);
? CArchive loadArchive(pFile, CArchive::load);
? Serialize(loadArchive);
? loadArchive.Close();
? pFile->Close();
? // should delete pFile
? delete pFile ;
? return TRUE;
}
BOOL CDocument::OnSaveDocument(const string& strFileName)
{
? CFile* pFile = new CFile(strFileName.c_str(), CFile::modeWrite);
? CArchive saveArchive(pFile, CArchive::store);
? Serialize(saveArchive);
? saveArchive.Close();
? pFile->Close();
? // should delete pFile
? delete pFile ;
? return TRUE;
}

4﹑我發現CObject的析構函數不是虛擬的。我想一定是老師的一時疏忽。:-)面對幾千行的
代碼﹐誰敢保證沒錯呢﹖﹗

5﹑MFCLite對新建文件﹑打開文件和保存文件模擬得很好﹔但是對關閉窗口﹐進而造成文檔的摧毀卻模擬得不夠。其實在C++中由於析構錯誤造成的內存泄漏﹐往往是最常見﹐也是最難
排除的Bug。希望在MFCLite的新版本能看到相應的模擬﹗


雖然MFCLite存在一些不足﹐但是瑕不掩玉﹐我覺得它對C++和MFC的學習都有非常非常大的幫助。老師的每一本書(無論是譯作還是著作)都是相關領域的精品﹐凡是在市面上見得到的我都買了下來﹐給我帶來的極大的幫助。現在我正在急切地等待老師的《STL原碼剖析》﹑《泛型設計與STL》﹑《標準C++程式庫》等書籍。希望老師注意身體﹐給我們寫出更好的作品﹗



Hi, xioax, 你好:

以下回答你的一些疑問與建議。

> 1﹑對象持久性。由於兩年前我曾仔細研究過Turbo Vision的持久性﹐對這個問題比較熟悉。所以一開始我就發現了您的代碼中所存在的問題﹐下面是我的測試程序(部份)﹐還有輸出文件的內容﹕…

> 我覺得對於C++ 對象持久性而言﹐最重要的問題﹕一個是如何保存相關的類信息﹐另一個就是如何解決上述問題﹗在您的兩本著作《多形與虛擬》﹑《深入淺出MFC》中對前者都有很精闢的論述﹐唯獨後者一點也沒有提及﹐不能不說是一個很大的瑕疵。對於如何解決這個問題也不是很困難﹐只要先實現CMapPtrtoPtr和CPtrArray﹐在寫入時先查map如果已寫過﹐就把輸出序號寫入﹐如果沒有就把對象的地址和輸出序號插入map﹐再把數據寫入文件。讀出時﹐遇到第二種情況﹐就先創建一個對象把數據讀出﹐接著再把新建對象的地址加到數組尾端﹐遇到第一種情況﹐就以輸出序號為索引直接從數組中得到對象(由於寫入和讀出的順序一樣﹐僅用輸出序號就可以完全解決問題)。


●侯捷回覆:你所指出的,對我是當頭棒喝。的確,模擬Persistence時我很少考慮你所說的(alias)情況。針對你的提議,我已修正 MFCLite3並開放於本網頁

MFC(Lite) 不但考慮了你所說的那種(alias)情況,還對「隸屬相同class」的不同objects的文件讀寫做了優化考量。對於已儲存 class information(例如class name和 schema no.)的 class而言,再儲存一次是一種浪費 — 浪費空間也浪費時間。因此,MFC的 CArchive利用CMapPtrToPtr做為cache,不但用來安置CObject*,也用來安置CRuntimeClass*。讀取文件時,道理相同,使用CPtrArray,不但安置CObject*也安置CRuntimeClass*。我不打算在MFCLite中再多加上CMapPtrToPtr和CPtrArray的實作(那恐怕又多出1000行代碼,而且CMapPtrToPtr是以 hash table完成,那就使閱讀的門檻又高了些),我已在新版的 MFCLite 3.0 中以C++ 標準程式庫的 map和 vector 取而代之。

MFCLite的閱讀門檻愈壘愈高,樂了諸位功底深厚的人,可難為了其他功力尚淺的讀者呀。對我而言,取捨成了難事。

我保留並擴大了你的測試,放在 MFCLite Application (mfclapp.cpp) 的 CMyWinApp::InitInstance()中。程式一執行就會在螢幕上顯示這段測試結果並顯示 StoreMap 和? LoadArray,然後才開始一般正常的執行流程。你對persistence 的這段 hint 帶給我莫大樂趣,迫使我完成《深入淺出MFC》第 8 章關於 document format 中的 tags(圖8-10a, 圖8-10b內的 FFFF, 8001, 8003...以及未出現於該圖的 tags 如 0002, 0005...)的深刻體會。


> 2﹑CPtrList的實現有內存泄漏﹐下面是相關代碼和我的修改(紅色)﹕…

●侯捷回覆:修改完畢。我沒有採用你的方式,而是採用MFC的方式。當初實作MFCLite時,為求簡化略去了一些東西,不想造成了memory leak 而不自覺。現補上。我的額頭開始冒汗了。

> 我覺得就MFCLite的目的而言﹐完全沒有必要象MFC那樣實現一些比較高級的內存管理技巧。因為一來這些內存管理與MFC的應用程序架構完全沒有關係﹐二來加大了讀者的閱讀難度﹐三來加大了MFCLite的出錯機會。如果CPtrList使用CObList的實現方法就沒有這麼多問題﹗

●侯捷回覆:CPtrList的精巧設計,與application framework之間非常獨立。雖然它比較複雜,應該不至於混亂讀者對application framework的學習。保留這麼複雜的list實作,有兩個用意,(1) CPtrList是MFC內部自我維護管理時,大量運用的一個data structure,因此對於效率(空間和時間)都很要求 (2)既然其十分獨立,再複雜也不至於混亂讀者對application framework的學習,那麼展示一下這種不錯的設計,滿好的。

> 另外﹐您說在下面的代碼中﹐刪除CPtrList中的元素程序會崩潰﹐不知是不是上面的原因﹖

●侯捷回覆:不是。原因已查出,乃因我把所有的documents的 delete動作放錯位置。這些動作在 MFC 之中應該由 CFrameWnd::OnClose()觸發(目前MFCLite尚未實作之),我卻把它們放在?CMultiDocTemplate::~CMultiDocTemplate()。下面是 MFCLite 的 CallStack:

CMultiDocTemplate::RemoveDocument() // (B)
CDocument::~CDocument()
CMultiDocTemplate::~CMultiDocTemplate() // (A)
CDocManager::~CDocManager()
CWinApp::~CWinApp()

(A) 已針對 pDoc 呼叫 m_docList.RemoveAt(),
(B) 又針對 pDoc 呼叫 m_docList.RemoveAt(),但 pDoc 當時已經不在 m_docList 中了,Find() 傳回 NULL,RemoveAt(NULL) 當然就掛了。

治本之道是模擬 MFC,開發 window close system(也就是你的第5個問題),治標之道則是在 CPtrList::RemoveAt() 一開始增加一行,判斷刪除位置是否為 NULL,如是則立刻回返。

> 3﹑CDocument::OnOpenDocument和CDocument::OnSaveDocument有內存泄漏﹕

●侯捷回覆:應該說是資源洩漏(resource leak)。已全部補妥。我的身上開始冒汗了。修補這個問題的同時,一併修補了 CFile::~CFile() 和 CFile::Close() 內的安全檢驗工作。

> 4﹑我發現CObject的析構函數不是虛擬的。我想一定是老師的一時疏忽。:-)面對幾千行的代碼﹐誰敢保證沒錯呢﹖﹗

●侯捷回覆:全部補妥。我汗流夾背了。

> 5﹑MFCLite對新建文件﹑打開文件和保存文件模擬得很好﹔但是對關閉窗口﹐進而造成文檔的摧毀卻模擬得不夠。其實在C++中由於析構錯誤造成的內存泄漏﹐往往是最常見﹐也是最難排除的Bug。希望在MFCLite的新版本能看到相應的模擬﹗

●侯捷回覆:汗如雨下。對於你所提的問題,我原本模擬了一些,後來覺得有點煩,不在我最關心的主軸範圍內,就放下了。針對你的提議,我可能會在MFCLite3.0中實作出來,也可能在書中描述這些問題,留給讀者去實現。

> 雖然MFCLite存在一些不足﹐但是瑕不掩玉﹐我覺得它對C++和MFC的學習都有非常非常大的幫助。老師的每一本書(無論是譯作還是著作)都是相關領域的精品﹐凡是在市面上見得到的我都買了下來﹐給我帶來的極大的幫助。現在我正在急切地等待老師的《STL原碼剖析》﹑《泛型設計與STL》﹑《標準C++程式庫》等書籍。希望老師注意身體﹐給我們寫出更好的作品﹗

●侯捷回覆:你非常優秀,基本功非常好。浙大電子工程系的學生真是令我刮目相看呀。《STL源碼剖析》和《C++標準程式庫》簡體版出版後各贈你一本,表示我的感謝,以及對你的期許。《泛型程式設計與STL》侯捷譯本只有繁體版,連同《C++ Primer 3e》侯捷譯本(只有繁體版)近日一起寄送給你。請告訴我你的郵寄地址。我只能寄海運(大約需時25天),空運實在太貴了 :)??

你有非常好的代碼追蹤能力,像獵犬一樣的鼻子 :)。我想你肯定用心追蹤過 MFC 源碼。賞析名家手法,是將自己拉抬到制高點的一個重要法門,形同大師灌頂。我自己離開編程第一線後,以此法修練自己,開拓自己。對於你,一個剛畢業的小夥子,我感到十分好奇。(你怎麼知道你的第一個問題的解法?你自行閱讀MFC源碼而參悟的嗎?真是不簡單)

感謝你給我如此豐富的訊息。MFCLite 十分複雜,能夠看懂它又指出問題,切中要害,甚至提出解法,實在很不容易。目前尚未有任何一位讀者給我關於MFCLite 的訊息。我聽說浙大的計算機水準很高,從你的來信得到了一些證明 :)

Servlet2.5和 3.0区别(Servlet 3.0 新特性详解)

Servlet 3.0 新特性概述 Servlet 3.0 作为 Java EE 6 规范体系中一员,随着 Java EE 6 规范一起发布。该版本在前一版本(Servlet 2.5)的基础上提...
  • fuxiaohui
  • fuxiaohui
  • 2017年05月26日 02:14
  • 3019

蓝牙4.0和3.0区别

简介   蓝牙4.0为蓝牙3.0的升级标准。   蓝牙4.0最重要的特性是省电科技,极低的运行和待机功耗可以使一粒纽扣电池连续工作数年之久。此外,低成本和跨厂商互操作性,3毫秒低延迟、100米以上...
  • liuhaomatou
  • liuhaomatou
  • 2014年09月10日 16:48
  • 9558

Android Studio 3.0全新时代:带来的一些新功能

前言Android Studio从3.0版本新增了许多功能,当然首当其冲就是从3.0版本新增了对 Kotlin 开发语言的支持,除此之外还有其他一些新功能,例如:Android Profiler (其...
  • guolipeng_network
  • guolipeng_network
  • 2017年07月06日 19:04
  • 15778

使用Application Loader 3.0 上传ios手机应用安装文件一般步骤

1. 登陆application loader。 2. 选择”交付您的应用程序”,点击 “选取”。 3. 选择需要上传的应用安装文件。 4. 确认应用详细信息。确认无误点击下一步. ...
  • yuh_c
  • yuh_c
  • 2016年04月08日 21:01
  • 1915

Flash as2.0与3.0功能性能详细对比

一、flash as2.0 与as3.0的定义 ActionScript 2.0:实际上是as1.0的升级版,首次将OOP(Object Oriented Programming,面向对象的程序...
  • caiwenfeng_for_23
  • caiwenfeng_for_23
  • 2013年03月09日 11:32
  • 11216

detours使用:detours3.0文档翻译

来自:http://blog.csdn.net/buck84/article/details/8289991 目录(?)[-] 拦截二进制函数使用DetoursPayloa...
  • xrain_zh
  • xrain_zh
  • 2015年04月07日 11:49
  • 723

Spring 3.0 学习-环境搭建和三种形式访问

理论学习 ·PO(persistent object)是持久化对象,所谓的持久化就是和数据库对应的主要是字段上,典型的应用是在hibernate中通过实体对象直接操作数据库的增删查改。一般提供get、...
  • bestcxx
  • bestcxx
  • 2016年09月09日 17:10
  • 2229

SunlightDB区块链3.0技术架构简介

SunlightDB是国内唯一符合3.0架构设计,且产品成熟的区块链3.0技术支持平台。
  • Dreamcode
  • Dreamcode
  • 2017年07月06日 23:08
  • 3001

[转发]在eclipse中新建Dynamic web project时选择2.5和3.0的区别

1、Dynamic web project时选择2.5和3.0是指servlet的版本,是2.5的还是3.0的     servlet3.0以后支持异步   2、dynamic we...
  • marsercn
  • marsercn
  • 2017年05月30日 13:26
  • 1041

Hadoop3.0的新特性

1. Hadoop3.0简介 Hadoop 2.0是基于JDK 1.7开发的,而JDK 1.7在2015年4月已停止更新,这直接迫使Hadoop社区基于JDK1.8重新发布一个新的Hadoop版...
  • sinat_31726559
  • sinat_31726559
  • 2016年08月03日 14:24
  • 1535
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:fw: 侯捷--MFCLite
举报原因:
原因补充:

(最多只允许输入30个字)