玄歌

唯快不破!
   ::  :: 新随笔 ::  ::  :: 管理

公告

2012年5月18日

1.    连接必须显式关闭,否则会造成大量内存占用无法回收。

2.    关闭连接之前,所有基于该连接的命令必须先析构。

3.    删除海量数据,不如重建表格。

4.    相应的Wrapper最好设为inline。

5.    海量数据的查询性能,常常是因为特定字段无索引、取出不需要的字段造成的。

6.    无需建立许多结构,多用tuple之类。

7.    数据访问逻辑和业务逻辑、ModelView不是一回事,尽量清晰界定,在单一的语境思考。

8.   不要用一次性的事务,insert之类没一万次提交、重启事务比较合理。

9.   release模式比Debug模式快30倍是很正常的,所以无需过多担忧。

posted @ 2012-05-18 10:49 玄歌 阅读(7) 评论(0) 编辑

2011年12月12日

    对于正常的应用项目开发来说,入门显然有两块比较重要的:如何方便的设计界面,业务对象如果与界面交互数据。只有解决这两个问题,你才能真正开始动手。很显然,完全动态的用代码来一个个的在View中写控件这种方式,明显的属于高手范畴,很多人号称完全用记事本就能写程序。不过,从工业角度来说,这种炫耀的方式,显然是很奇怪的,为什么不能用最简单的办法?

   VC里我们唯一能够看到的可视化设计器,是对话框设计器。老实说,Ribbon设计器仿佛也不错,但你总不可能将所有界面都放到Ribbon工具栏里吧?虽然这并不是不可行的,你可以想象一下,整个应用就由Ribbon工具栏组成----微软曾经有例子在Ribbon工具栏实现一个计算器,作为一个反面的例子,来说明“不能这样使用Ribbon”,因为整体看起来确实会显得太奇怪。

   所以对每一项主要的功能,对话框还是无法绕过的,我目前所知,用于操纵对话框的类,包括:CDialogEx,CFormView,CMFCPropertyPage几种,最直观的当然是对话框类。

 

   所以这一节我们讲解对话框的数据交换:我们怎样在对话框类中创建我们的对象与实际的对话框控件联系起来,从而获取对话框控件的值或者为对话框控件设置相应的值。只有两种方法,一种是DDX数据交换机制,一种是GetDlgItem方式。

    前者实际上是建立一个数据交换机制,当你执行UpDateDate()的时候,所有控件都使用当前用户的输入,更新你绑定的变量。而执行UpdateData(false)的时候,则反过来,你所有绑定的变量值都将传递给控件。这里“正反”记忆的方式:updateData的意思是控件更新你的数据,这是其基本功能,带false参数自然是反向操作。

    后者,则是直接获取或者设置某个对话框控件的值,这个需要带控件的ID参数。

    如何选择呢?实际上,DDX机制我们多可以通过类向导简便的完成,不需要考虑太多,只要记得及时或者需要的时候UpdateData就行了,我们当然选择这种方式,另一种方式我们就无需理会了,节约脑力。

 

    那么我们考虑先看看导入日线对话框的外观:

更新日线对话框

 

用户在编辑浏览控件中,选中文件,那么我们需要一个变量绑定这个控件的值。选中这个控件,右键,添加变量,会出现下面的选项窗体:

添加变量

请注意“类别”有“Value”和”Control”两种选择,前者表示我们只关心这个控件的值,无论是字符串或其他类型,此刻是一个CString对象;后者则是要求替我们创建一个完整的对象,对应这个控件,将是一个CMfcEditBrow的实例。同时也注意生成的变量名称命名风格,成员以m_开始。

完成之后,看看代码有什么变化:

1、首先,头文件中对话框类增加了一个成员:

   CString m_FilePath;

2、其次,源文件中,构造方法对这个成员初始化


CQuoteDialog::CQuoteDialog(CWnd* pParent /*=NULL*/)
    : CDialogEx(CQuoteDialog::IDD, pParent)
    , m_FilePath(_T(""))
    , m_Info(_T(""))
    , m_lastday(_T(""))
{

}

3、源文件中,在DoDataExchange里面自动添加了相应的映射,这个方法显然是在UpdateDate时会运行的。

void CQuoteDialog::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
    DDX_Text(pDX, IDC_MFCEDITBROWSE_QUOTE, m_FilePath);
    DDX_Text(pDX, IDC_STATIC_INFO, m_Info);
    DDX_Control(pDX, IDC_PROGRESS_QUOTE, ProgressBar);
    DDX_Text(pDX, IDC_STATIC_LASTDAY, m_lastday);
}

当然,不需要我们做其他的事情,我们仅仅需要理解这些简单的操作,一个变量的值和我们对话框类的一个成员就绑定起来,不过需要你手工使用UpdateData来双向的交换。对于这些代码的阅读,我们仍然和针对消息映射的处理一样,仅从字面上理解:

    DDX_Text:在控件和这个成员变量,交换值。

    DDX_Control:完整的复制控件内容和到相应的控件对象。

    很明显,这样的方法都包括控件ID和类成员两个参数,事实上是建立双方的映射关系。不要继续探究,那样很费脑力且对你的工作、思考、理解帮助不大。

 

那么我们看看怎样使用这个变量

1、获取:任何时候,我们只要下面两行语句就可以获取最新的值:

      UpdateDate()

      然后使用m_FilePath

2、设置:

    m_FilePath=“C:\sample.dad”;

    UpdateData(false);

 

这样,我们在使用对话框编程的时候,最基本的问题就解决了:这是一种老式的、手工的、简单的数据绑定方式,但它能够正常工作。

我们小结一下,在界面开发模式方面,最终的选择:

1、我们使用单文档结构创建项目。

2、我们使用Ribbon在各项功能间导航

3、我们让视图类、文档类无事可做,通过在视图中切换显示子对话框,来实现不同功能的界面切换,所有界面开发基于对话框。

4、在资源视图中设计对话框,实际上是保留了对话框的位置、大小、颜色等信息,其中包括的各个控件,以及这些控件的属性,和代码无关,是纯粹的数据文件。

