已阅读5页,还剩25页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
编程经验谈以下主要由softit提出,StoneLee整理第一章 程序结构性设计第一节 层次分明,层次即不能太多,也不能太少一、 层次太少的范例1、 正例比如我们的棋牌游戏客户端,适合分两个主要类来处理。一个是逻辑和数据类,一个是界面类。这类似Microsoft的Cview/Cdocument结构。这样的好处是:l 层次清洗,易于阅读和理解很清楚,如果我想了解数据结构和算法的话,主要看“逻辑和数据类”。l 支持未来界面的可变性支持未来界面的可变性。未来界面发生变化,则主要改动界面类即可。而且这两个类的关系主要应该是单向的,即Cview知道Cdocument,并根据Cdocument的情况来进行做图。也可能存在双向性,即Cdocument的数据发生改变后,要通知Cview发生改变。在MFC中,Cdocument-UpdateAllViews()这个方法实现这个功能。但这样,Cdocument里必须放Cview的指针,而且必须在Cdocument里有初始化的函数,例如Cdocument-AddView()。这些都加重了程序的复杂性,所以,尽量采用单向联系,能简化程序尽量简化。注意:和界面有关的数据和逻辑应该放在界面类,比如:牌的间距等数据。2、 反例我个人在编写很多游戏的客户端时,将所有逻辑和数据都放在GameView类里,使GameView非常庞大,有几千行代码。这样,阅读和维护都有困难。特别是第三人维护。二、 层次太多层次太多的错误比比皆是。很多程序员以为划分很多类就很牛比,就显示自己对对象学和C+的精通和高水平。先不要说其他人,说说自己吧。我在写第一个游戏“升级”时,犯下了教条主义错误。当时为了表达一个双张拖拉机,就做了如下的类:首先是单张牌类:CCard,表示单张牌,方法有,获得此牌的内部值、花色、权值,比较单张大小然后做了双张对牌类CdblCard类,这个类继承了CCard类,方法主要是继承的函数然后做了双张拖拉机类CdblTractorCard类,这个类继承了CCard类,同时内部数据有CdblCard类的内部变量。(比如可以用一个开始CdblCard类对象+一个长度数据nLen表达双张拖拉机)。后来再加上三张的类,加上逻辑类,加上界面类,整个程序非常复杂。经常在引用一个数据时,要调用其继承的数据或方法,经常做类型转换工作,这个工程像个大蜘蛛。后来,在新的升级中,我只用了一个类就就将所有这些类解决了。对于单张牌,我就用Byte数据表达,对于CdblCard,也是一个Byte数据就可以了,只要函数调用时,知道其代表双张对即可。我觉得相对于层次太少,层次太多是我们主要犯的错误,特别是那些读了很多教科书,但写程序很少或者是写程序很多但没有吃过亏或则是吃了亏还朦胧不懂的人。第二节 类的定义要简捷,不要做过多的工作这和下面一节有共性。这样的类一般有如下特点:n 对其他类的疯狂引用n 代码超过千行n 什么都能做第三节 类的互相关系:关联要尽量简单最好是单向的。我个人不喜欢双向关联。这样意味着你阅读一个类时,必须理解另外一个类。单身汉是快乐的,夫妻就要麻烦很多。未来的工程文档里,主要应该说明两点:n 类的关联关系n 对象的生成和销毁大家有空可以阅读一下Rose,这将是未来我们文档的一个重要规范。第二章 程序健壮性第一节 不要相信传入的参数,即使调用函数是自己编的模块接受外部参数时,做更多的检查。原则上,不论外部如何操控,一个模块自身必须严格安全的,如对外部对模块调用的顺序和参数都不要有默认的假设。最多的问题来自于空指针的使用。例如下面的例子相当普遍:void Reverse(char* str)int nLen=0;char *pTemp = str;while (*pTemp+ != 0)nLen+;/ 下面省略.还记忆得这个例子吗,是考试的一道题目,这道题功能很多人实现了,但有很多毛病,为什么这样说?原因很简单:你怎么知道str不是NULL?所以正确答案如下:void Reverse(char* str)if (str = NULL) return;int nLen=0;char *pTemp = str;while (*pTemp+ != 0)nLen+;/ 下面省略.如果有的程序员追求效率,认为str不可能上null,那么,代码应该如下书写:void Reverse(char* str)assert(str);/ MFC下,可以是ASSERT(str);int nLen=0;char *pTemp = str;while (*pTemp+ != 0)nLen+;/ 下面省略.这样在debug下编译,如果str是NULL,就可以使程序立刻停止下来,检测到错误;如果是release,则没有任何多余代码。这对于我们检测到错误,是非常有帮助的。记住一点:不要相信任何传入参数,包括自己编写的代码。第二节 变量初始化下面也是我们常犯的一个错误:void Sample()char *szTemp;./ 很多代码lstrcpyn(szTemp, “abc”, 3);/ 此句出错上面代码,当出错时,我们很难调试,因为szTemp里面的值并不是0x000000,而是乱七八糟的值,有经验的人容易判断出是szTemp未初始化,而如果中间的代码过多,我想谁也不曾想到是szTemp未初始化导致。所以,正确的写法如下:void Sample()char *szTemp = NULL;./ 很多代码lstrcpyn(szTemp, “abc”, 3);/ 此句出错这样,我们就很容易找到错误了。有些人认为,自己很牛,完全能控制主这些变量,也许几个变量、几十行代码还控制得住,但多了,肯定不行。还有人认为这是浪费效率,可这个效率值得浪费,因为它带来的程序健壮性会远远超过多几个汇编指令的开销(其实我们代码里浪费的指令的地方多啦,难道还在这么一个小地方讲学究!)唯一可以例外的是: for(;)循环中经常使用的i,j,k等控制变量,因为它们肯定在for中做初始化。同时,对于一些自定义结构,我们最好也养成初始化的好习惯,常见写法如下:typedef struct tagSample int iMax;BOOL bFound;char *szName;Sample() memset(this, 0, sizeof(Sample);int GetMax(); Sample;这里构造函数里的memset()函数,非常有用。它将所有值都初始化,避免了上面可能出现的问题,是个很好的代码范例。也可使用:ZeroMemory()来替换memset,只不过一个是Win32,一个C Runtime Library。第三节 关于缓冲越界一、 用:lstrcpyn()替换strcpystrcpy不是很安全(因为没有边界检测),应该尽可能用lstrcpyn代替strcpy。可以写成:lstrcpyn(),也可简单写成lstrcpyn()。二、 Debug下无问题,Release就有问题Debug下程序好好的,到Release下,就出现问题,一般情况下,这是数据越界导致。为什么会这样?因为Debug下分配内存和Release下分配内存是不一样的。比如:new char(10),Release下是十个字节,但Debug下是更多的字节,这是因为MFC为了调试程序,跟踪内存泄露,所采用的特殊做法。三、 开设较大内存的变量我们在设置变量时,最好不要刚刚好,可以稍微多分配一些,特别是针对那些稳定性要求很高的应用,比如服务器系统。比如,我们一个变量是char m_szMsg10。为了提高程序稳定性,我们可以提高其大小,如char m_szMsg32。有的古板的C程序员会加了,说:这样浪费内存。我们用多少字节就要多少字节。请大家记住:现代计算机系统,内存已经很大了,已不是DOS下的640K,而是以G为单位。请忘掉Bill Gates说的那就可笑的千古名言:640K is enough for PC。另外,用了char m_szMsg32和char m_szMsg10,可能更快,至少效率是一样的。为什么?因为计算机是二进制的,他识别10不如识别32来得快,所以在内部地址分配时,实际上操作系统分配的内存都是16或32的倍数。四、 一些错我范例在数据库引擎中,有如下代码if (strlen(szDataSource)+strlen(szDatabaseName)+strlen(szLogin)+strlen(szPassword)=MAX_DATABASE_CONNECTION_STRING_LEN) assert(0);/ 下面设置连接串strcat(m_szDatabaseConnectString,Provider=SQLOLEDB;Data Source=);strcat(m_szDatabaseConnectString,szDataSource);strcat(m_szDatabaseConnectString,;Initial Catalog=);strcat(m_szDatabaseConnectString,szDatabaseName);strcat(m_szDatabaseConnectString,;User ID=);strcat(m_szDatabaseConnectString,szLogin);strcat(m_szDatabaseConnectString,;Password=);strcat(m_szDatabaseConnectString,szPassword);其中szDatabaseConnectString的定义是char m_szDatabaseConnectStringMAX_DATABASE_CONNECTION_STRING_LEN;这段代码是有风险的。应该改为:char m_szDatabaseConnectStringMAX_DATABASE_CONNECTION_STRING_LEN+64;大家可以想想为什么?第四节 关于调试调试有多种方法,重要的是捕捉错误和将错误环境输出。关于捕捉错误,有多种方法,一种是用诸如:ASSERT、VERIFY、assert将程序停住,一种是使用try() catch()或简单的函数返回值来定位错误。这个只能分场合。比如,我们经常使用的CLimitedArray,它的定义范围就是一个静态定长数组,只要其超过长度,在DEBUG下,就应该停下来。便于诊断。当然,也可以返回值,由上层判断进行处理。但这样就复杂化了,将处理工作抛给了调用者。所以简单处理之,还是在CLimitedArray内部,使用ASSERT或VERIFY宏。但另外一些程序,特别是一些普遍使用的接口函数,如通信层底层,则应该考虑尽量不要使用ASSERT等工具将程序停下来,一是这样,程序依赖MFC(虽然可以用assert(),但不如ASSERT方便),二是没有必要停下来,应该交给上层决定如何处理错误。对于不需要停下来的错误,是用try、catch,还是使用函数返回值来定义错误,则完全取决于程序的编写。相对而言,函数返回值较为简单,上层也易控制,程序较简单,出错概率小,应该多采用;而多余多错误返回,程序流程长且复杂的,应该考虑用try、catch。总而言之,使用何种错误调试方式,完全取决于编写工程的需求。但有以下几个原则:n 能使用ASSERT的,尽量使用,因为上层调用不需要处理相关工作,简化上层工作;n 能使用函数返回的,尽量使用,因为上层调用处理时简单易用举个例子,在服务器框架里,有这么一行话:HRESULT STDMETHODCALLTYPE IGameLogicalImpl:SetServerSite(IServerSite * pGameSite)if (pGameSite=NULL) return E_INVALIDARG;。因为,服务器逻辑对象IGameLogicalImpl(实际应该是CGameLogic),如果没有IserverSite对象,就根本无法运行,所以,可以直接改写为ASSERT(pGameSite);这样代码简捷得多,DEBUG调试时,完全可以捕捉到这个错误,而且Release下,我们已假设肯定不会出现空指针的问题。(换句话而言,出了空指针,我们后面什么事情都做不了,就应该停下来,没有其他更好的容错办法了)第三章 程序可读性第一节 可读性第一,效率第二第二节 保持注释与代码完全一致第三节 每个源程序文件,都有文件头说明,说明规格见规范第四节 有二义性的函数、重要函数特别是逻辑复杂的函数需要有函数说明第五节 处理过程的每个阶段都有相关注释说明第六节 在典型算法前都有注释第七节 主要变量(结构、联合、类或对象)定义或引用时,注释能反映其含义第八节 常量定义(DEFINE)有相应说明第九节 利用缩进来显示程序的逻辑结构,缩进量一致并以Tab键为单位,定义Tab为4个字节第十节 循环、分支层次不要超过五层第十一节 注释可以与语句在同一行,也可以在上行第十二节 空行和空白字符也是一种特殊注释,可以增加可读性第十三节 一目了然的语句不加注释第十四节 注释的作用范围可以为:定义、引用、条件分支以及一段代码第十五节 注释行数(不包括程序头和函数头说明部份)应占总行数的 1/5 到 1/3第十六节 while和for比如,在我们的考试的第二题中有个是这样回答的:LIST *pL1;BOOL bResult = FALSE;if (NULL = pList)return FALSE;for (pL1=pList-next; pL1; )这种答法解决了问题,但这里更应该用whileLIST *pL1;BOOL bResult = FALSE;if (NULL = pList)return FALSE;pL1 = pList-nextwhile (pL1).pL1 = pList-next;第十七节 常数考虑用宏我在一些程序员编程中发现,他们喜欢用常数。比如:小沈在编写服务器管理器时,各个命令码和定义值全部写成实际数字,而且通篇都是,后来小余接手时,累得吐血。为什么不用宏?用宏的好处:1) 将数字变成了英文,字面就能体现例如:25还是COMMAND_LOGIN,哪个更好懂2) 方便未来维护如果未来修改,只需要修改一处,全局有效第十八节 宏在什么地方,.h or .cpp宏是C+里经常用的一个东西。大家可以读一下侯捷的深入浅出MFC,对宏的理解可以加深。但宏放在哪里好,一般我们将宏放在文件的开头。如果此宏只在.cpp里使用,则请将此宏放在.cpp里,因为这样的好处是:宏定义不冲突,特别是你引用其他人的宏时。第十九节 在VC工程里使用Folder我们经常在VC工程里放了几十个类和结构。(大家读一下老框架就有体会了)这样,我们阅读起来就非常费劲。所以,应该将这些类组织一下。方法,就是使用VC工程的Folder。方法:在ClassView里的TreeView上,可以点鼠标右键,然后选择New Folder菜单。这样,我们可以使整个工程特别清晰。至少,我们要将引用其他人的类和我们自己的类分离。小提示:甚至一个类的内部都可以用Folder进行组织。第四章 编码规范第一节 变量命名一、 前言命名必须具有一定的实际意义,形式为xAbcFgh,x由变量类型确定,Abc、Fgh表示连续意义字符串,如果连续意义字符串仅两个,可都大写.如OK变量名可以取得充分长,以明确变量的含义,如m_bIsMsgRecved二、 例程表类型范例特别注释BOOL类型bEnable;int类型nCmdShow; iCmd;n一般表示值为正,表示统计i一般表示有符号数,可负值但不强制这个要求LONG类型lParam;UINT类型uNotify;DWORD类型dwStart;PSTR,char*类型szTip;LPSTR类型lpCmdLine; szCmdLine;在WIN32下,不存在long指针概念所以,如果Win32环境,l可不考虑LPTSTR类型lpszClassName;在WIN32下,不存在long指针概念所以,如果Win32环境,l可不考虑LPVOID类型lpReserved同上WPARAM类型wParamLPARAM类型lParamHWND类型hDlgHDC类型hDCHANDLE类型hInstancefloatFTmpDWORDdwDataWORDwDataBYTEcbData; bytDataString,AnsiStringstrMsgm_类成员变量 m_nVal, m_bFlagg_全局变量g_nMsg, g_bFlag局部变量可采用如下几个通用变量:nTemp,nResult,i,j(一般用于循环变量)类名以C开头CDialog数据组的成员的个数最好为4的整数倍如char szName32,szTemp128三、 一些糟糕用法的范例第二节 常量命名和宏定义 常量和宏定义必须具有一定的实际意义;常量和宏定义在#include和函数定义之间;常量和宏定义必须全部以大写字母来撰写,中间可根据意义的连续性用下划线连接,每一条定义的右侧必须有一简单的注释,说明其作用;资源名字定义格式:资源类型范例菜单IDM_XX或者CM_XX位图:IDB_XX对话框:IDD_XX字符串IDS_XXDLGINITDIALOG_XXICONIDR_XX第三节 函数命名函数原型说明包括引用外来函数及内部函数,外部引用必须在右侧注明函数来源: 模块名及文件名, 如是内部函数,只要注释其定义文件名第一个字母必须使用大写字母,要求用大小写字母组合规范函数命名,必要时可用下划线间隔,示例如下:void UpdateDB_Tfgd (TRACK_NAME); /Module Name :r01/sdw.cvoid PrintTrackData (TRACK_NAME); /Module Name :r04/tern.cvoid ImportantPoint (void); /Module Name :r01/sdw.cvoid ShowChar (int , int , chtype); /Local Modulevoid ScrollUp_V (int , int); /Local Module实际工作中,为了简化,一般不标注函数的模块名及文件名。第四节 结构体命名结构体类型命名必须全部用大写字母,原则上前面以下划线开始;结构体变量命名必须用大小写字母组合,第一个字母必须使用大写字母,必要时可用下划线间隔。对于私有数据区,必须注明其所属的进程。全局数据定义只需注意其用途。示例如下: struct DBS_DATABASE charszProductName20; charszAuthor20; charszReleaseDate16; charszVersion12; unsigned longuMaxTables; unsigned longuUsedTables;相比而言,类的名字要求第一个字母为C,且不能全部为大写字母,以与结构体区别第五节 控件的命名用小写前缀表示类别范例表:控件类别小写前缀对话框dlg按钮bn; btncombo,下拉式列表框cmb文本输入框txtlabal,标签labimage,图象ImgpicturepicGrid,网格Grd滚动条scr列表框lst第六节 注释一、 总体说明原则上注释要求使用中文;文件开始注释内容包括:公司名称、版权、作者名称、时间、模块用途、背景介绍等,复杂的算法需要加上流程说明;函数注释包括:输入、输出、函数描述、流程处理、全局变量、调用样例等,复杂的函数需要加上变量用途说明;程序中注释包括:修改时间和作者、方便理解的注释等;二、 文件开头的注释模板/* 文件名:/ 可省略* Copyright (c) 1998-1999 *公司技术开发部* 创建人:* 日 期:* 修改人:* 日 期:* 描 述:* 版 本:/ 可省略* 修改记录:*/三、 函数开头的注释模板/* 函数名:* 功能描述:* 输 入: a,b,c* a-* b-* c-* 输 出: x-* x 为 1, 表示.* x 为 0, 表示.* 全局变量:* 调用模块:* 作 者:/ 可省略* 日 期:/ 可省略* 修改记录:* 版本/ 可省略*/四、 程序中的注释模板/*-*/* 注释内容 */*-*/五、 程序l 程序编码力求简洁,结构清晰,避免太多的分支结构及太过于技巧性的程序可读性第一,程序性能第二l 编写程序时,亦必须想好测试的方法,换句话说,”单元测试” 的测试方案应在程序编写时一并拟好。l 注释一定要与程序一致。l 版本封存以后的修改一定要将老语句用/* */ 封闭,不能自行删除或修改,并要在文件及函数的修改记录中加以记录。l 程序中每个block 的开头 ” 及 ” 必须对齐,嵌套的block 每进一套,缩进一个tab,TAB 为4个空格,block类型包括if、for、while、do等关键字引出的。l 对于比较大的函数,每个block 和特殊的函数调用,都必须注明其功能,举例如下count.divisor = 1193280 / freq; / compute the proper countOutByte(unsigned short)67, (unsigned char)182); / tell 8253 that acount is comingOutByte(unsigned short)66, count. c0); / send low-order byteOutByte(unsigned short)66, count. c1); / send high-order byte第七节 文档每增加一个新的类,即在类的头文件里定义类的注释(关于注释参见6.7节);同时将这类的注释写入开发文档中第八节 VC工程规范一、 标准要求请记住下面这两个原则:l 请不要改变VC工程的缺省编译环境。l 如果有特殊要求,请在源代码中显式地声明这一点。二、 教训曾经发生过两个范例说明了这个教训:1、 Struct Alignment早期,我们在编写棋牌游戏时,为了简单,曾经约定,所有的工程都将alinment改为1Byte。早期的数据库引擎就是这样的。后来,小牟在接收数据库引擎后,总是调用不成功,才发现,原来,其客户程序,调用引擎的dll时,参数传递对alignment的规范不一致,导致这个问题。所以,未来全部采用缺省的alignment,但在Socket传输中,我们全部采用1byte struct alignment方式(因为这样传输量最小),这时,应该在结构前显式的说明。具体可参看简单接口实现规范.doc。2、 _stdcall和_cdecl在沈安民编写通用管理器程序时,其工程的代码全部采用_stdcall。但在引用高锋的ITrace接口时,由于高的ITrace接口是基于_cdecl时,所以发生错误。因此,有必要要求,VC工程设置必须保证是缺省的,即_cdecl。如果你觉得有必要使用其他方式,如_stdcall,请显式地说明这一点。例如:在COM中,所有的接口函数都要求显式地声明为_stdcall。第五章 规范化工程管理要保证自己的工程,随时可以被其他人GetLastestedVersion & Built Ok。一般存在以下问题:第一节 工程依赖自己的VC工作环境典型范例一、 老的框架的目录搜索自己的VC工作环境里,在Option里,加入一些头文件的搜索路径。必须依赖自己的头文件,如:ClientSite.h,每个人拿过其他人的工程,都要做一些配置,如果在两个都这样的工程里互相切换,我想程序员肯定要骂娘。二、 Struct member alignment: 1Byte这个工程特殊定义,也是导致我们出问题的一个方面,当时是为了通信的见解,现在来看,是个隐患。特别是未来,我们依赖简单接口模式,这个问题就更突出了。小牟在刚开始编写数据库引擎时,调用李彤的老的数据库引擎,就出现了这个问题,整个数据结构全乱掉了。现在的解决办法,请参考简单接口实现规范.doc或看下面附注(摘自简单接口实现规范.doc):在Microsoft的VC编译器中,为了优化性能,会自动对代码中所定义的数据结构进行字节对齐。假如VC使用4 Byte 字节对齐,如果你的结构只定义 1 byte,它会自动补齐3 byte,使之4 byte 对齐。如果在使用一个接口时,要互相传递结构,而接口的实现与接口使用所以的VC 字节对齐设置不一样,就会发生末知的后果。最好的方法就是在接口定义的头文件中,在定义结构的地方加上结构对齐的前置指令#pragma,这样不论在那里使用都不会产生问题,而这正是我们所需要的,例子如下,这是找朋友中的一段源代码。#pragma pack(push)/保存现在的字节对齐设置#pragma pack(1)/设置1byte 字节对齐/存储用户分数typedef struct tagFriendScorelong lScores;/ 分数long lWins;/ 所赢局数long lLoses;/ 所输局数long lDraws;/ 所平局数long lFlees;/ 逃跑局数FriendScore;#pragma pack(pop)/结构定义完,恢复原来的设置第二节 只给自己编写的文件,其他环境未提供包括:其他接口或库的头文件、Lib、dll。这样,别人在编译时,需要找这些文件,有时还可能找不到或历史版本销毁。这样,都给包括自己和其他人的工作带来不便。所以,一次性将环境建好。第三节 输出文件或引入文件使用相对路径在工程里放入绝对路径也不好。比如:我们输出文件,为方便调试,写成:d:iGameTestGame20.exe但其他人可能没有这个目录。未来,我们将制定标准的开发的目录结构,这样便于大家统一和协调。但现在开始,就应该养成相对目录的习惯。第四节 工程目录要求一、 服务的目录结构1、 服务工程的目录结构假设服务的工作目录是:C:FooService,下面用¥表达。目录安排如下:l 工作目录的根目录:¥下面放Foo.dsw,还有相关文档还包括Foo.dsp,IFoo.h、IFoo.cpp、Foo.h、Foo.cppl 输出:任意方式,可以是子目录output或bin,也可以简单地放在VC缺省使用的Debug和Release目录下。输出文件为:foo.dll或Foo.lib(也包括FooD.dll、FooD.lib)注意:需要更改dsp中,使输出到output目录,缺省输出到debug和releasse目录,同时注意使用相对路径(这样其他人使用Copy一下即可)2、 如果Foo工程还需要引用其他接口或dll将这些引用的接口和相关的lib或dll直接copy到服务的工作目录下。记住以下原则:在Foo工程的工作目录¥下,有所有需要的文件,包括自己的.h、.cpp文件,也包括引用的.h、.lib、.dll等文件。当然,系统的头文件,如Windows.h、kernal.lib等不需要包括在内。这个由VC环境解决。建议:请通过SourceSafe拷贝其他人的.h、.lib文件,这样,当其他人更新了文件时,只需要通过SourceSafe的Get Last Version获得最新文件,然后,再人工拷贝这些相关文件到自己的工作目录。二、 客户的目录结构同上。仍是下面这个原则:l 在自己的工作目录¥下,有所有需要的文件,包括自己的.h、.cpp文件,也包括引用的.h、.lib、.dll等文件。l 请通过SourceSafe拷贝其他人的.h、.lib文件,这样,当其他人更新了文件时,只需要通过SourceSafe的Get Last Version获得最新文件,然后再拷贝这些文件到自己的工作目录下。三、 多工程的目录安排1、 多工程间无强烈关联,但有全局共享文件比如,程序员FlyFox开发两个游戏客户端,分别是:升级(Upgrade)和斗地主两副(SiguoClient)则我们的目录结构如下图所示意:如上图,四国和斗地主两副,我们认为是相对独立的。这样,我们将两个工程放到独立的目录下。然后,在每个工程目录下,都有全部的文件,包括“include”、“dll”、“lib”等。这里,有人可以提出这样一个问题,对于Siguo和Upgrade,其类似common.h的文件(还包括其他由公司框架负责人编写的.h、cpp、.lib、dll文件)内容不是完全一样吗?为什么不合起来?我们觉得,还是分开的好,理由如下:比如,我们有一个lib,名为聊天的功能模块Chat.lib,我们对此lib进行了升级。此时,四国的程序员先感觉到,马上将对应的lib库和头文件copy到自己的工程中,修改SiguoClient目录下的相关文件,重新编译、测试并发布出去。但此时,升级的程序员可以先不理会这个更改,因为老程序虽然没有新功能,但一样能用,不需要升级。(但未来一定要升级,可以等升级的程序员闲下来的时候进行)。如果将include、lib这些目录放在一起,就会导致,升级总是编译不通过(因为升级的代码并),万一有意外,我们希望重新恢复系统,就发现出了大事情!所以,一个重要的原则是:每个工程,如果不和其他工程关联下,应该形成自己独立的目录结构,并可以单独编译成功,即尽量不依赖其他人的变化。这个方法的不好的地方是:每次有其他接口模块升级后,程序员都要做以下两步:1)去Get Lastest Version一下.h和.dll文件到本地。2)将这些文件再Copy到自己的工作工程目录下,然后重新根据新的头文件编译调试工程。勤快点,当作锻炼身体,点两下鼠标而已!第五节 文件dll的属性文字描述我们希望在输出文件里直接加入文字描述,最重要的包括,输出的dll的版本号是多少。这样,我们可以直接用鼠标点dll文件名,然后,按右键,选“属性”,在版本里看到相关的信息。而不用非要写一个客户程序去尝试服务的版本号。具体方法如下:l 在工程里,加入一个Resource文件方法:在Visual C+6.0里,选择New菜单,在弹出的Dialog里选择“Resouce Script”,并输入“Description”。注意:已有资源文件的工程,可以省略此步。l 在Resource里加入Version资源在Resource里,选择菜单Insert,选择“Resource(Ctrl R)”,在Resource Type Dialog里选择“Version”。l 编辑Version资源对于VS_VERSION_INFO资源,最重要的是编辑“ProductVersion”项,我们输入版本号“1.2”。注意:有两个ProductVersion,编辑下面那个。可惜,lib无这种机制,不过由于lib是完全编译到客户的exe中去(或dll中去),所以不用担心版本的控制。第六节 SourceSafe和VC的工作环境SouceSafe很好地和VC结合。(当然了,是一家公司开发的)怎么方便使用SourceSafe和VC呢?一、 对于新建立的WorkSpace方法很简单,首先,在SourceSafe里建立自己的工作目录,然后关联到自己本机的工作目录。(记得Set Working Folder这个功能吗?)然后在VC里New一个WorkSpace,New完后,在VC的FileView里,点WorkSpace名,按鼠标右键,选择Add to Source Control。未来每在WorkSpace里增加一个Project,都需要对这个工程进行一次Add to Sour
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 物业车位双租合同范本
- 2025年小学六年级英语下学期专项训练试卷
- 美术用品采购合同范本
- 租房保证金的合同范本
- 货物代储管理合同范本
- 衣服清仓收购合同范本
- 六年级下科学教学设计-无处不在的能量-青岛版(六年制三起)
- 酒店合作项目合同范本
- 直播合同范本资料模板
- 维修售后协议合同范本
- 机电安全知识培训资料课件
- 学堂在线 运动与健康 章节测试答案
- 满意度与NPS关联性分析-洞察及研究
- 2025宁波的租房合同模板
- 《红楼梦》里的茶茶水与茶器
- 延髓梗死课件
- 海绵城市建设技术要点总结
- 大跨度钢结构滑移施工技术
- 2025-2026学年人教版数学七年级上册暑期计算题自学练习(含解析)
- 河南公墓管理办法
- DB42-T 2391-2025 全域国土综合整治项目实施方案编制指南
评论
0/150
提交评论