5、由于对话框的布局方式比较原始,因此我们默认让多数对话框居中显示,少数需要适应主窗口大小的对话框,使用EasySize解决。

6、我们使用DDX数据交换机制,绑定类成员和实际的控件。

这种模式,已经能做出比较专业的界面,同时也最充分的利用了VS提供的各类工具,比如Ribbon设计器、对话框设计器、类向导。

 

另外,遇到一些简单的问题,没有必要“冰天雪地裸体旋转三百六十度跪求各位大大”,或者扮作娘们“小女子有个弱弱的问题有请各位大哥……”,稍稍动点脑筋,在MSDN里基本上都能解决:

1、VC的示例:

http://archive.msdn.microsoft.com/vcsamplesmfc

2、VC帮助:

http://msdn.microsoft.com/library/60k1461a(VS.100).aspx

posted @ 2011-12-12 12:11 玄歌 阅读(1607) 评论(3) 编辑

2011年12月8日

      作为程序员,我们其实生活在一个荒诞的空间。每次看到白发苍苍的教授,很认真很庄重的说:“面向对象灰常灰常重要”的时候,我都完全忍不住要笑出来。我们用所有的词,第一个反映怕是从字面上来理解。对象,准备结婚的异性,面,你的脸,向,朝着她。你的脸朝着异性,接下来会发生什么,外交部曰:“那是他们的内政,各国无权干涉”。教授究竟在教我们什么?

    所以在这个众牛奔腾的圈子里,好象每个人都生活在这样香艳的语境中。随随便便拉头牛过来,他估计能够告诉你面向对象是多么的重要、多么的牛,你不会面向对象你都不好意思摸着键盘。你要问他什么是面向对象,他会告诉你:“嗯,不是写个类就完了,这是一种思想,一种……”,然后还是归于那三个指头、五个字,“博大精深”。

    台湾对object的翻译有些不同,相对来说更合理一些:“物件”,面向对象被翻译成“物件导向”。世易时移,这两片土地的语言都在渐渐分化,作为反面角色的我们,早已经成为文化的沙漠和马某人的余孽。那是题外话,说正题。如果用今天的汉语,准确的翻译,我猜测只有一个词:“东西”,“面向对象”不妨翻译成“站在东西的角度”。

    汉语原本是比较模糊的语言,生造一些词汇,或者给一些词汇赋予完全不一样的含义,这是最糟糕的情形。之所以最糟糕,因为我一向所强调的“简单”,首当其冲的就是“理解的简单”。站在东西的角度,这个小学生都能够理解,而面向对象,其他专业的博士生都无法理解。之所以会出现这类翻译,是因为语言和技术都精通,而且具备专业翻译技巧的人,几乎没有。最明显的例子,大家可以看看微软的Team Fundation Server,里面的ms agile模版,其中优先级,翻译成“堆栈级别”;然后“产品积压工作”、“用户情景”、“迭代”、“情景点”,看看,哪里象是正常人说的话?

    面向对象,也令我想起一句广为流传的小资名言:“面朝大海,春暖花开”。当然你略微动动脑子,这实际是个病句。面朝大海的时候,你看到的往往是一片蓝色,如果你的身体这时候感觉到温暖,倒也正常,同一瞬间你的思维居然意识到是在春天,这就是不专心了----比较恐怖的是,你的眼睛居然在海面上发现了鲜花正在开放,唉,一段时间好象不说这句话都算是没素质,所以断章取义是不行的,这是海子的一首很平常的诗,这句话前面还有一句是“我有一所房子”,当然,是这房子面朝大海,是这房子春暖花开。

    然后第一句是:“从明天起,做一个幸福的人”,两个月之后,诗人卧轨自杀,成就了杯具般的凄美。我一直不太敢说:“从明天起”如何如何,也知道幸福的人不是你想做就能做的。必须给自己保证,做一个活人,哪怕是垃圾般生存,这个比幸福重要。

 

   我们可以回忆一下,所有教科书,都将“面向对象”概念和如何用面向对象的方式设计割裂开来,讲概念通常是一章的内容,特别精细的还分成多章,将封装、继承、多态拿出来逐一讲解。通过漫长的岁月之后,恐怕很多人还是不理解,为什么?黑板、讲解、瞌睡的三部曲而已讲解面向对象的概念,仿佛是入门的东西,讲解面向对象的思考方法,则是需求分析、设计模式等等看起来高端的范畴。

   其实,还是那句话,哪里有那么复杂?我国专业人士,基本上个个都是老中医,仿佛不磨你几十年你就能够做好事情,那就谈不上博大精深。

 

   我们就拿他们常见的场景,用我们的语言来剖析一下。假设我们要做一个模拟的生物世界游戏,有树、草、花、苹果树,有鸟、有鱼、也有狗狗和牛牛。

   那么我们看到了什么?就是这些“东西”,嗯,他们说的对象:树、草、鸟、鱼等等。

 

   他们说的继承是什么?这些东西是有分类的,分类是通过相同的特性、相同的行为来划定的。分类的原因是什么?不要重复。这里首先是生物类,哪些特性?生或者死、寿命。哪些行为,比如“生长”。然后动物和植物类,可以继承自生物类,而树可以继承自植物类,鱼可以继承自动物类。

    这样,共同的特性和行为,往往不需要每个类都要重复编写。看清了吗?实际上来源于一种树形分类的方式,目的是令相同的代码不要重复编写,是一种提高效率的手段。

 

    类对对象的区别:我们用一类东西和一群东西来表达,比如一种叫做鱼的动物,我们用class 鱼{}来定义,实际上是抽象所有鱼的特性和行为。游戏中要呈现三头鱼,两条鱼合作生下了很多鱼籽,然后又产生很多小鱼,那就是一群鱼,什么意思的:“创建了具体的鱼”,这就是所谓的实例化,常常有人鼻孔朝天的说:“你知道对象和类的区别吗?”,就象说:“你知道VC和C++的区别吗?”一样,其实是普通初中生都能够轻易理解的东西。

   再说说封装:如果我们着手写这个游戏,早期的方法,显然是写一大堆的函数,会有“鱼游动”、“狗跑动”,然后一大堆数组,描述存在多个各种类型的动物、植物。这时候程序员象上帝一样,每一个细节都由他的代码所控制----这意味着他独自面对复杂的世界,编程是不是很麻烦?现在,我们将这些东西找出来,对于鱼、狗狗,他们的特性和行为都看成是一类东西,也就是类。一个名字:狗狗;一堆特性、一堆行为,这样我们的代码,就将和狗狗相关的一切包裹在一起。程序员就能站在更高层面思考问题:比如他可以先不实现这些类,这个场景需要N只狗狗、两头牛牛,创建相应数量的实例就行了,他们的移动、生长、繁殖不需要在这个层面考虑。

所以封装首先是一种良好的代码组织方式,能够简化复杂性、有利于团队合作:比如游戏中鱼居然飞起来了,去找写鱼这个东西的程序员;狗狗竟然不会动了,去找写狗狗的程序员;牛居然只有3厘米高,嗯,去找博客园的牛牛们。

   至于多态,则是一种弥补方式,树形分类的思维,也就是划分层次的思维,虽然效能要高过线性思维,但也存在问题:比如动物有“移动”的行为,但鸟在飞、牛在爬、狗在跳,行为的方式不同。那么,我们只约定动物这“类”东西有个虚方法“移动”,让它从一个点到另一个点,而鸟可以重写这个需方法,可以既保持动物类在不同点之间的移动过程,又增加自己移动的姿态,方便界面上呈现出来。

 

    大体上就这么些内容,面对任何问题,找出里面所有的东西,归纳这些东西具备的共同特征和行为,在抽象的层面寻求继承、多态从而从整体上减少代码量的可能。这就是面向对象的思考方法和实际的编程方法。东西不一定是业务上的,比如“线程”也是一种东西,操作系统范畴的。

 

    那么针对导入日线文件这项功能,我们站在东西的角度,怎样做?

    首先,我们知道要做什么:用户从其它行情软件下载日线数据;用户选择这些日线数据;系统识别这些日线数据;系统将这些日线数据保存在自己的数据库中。

    这是主要的流程。

    另外:因为存在对这些日线数据进行预处理的工作,用户要求只能增加系统中最新交易日之后的数据,这就存在着当某日没有;因为数据量大,导入过程等待的时间比较长,而且界面僵死不能做其他事情,因此需要随时告知导入进度,同时使用后台线程导入。因为用户选择文件很可能有误操作选中了不是我们采纳格式的文件,因此需要有文件格式的合法性检查。因为用户对行情数据的利用有大量查询要求,因此要建立相应的索引以突出查询性能,但这与导入性能冲突,因此要做好权衡,并比较禁止索引--导入--重建索引和直接导入之间的性能差异。

 

    那么上面就是这项功能的主要描述,请注意我称它为“功能”,这是人话,不是“用例”、“用户场景”、“故事”。

   我们首先看看,这里面有哪些类型的东西?

    纯业务角度:用户、其他行情软件、行情文件、日线、数据库。其中没有对用户和其他行情软件的要求。

    非业务角度:线程。

    因此我们将用三个类来体现业务因素:日线类Quote,表达一只股票一天的日线,行情文件类QuoteFile,识别行情文件格式、转换成日线的集合,数据库类QuoteDB,对应数据库的日线表格。至于非业务对象,线程,则编程语言已经提供了足够的支持。

    其他如对话框、界面设计是另一个范畴的东西,当然,你看到的仍然是一系列非业务类型的类和对象。有时候我觉得又好气又好笑,不知怎么,那么多人觉得MFC是怎样的复杂、不花费几十年读它的源代码则不足以平民愤,那就是菜鸟、那就不能做500万行代码的程序。我真无法想象500万行的VC程序是干什么的,但是,站在所谓面向对象的立场,微软这个MFC不就是已经设计好的一串类吗,它们组合起来帮助你完成日常的界面呈现、互动工作。你一定要阅读其源代码,那就是不尊重微软封装的能力、质疑微软做的产品的可用性和简单性了。相对来说:你简直是在说MFC是一群菜鸟玩出来的,连最起码的封装都显得滥竽充数。在团队中,你会仔细阅读其他同事写好的类吗?对MFC,你也可以理解成其他同事写好的类,而且是文档非常丰富的、远超出我们那种应付方式的类库,还是克制一下探究细节的冲动吧。

 

   在你开始编程之前,这些东西大约需要一个小时甚至更短的时间就能弄清楚,忽略这个步骤往往会造成整编程过程的杂乱无章,站在东西的角度,也就是面向对象的思维方式之所以重要的原因。 这一节的主题是“谁说对象不是东西?”,起因是有牛认为多数程序员缺乏面向对象思维、算法和数据结构的知识,我不太认同。事实上,实际的编程过程中,抽象类甚至接口、继承这些出现的机率是非常小的,多数的时候大家接触最多的不过是封装特性。而算法和数据结构固然重要,但也仅仅是理解的问题,只有极少数的场景需要相应的基础---而这些东西,学院里的却未必够用,多数情形下我们的程序员接触的还是应用开发。

    所以我明确的反对将这三样东西提到过分的高度,生存逼迫你编程,兴趣才能让你成长。我们还在求生的过程中......

posted @ 2011-12-08 13:23 玄歌 阅读(1618) 评论(9) 编辑

2011年12月6日

    中国的文人,有个根深蒂固的传统:皓首穷经。

    从三百千千,到四书五经,到诸子百家,诗词歌赋,琴棋书画,每个学子往往要经历漫长的十几年甚至三十年。所谓十年寒窗无人问,一朝成名天下知,是所有文人的潜藏情节。所以,有老实人说出了老实话:百无一用是书生。我一直觉得他们仿佛在学东方不败大侠,先对自己狠心点,然后闭门苦练,最后终于明白了万物滋生的大道,乃至所向无敌。

    作为普通人,我们还是尽早动手为好。

 

    第一项任务很简单,精确点描述是这样的:创建一个应用程序,使用两个Ribbon按钮,在两个窗体中切换。

    是啊,用户做的第一件事情,就是找到功能,然后进入相应的界面,菜单、工具栏还有Vc++2010提供的Ribbon功能区方式,无论怎样,这都是首先要掌握的内容。

    站在程序员的角度,你最经常遇见的可能是哪些场景呢?界面、读写文件、读写数据库、设计业务类,就这些,做得专业点,号称程序员就顺理成章了。我觉得各种知识的讲解很多,但如何在实际工作中,做到像模象样,好象没有多少这样的资料。因此,这个系列讲解的虽然只是一项不太复杂的功能,但会尽量的讲透,涉及到界面、性能、异常、用户体验诸方面。

 

    因为是首次接触VC,那么我给自己定下的规矩很明确:学习阶段,绝不考虑重用性问题、绝不考虑扩展性问题、绝不考虑代码风格问题,尽量在第一时间将功能稳定的实现。在不熟悉语言的时候,你七七八八太多,那简直是在虐待自己的大脑。语言熟悉后这些东西你有精力再去玩,现在,还是老实点好。

     那么,我们首先面临的问题是使用传统的菜单、工具栏形式,还是使用Ribbon界面。这种选择对于我来说,不是什么问题,喜新厌旧据说是男人的本能,嗯,照照镜子,我发现里面是一张男人胡子拉碴的脸。然后呢?我遇到的第一个问题,是文档视图结构。

     这是一种延续了几十年的模式,换句话说,是一种古老的模式。大家看看记事本就知道了,处理一种后缀名为TXT的文件,打开、关闭、保存文件,将文件后缀在操作系统注册到记事本,简单的说,就是你创建的这种应用,将针对某种专用的格式,相关的一些处理工作,由文档视图结构预先的帮你做好,很贴心,是不是?

    所谓文档视图结构,马上动手,用项目向导创建一个简单的单文档、单视图结构的应用,可以看到VS帮我们创建的一些代码,嗯,四个类:一个应用程序类,是整个系统的入口,然后创建MainFrame实例、后者将管理文档类和视图类。文档类负责处理对这种格式的文档的各类操作,视图类负责呈现、编辑这种格式的文档。

    但是,两个问题:首先,我们现在已经很少敢于制定自己专有的文件格式,比如各类视频格式,都是几家恐怖的巨头在发布标准然后互相撕咬。一个可怜的程序员竟然做这种不合身份的事情,实在令人齿冷。其次,相对于专有格式而言,我们做桌面应用普遍还是以数据库应用居多。

    所以我的想法很简单,文档类基本不用,放个空壳在哪里。视图类呢?首先我想找寻类似Wpf的窗体设计器,视图能不能直接的“画界面”?很沮丧,不能,我只找到了对话框设计器。接下来,我们知道,若视图继承于CFormView,则可以使用对话框设计器来设计界面。 但这样我们需要做多视图项目,

    为了利用对话框的界面设计能力、同时保持整个程序的简单,浮现在我脑子里的,是一种古怪的想法:对话框有一种“子对话框”风格,我们在视图中直接打开这个对话框,则看起来是无缝集成的,我们就能自如的在两个对话框之间切换了。

    嗯,有熟悉的程序员马上要问了:不是有基于对话框的应用程序向导吗?两个问题:首先我们希望的是处理多个窗体,但不要弹出,这个是木有办法的;其次,更重要的是,这时候你不能在应用程序向导中选择Ribbon方式,也许有“在对话框应用中使用Ribbon“之类的深入研究,但我确实很懒惰,不太愿意在这类事情上消耗时间。

 

    接下来,我按照工作的步骤,简单的描述完成这项任务的过程。需要说明的是,这个系列的帖子是休闲贴,我不会一边做一边截图一边写,时间和精力不允许,所以很难做到step by step的方式,仅仅是列出步骤和基本的知识点,当然也包括初学者肯定会闹出的笑话。

 

1、创建项目:创建一个MFC应用,在配置向导中,注意勾上“功能区方式”,提供文档视图支持,单文档模式,其他无所谓。项目创建完毕后,运行一下。

2、简单的阅读一下生成的代码,次序是App类->MainFrame类->Document类->View类。不要深究,比如View中那堆很混乱的宏,只要从字面理解就行了:定义消息循环,不需要学究到一定要弄懂这堆宏究竟会转换成怎样的代码。读代码的方法,嘿嘿,这是秘籍:看看类结构,揣摩一下方法的名字,然后不要太关注每个函数内部的代码,会很快的。需要注意的问题,是我们经常见到的项目按文件夹组织的方式,这里没有,VC项目中叫做筛选器,仅仅将同一个目录下的文件分类,并不改变文件的物理位置。

3、我们将创建两个对话框,注意:style设为child,去掉确定和取消按钮,不要边框。仅仅放上一个静态文本字段,分别是“第一个”和“第二个”。

4、我们要为两个对话框创建类,这个不要傻乎乎的自己写代码:右键,为对话框生成类,就行了。为什么要有这个步骤?对话框以“资源”形式保存,仅仅保留了一个唯一的ID和控件类型、位置、大小等信息,我们毕竟要通过代码控制其工作,那么创建类绑定这个对话框就是必须要做的事情。

我不太喜欢用记事本写程序那一套,今后自然会理解:类向导自动的帮我们创建对应的对话框类,一般是继承自一个公用的基类,添加消息循环等代码片段,重写一些虚方法。

5、那么,我们在Ribbon设计器中,创建一个“面板”,然后放上两个按钮。

6、当然,需要为两个按钮创建“添加消息处理程序”,也是在Ribbon设计器中,右键,就能看到了。针对消息类型“Command"”和“Update_Command_UI”都要创建,前者是用户点击按钮后要做的事情,后者我们用来处理按钮是否可用的问题,当然,这两个方法添加到View类中。

7、我们现在有了四个消息处理函数,以Command为例,怎样在视图中显示一个对话框?

8、看看下面的代码:

void CSouGuView::OnButtonHome()
{

    if (CurrentDialog) //如果已经有对话框打开了,干掉它。
    {
        CurrentDialog->DestroyWindow();
        delete CurrentDialog;
    }

    CHomeDialog* dlg = new CHomeDialog(this);  //创建对话框类的实例
    CurrentDialog =dlg; //我们在View中定义的CDialogEx* CurrentDialog; 我们创建的对话框类都是从CDialogEx继承的,因此,这个指针用来表示当前显示的窗体。
    CurrentDialog->Create(dlg->IDD,this); //这里创建窗体
    CurrentDialog->ShowWindow(SW_SHOW);  //显示窗体
    CurrentDialog->CenterWindow(this);//将对话框在视图中居中,注意屏幕大小改变后,就不再居中,需要在OnSize事件中处理
    CurrentDialog->SetBackgroundColor(RGB(255,255,255));//将背景色改为白色,马虎应付一下

}

9、然后看看消息循环中的代码:

BEGIN_MESSAGE_MAP(CSouGuView, CView)
    ON_WM_CONTEXTMENU()
    ON_WM_RBUTTONUP()
    ON_COMMAND(ID_BUTTON_HOME, &CSouGuView::OnButtonHome)
    ON_WM_SIZE()
    ON_UPDATE_COMMAND_UI(ID_BUTTON_HOME, &CSouGuView::OnUpdateButtonHome) //注意这句,如果对应的对话框已经打开,则按钮不可用
    ON_COMMAND(ID_BUTTON_QUOTE, &CSouGuView::OnButtonQuote) //注意这一句:如果用户点击这个按钮,执行OnButtonQuote方法。
    ON_UPDATE_COMMAND_UI(ID_BUTTON_QUOTE, &CSouGuView::OnUpdateButtonQuote)
    ON_COMMAND(ID_BUTTON_CHART, &CSouGuView::OnButtonChart)
    ON_UPDATE_COMMAND_UI(ID_BUTTON_CHART, &CSouGuView::OnUpdateButtonChart)
END_MESSAGE_MAP()

10、当然,如果你打开了这个对话框,可以让这个按钮失效:

void CSouGuView::OnUpdateButtonHome(CCmdUI *pCmdUI)
{
    if (CurrentDialog)
    {
        pCmdUI->Enable(CurrentDialog->GetRuntimeClass()!=RUNTIME_CLASS(CHomeDialog));
    }
}

很显然,消息映射中ON_UPDATE_COMMAND_UI(ID_BUTTON_HOME, &CSouGuView::OnUpdateButtonHome),是对应这个的。

大致的意思:如果当前有对话框打开,那么:如果是CHomeDialog类的实例,这个按钮就不可用。已经打开了这个对话框,你还打开它干嘛?

11、另一个对话框也是一样,算起来我们创建了四个消息处理函数。代码就复制一下,运行,能够看到来回切换的效果就行。

12、存在的问题:

     首先,VC的对话框的布局问题,很原始。原始的意思,就是你需要自己处理。当你改变主窗体大小、或者在不同分辨率的机器上,对话框的所有控件总显示在视图的左上角,界面是很丑陋的。这是以后的问题,我最后的解决方案是:多数情况下窗体比较小,保持居中显示就行了;少数情况下,比如显示K线图的时候,需要自动适应主窗体的大小,使用一套叫做EasySize的宏解决问题。

    其次,这里虽然实现了对话框居中显示,但由于OnSize事件中没有处理,窗体大小改变的时候,对话框的位置将不再居中,这个此后再处理。

 

总结一下遇到的问题:

1、首先是所谓指针的概念:

    其实这真是幼儿园级别的问题,往下看,五分钟差不多能够弄清了:我们假设内存有1000个字节(不用说2G了,只说1k吧,好描述),从0开始给每个字节编号,最后一个是999。这个编号就是指针,很显然,指针实际上是整数,表示内存的编号,也就是内存地址。我们常见的,32位操作系统的电脑,不能利用4G内存的事情,大家可以算算,32位也就是4个字节的整数,最大值是什么。

    比如我们定义一个int 指针,怎么做?

    int *p;

    意思是我们现在有了一个int指针变量p

    然后p=0,什么意思?指向内存0的位置。

    你如果没有为指针分配空间,那么对指针的操作就是一种令人崩溃的游戏。

    虽然指到了那个地方,但那里并没有你分配的一个整型的4个字节的空间,而很可能是另一个变量的位置。

    所以第二步我们可以为其分配空间:

    p=new int;

    或者p=new int();

    这个时候,你设置断点看看,p的值已经变化了。怎么回事呢?系统将从空闲的内存中画出四个字节给你,并将这小片内存的地址:也就是第一个字节的编号传给p。系统同时会给这部分内存做上标记,继续分配空间的时候,不要再拿出来献宝,

     好吧,第三步:这片内存里的值是不可知的,为其赋值。

     *p=10;

     嗯,第四步,我想将p的值传给一个局部变量 int x;

     容易:x=*p;看看,x现在也是10了。

     注意x是局部变量,不是指针,那么背后发生了什么事情呢?p所指向的四个字节的内容,拷贝到x所占用的四个自己的内容,如此而已。

     那么第五步,我们反过来,令x=100,想让p接管x所占用的四个字节,怎么办?

     p=&x;

    这就行了,&表示x的地址,再看看p,他的值显然发生变化了,指向另一片位置了么。

    不过马上你面临第6步:

    原来p的那四个字节呢?没主了,谁都遗忘了,它还在那里,不再能够使用。内存泄漏?一个函数可能短暂时间执行几千次,这样不断失去控制、不断丢失的内存就可能膨胀起来,最终的结果大家很容易想象。内存泄漏是很多大侠不断用来表达自己功力深厚的东西,其实不复杂是不是?

    第7步,怎么避免呢?

    容易,请在改变p之前,先delete p;先不说执行析构方法之类,我们简单的理解:将p指向的内存空间还给系统,那么,虽然p指向其他地方的话,这块地方也能继续分配给其他变量。

    第8步,那么int类型的局部变量,是不是也要delete呢?不需要,堆的概念、栈的概念也不用多讲,一个函数结束的时候,所有局部变量都会自动的析构,不用我们操心。只有用手工方式分配内存的,比如new出来的,需要我们处理。我最初闹过这个笑话,自己在函数里创建一个对象,退出前自作聪明的运行其析构函数,程序运行时崩溃,因为析构方法执行了两次,第二次执行的时候那个对象已经不存在了。嗯,这种错误犯过两次,弄半天才找到原因,相当于在同一条沟沟里跌倒两次,所以我对自己的智商也委实不太自信。

    第九步:int毕竟是简单的,你创建一个类的实例,用指针指向它,一切行为没什么不同,两个区别:1、你创建的对象可能需要更多的空间;2、p->访问对象的成员,对象x则用“.”这种符号。

    需要一个月才弄清指针的概念吗?不需要,没有更多的知识了,指针的指针是什么?嗯,指向指针变量本身的指针而已;Void *是什么:指向一块不知类型的内存空间罢了。p++什么意思?指针越过其类型所占的字节,这里是加4。如果你刻意将这种概念弄得太精巧和复杂,要整出很多变态的用法,我要问问你为什么?你其实只需要这些知识就能轻松的做事,何必折磨自己的大脑。

    第十步,我是怎样弄清这些的?知道内存,知道内存地址,知道指针是存放内存地址的变量,这么三句话之后,上面的这些不是很自然的理解了吗?

    第十一步,有大牛说过,不解决内存泄漏的问题,C++无法做超大型的项目,怎么办?嗯,搜索一下“智能指针”,不过好象我们很少做超大型的项目吧?虽然这里有高手说起500万行以上代码的项目,但做那个的,我觉得和我们几乎是两个世界的人物,或者就是一帮精神病院的家伙。暂时就不要杞人忧天了,发现问题解决问题就是,仿佛一句流行的官话:“发现一起,查处一起”,当然,聪明人马上会想到:怎样才能发现?谁来监督你是否去想办法发现了?怎样查处?谁能知道是否真被查处了?查处了以后会不会马上就复出?中国语言的模糊性,注定了中国程序员的质地。

2、然后是头文件的问题:

    对于习惯C#命名空间的人来说,头文件真是很无聊的东西,太古老、太原始,Too simple,some times naive。

    我很快弄清了原因,为什么这个是需要的?.net 最终编译之后,保存了元数据,所以你使用一个dll,有反射机制可用。C++没这种东西。

    在头文件中声明,在源文件中实现。

    声明的意思是告诉编译器,我这里定义了一个类,编译器只需要知道,却不会将这些东西编译到obj或者dll中去。事实上,主要的作用,是编译器知道某个对象分配多少空间,相当于编译器使用的参数。

    源文件中include “xx.h”

    意思是将头文件全文复制到这里,然后一起交给编译器。

    在这里我闹的笑话很简单,就是void CSouGuView::OnButtonHome()

    看看这个符号“::”

    表示这个类的方法,头文件里要声明,源文件里要实现。

    如果没有这个呢?

    最初我本能的在源文件中写int xxxx;

    当然,不在任何函数内,用起来似乎也比较正常,但总是很奇怪,为什么这里改变了,在那里它也同样改变了。

   直到后来出现问题。

   甚至你这样自由的在源文件中定义不带“::”符号的函数

   似乎也可以用,但却很奇怪为什么里面的智能感知失效了,用类的成员会出现编译错误。

    噢,这种简单的问题是一周后才弄清楚的,不要笑话我,所有人都会碰到的,高手们不会讲解这种问题。   

3、代码的坏味道:

    极限编程、Scrum这类敏捷流派,耳熟能详的一句话是:“当你复制剪切的时候,就要嗅到代码的坏味道,啊,重构的时机来了。”

    看看,两个Command消息函数、两个update的消息函数,是不是都是复制的?为了重用,这里显然要重构一番是吧?对不起,我现在没工夫,就这么对付了。要知道自己的目的,时间花在主要目的上,对于重构也好、对于怎样将代码设计的更柔韧也好,我相信自己比多数人都会强一些----可是我现在在干什么?尽快实现功能,熟练掌握Vc++。重构不需要时间吗?需要的,但这个阶段不能花这种时间。这个小的应用程序,总数不超过15个窗体,几秒钟就能复制粘贴一番,估计到项目结束之后才有空去考虑这个问题。

    第二个坏味道,是View类必须知道每个对话框类的名字,当然,这也意味着有多少对话框就要include多少头文件。哇,设计模式发挥作用的地方到了,为每个类提供一个创建实例的方法,然后整个接口?创建一个对话框的基类?我说是吃饱了撑的,有闲的时候再做好不好?每个小时都很重要,你追求的是可扩展性,可是至少要等到你的产品做出来再说吧?或者今后就完全没有扩展的需要呢?或者今后接手的是个菜鸟呢?最优先考虑的肯定是如期完成项目,可扩展性服务的是程序员而不是用户,而大量使用各类设计模式,因为更抽象,所消耗的时间往往是简单实现的数倍以上,是一个工时和5个工时的区别。是否能够分清重点,是智商是否达到及格线的问题,不是技术能力问题。并不是某位高手说“我主要找写库之类的岗位”,那就更高人一等,虽然写库这种词听起来就有些别扭的味道。

    做类设计的时候,多一个类可能意味着10%以上的理解难度、开发成本的增长,所以我的目标往往首先是用最少的类实现,只在会影响到实现、或者通用性考虑不足会加大开发成本的情形下,才考虑那些稍稍抽象的东西。我当然也经历过设计模式狂热的短暂阶段,以这种实用主义、直触本质的学习方法,武器显然是不缺乏的。最明显的例子,大家可以看看通达信的股票行情软件,这样以产品吃饭的公司来说,居然存在针对不同证券公司的多种版本,对于开发团队的维护、升级来说,这绝对是一个灾难:不同的公司显然会提出一些各自特色的要求,有些可以拒绝,不能拒绝的往往是公司很关注的部分,这要做好业务分析、精心抽象,并通过通用化的设计,进行单一版本的升级来向所有客户提供同样的功能,是否使用由客户自己决定。这种多样版本并存的情形,源代码管理、开发人员是否永远在公司、自动升级服务器的部署,复杂度和工作量完全是几何级数的增长。至于应用项目,短周期的情形下,则要尽可能回避复杂化的苗头。软件开发首先是生意中的一个环节,在保证交付、稳定运行的前提下,开发和维护成本该怎样控制、如何保证如期交付、如何保证符合合同规定的质量要求,其实是团队要重点平衡拿捏的,这种观念必须贯穿于软件过程的始终。简单的说:你是在工作,不是在研究。

    所以按照前面说的,我理直气壮的忽略这些“代码的坏味道”,留待项目结束之后。

 

    再总结一下这短短几行代码,我们逐渐嗅到了些什么东西,当然,谈不上熟悉:

    1、消息循环:宏语句,不要探究细节,从字面上理解足够了。

    2、类向导:能够用工具就不要手工写了,相当于代码生成器,多用。

    3、头文件的问题:理解多数情况下,头文件仅仅是嵌入源代码的“声明部分”。

    4、调试、断点设置之类,这个没什么变化。

    5、指针:整数,记录某块内存的开始地址。

    6、文档视图机制:当作阑尾吧,留着。

    7、Ribbon界面的设计:操作问题罢了。

    8、对话框的设计、创建类、在视图中显示子对话框。

    9、this指针:和C#中一样,this指向是指向类的当前实例的指针,访问类的成员,不是我们习惯的“this.xxx”而是this->xxx,因为是指针么。创建对话框的方法中,this作为参数,意思是将目前的这个view对象的指针,作为参数传递进去。

    可能都不是特别的清晰,但不停编码的过程中,不知不觉的你的理解会一步步增强。慢慢来,最终也不会超过一个月的,我们今后会遇到std、stl、各类字符串、unicode问题、多线程问题、Directx Api,发现一起查处一起,这项功能做完,由任务的驱动会逼着我们逐一弄清所有这些语法细节,这样比你主动的去看书强吧?到时候象我这样写几篇博客总结一下,思路能够整理得更为清晰、众多有如牛毛或者过江之鲫的高手们如果肯指出理解上的问题的话,又能够更上层楼,何乐而不为?

 

略微解释几句:

    这个系列,由于是面向初学者的东西,主要是希望提供一个学习的路径图,因此内容相当的初级,也不会太过详细。这里还是希望高手们要么当作游戏之作,要么带点容忍异己的心态。当然,我说起高手,一般是当贬义词用的,这是习惯,很难改变。同时因为时间的关系,一篇帖子往往用不了半个小时,两分钟写个提纲,然后顺着往下胡侃,因此错漏之处必定也有很多,就内容挑毛病我是很欢迎的,也会尽量修改。至于对我的人品挑毛病的话,就看兴趣了:兴致高的时候我会争锋相对,练练嘴皮子,体验斗嘴的乐趣,对心态保持年青、养颜防老还是很有好处的。兴致不高的时候直接忽略,所以还请各位骂手体谅,不予还击并不是不尊重你的劳动,当然也不是看不到您鼻孔朝天一脸不屑的模样,而是此人正在沮丧中……非洲还有许多孩子没有校车呢。

    关于侯捷,嗯,我还是强调一下:这个人真的很无聊。他的粉丝众多,也掩盖不了这种无聊。你可以喜欢他“滴人”,我也可以不喜欢他的方法,我不能勉强你,也希望你不要逼着我和你一起做粉丝,既然都那么博大精深,我也期待你们成为星星本身,呃,通过追星这种方式?偷偷说一句:我唯一喜欢的是他的表达方式,这是个人好恶的问题,无关其他。

    继续预告:下一篇将将讲述怎样设计“导入日线对话框”的界面,如何获取对话框控件的值、处理相应的消息。

posted @ 2011-12-06 08:42 玄歌 阅读(2522) 评论(37) 编辑

2011年12月5日

    任何时候,我都希望做绝对有把握能够完成的事情。换句话说,是缺乏冒险精神。因此,对杨先生叫苦,很大程度上是一种讨价还价的手段。在同意做这个项目的前一天,我就做了完整的评估。风险主要体现在两个方面:1、语言不熟悉;2、算法具有一定复杂性;3、因为是海量的计算,而客户要求每位分析师都能在笔记本上使用,性能方面的问题会大幅增加工作量,甚至存在可行性问题。

    编程语言是初哥们很重视的问题,不过从内心来说,我从来没有将这个当作问题。很多事情,都有一些人类考虑不到的死角,熟视无睹,则眼光永远无法漂移到那边。 就比如编程语言,你该怎么理解? 简单的说,编程语言是我们命令计算机完成一系列工作的工具。假设你面对的计算机是一台高度智能的机器,具备听觉和视觉,你告诉他----卧倒,主机箱便在桌面上打个滚,然后那个显示器就会可怜巴巴的望着你,等待你的下一条命令。
直接用机器指令给电脑下令,是最为简单的----一串零和一的组合之后,电脑能够为你做一次计算工作。但这种工作枯燥无比、容易出错、而且工作量大得惊人。所以人类在不停的想办法,让事情轻松一点。
     最初,是汇编语言。我听到许多高人谈论“你要懂得一定的底层知识”,嗯,在他们脑子里,汇编几乎是最底层的东西了----其实不是的,最底层的是机器码。最早期的程序员,是真的使用机器码编写程序的,汇编语言已经是一个很大的进步了。此时,你面对的不再是一串串指令,而是寄存器、内存、变量等等。你通过类似Mov之类的命令,要求计算机完成某个步骤。 那么,怎么办呢?必定有一个翻译机制,将我们的汇编语言转换成机器码,这就是编程语言的由来。这个翻译机制,实际上也是一套软件,它负责将你用汇编编写的程序,然后变成机器码。 所谓编译型语言和解释型语言的差别,很大程度上,是指:将你写的程序,一次性转换成机器码,还是运行这个程序的时候,动态的转换成机器码。

     而C语言,则是汇编进一步的简化。是啊,我们下命令的过程,其实仅仅是表达我们的诉求,没必要要求你控制每一个设备啊----你让计算机做一些组合工作就行了,所谓高级语言由此而来,主要的原因是:希望我们下命令的过程,变成面向问题的,而不是面向电脑内部的各种元器件、面向动作的。就象你对一个孩子说:抬手、解下腰带、蹲下,嗯,很麻烦。C语言则变成了一句话:“上厕所”。换一个角度来说:此时C语言已经包装了很多东西,你面对机器变成了面对C语言提供的功能。于是定义变量、表达式、计算、编译、分支、循环等等语言概念,逐渐稳定下来。

    C++之类,是面向对象编程语言,这实际上是简化思考的努力。而java、C#等,主要的差异是屏蔽平台差异、实现垃圾回收机制,进一步为程序员减负。

 

    计算机要完成一个复杂的业务过程,显然不可能要求程序员时时刻刻下命令,而是普通人使用你的程序----这个比较重要,意味着某种通用性。那么你的程序对一些重要的流程,仍然会安排一些操作方式,“最终用户”由此产生:他们运行你的程序,按照你约定的操作方式,命令计算机完成他要求的工作。所以到高级语言层面,我并不太在意语言的差异。
     至于其他两个问题,多少也有腹案。
    然后,接下来,开始简单的规划过程,列出所有的功能清单、确定要做的第一项 功能并细分任务。
    我的第一个迭代,只包括一项功能,就是“导入日线行情”。
    那么这个系列一直到最后,我也只描述这项功能实现的过程,基于一项项任务,逐步描述我要做什么、怎样着手去做、怎么思考、遇到问题怎么解决。大体上,由于源码服务器完整的保留了每一次代码的变更,这个过程也很容易复述。
    仅仅希望这一个月的学习过程,对初学者有帮助。这里的方法,事实上是学习编程语言比较通用的方法:若去学习java或者object c的开发,办法其实也差不多。甚至我觉得仅仅使用这种导入日线的例子来做,就能够覆盖常见的编程任务了。至于高手们,大可以一笑而过-----我一向非常鄙视中国的编程高手,因为这些家伙虽然牛哄哄的,但作为一个整体,他们倒真的没有做成什么象样的事情,感觉和中国足球队大有一拼,整个一群浪费粮食的群体。相对来说,我比较喜欢象我一样分布广泛的低手,虽然很差,但绝对不会乱现眼,能做什么就做什么绝不挑挑拣拣,从来不敢告诉外人自己是是踢足球,噢,是写程序的。

    下一篇的题目大体是“确定界面开发模式”,特此预告。

posted @ 2011-12-05 15:16 玄歌 阅读(2756) 评论(36) 编辑

2011年12月1日

摘要: 一个月学会VC++2010 1.送上门的银子 那时,我开了一间小型软件公司,包括我在内,技术人员只有5个人。全部的开发力量,也就是这五个人,在为一家企业做项目,工期还剩下2个月。整个小团队都觉得时间很紧张,但还没有到无法履行合同的程度。 然后,一个阳光不太明媚的日子,有客人过来拜访。 嗯,我的经理办公室装修的不错,老板桌、客人用的沙发、书柜、茶几看起来很有档次的样子。这当然不是我的功劳,是一位很富有的朋友,在公司成立的时候亲自挑选的。公司成立的时候找这家伙借了不少钱,于是理所当然的,他就拥有了决策能力。这种风格我觉得很暴发户的样子,不过坐在里面慢慢也习惯了,甚至偷偷的有些喜欢。事实上,这种..阅读全文

posted @ 2011-12-01 17:23 玄歌 阅读(2776) 评论(22) 编辑

2011年11月29日

摘要: 我个人对C++毫无概念,但在实际做项目的时候,仅仅用了一个月的时间,在完成当月工作的同时,就能够没有障碍进行相当复杂的应用开发。这个过程耗时之短或许不可思议,但采纳同样的方法,相信所有程序员都能够做到。这种方法说白了,就是边学边干。对,就是很多培训机构强调的,在实际项目中学习。不过,略有不同的,是我更加强调以下几点: 1、绝不做成学习项目:即使是最简单的功能,要做成实际产品级别,这意味着几乎成倍...阅读全文

posted @ 2011-11-29 10:56 玄歌 阅读(11766) 评论(228) 编辑

2011年10月28日

摘要: 更新日线,是一项我们经常遇到的数据导入功能。 这项功能的说明文字,我是这么写的: 用户在初次使用本系统的时候,需要安装沪深两市从开市到本年度的所有历史数据,这些数据可以从某证券行情软件获得,是遵循一定格式的二进制文件。此后,每天两市交易结束之后,用户必须更新当天的日线行情,当然,用户也可能在几天后一次性更新最近几天的日线行情。用户更新日线,才能建立完整准确的数据基础,我们所有的分析工作都基于...阅读全文

posted @ 2011-10-28 10:52 玄歌 阅读(1416) 评论(2) 编辑

2011年10月27日

摘要: 一间私募基金,希望开发软件实现他们自己的一种分析思路,这是一个小项目,我们隐去他们的分析思路,仅就行情更新和显示K线图这部分内容作为具体的例子,演示需求分析、阶段划分、任务划分、问题和Bug处理、源代码版本管理的全过程。这个项目由我个人承担全部工作,三个月的业余时间完成,本系列描述的是前两个月的工作内容。 我们首先需要确定项目的目标。简单的表述为:查看股票的K线图。这意味着我们需要每天更新当天的股市行情、除权除息资料,同时能够按照他们习惯的方式绘制K线图。 第二步则是列出功能清单: 所谓需求,是围绕着项目目标来定的。 既然要分析股票的走势,那么股票每天交易的行情数据,显然要保存,所以有“更新.阅读全文

posted @ 2011-10-27 10:17 玄歌 阅读(1322) 评论(6) 编辑

2011年10月19日

摘要: 第一步当然是需求的采集,怎么做? 先和用户交流,弄清楚项目的目标,这个通常是几句话,用户的语言,但你必须理解,并且文字记录下来。然后我们需要做第二件事情,即软件需要提供哪些功能,来实现项目目标。当然,这和用户的作业流程、业务规则、具体岗位是有关系的,这里的产品是一份功能清单。第三个则是功能清单的说明,通常也就是一段话,使用用户的语言。 一般没有必要记录非常详细的业务规则,在交流过程中这些通常会...阅读全文

posted @ 2011-10-19 16:48 玄歌 阅读(1672) 评论(2) 编辑