界面和排错 界面( nterface) 造墙之前,我必须设法弄 该把什么放在墙里,什么放在墙外 071121 最需要防御的又是什么 确实有些东西不喜欢墙 总希望它倒下来 obert Frost,《修墙》 什么是界面( Interface)? 程序界面 界面就是接口( nterface) 团队合作 向调用者隐藏实现细节 程序界面 ·课程项目 类和函数的接口 公司项目 自己用的小程序中无关紧要 开源项日 在提供给别人用的时候非常有意义的 用户界面 清晰性、规范性、统一性、熟悉性和 目标:让别的程序员无障碍地、安全地使 用你的代码。 鲁棒性 在遇到坏的输入时本身不会受到损害 类和函数的接口 错误处理 类和函数的设计原则 类和函数的设计原则(1/10) 单一功能原则( Single Responsibility Principle) 开放封闭原则( Open/Close Princip 单一功能原则 Single Responsibility 最小惊讶原理( Least Surprise Principle) 完整且最小化( Complete and Minimal 处理的事情太多的类是脆弱的。 尽量使用 const 比如,一个全文索引类 FullTextsearch,既要 避免公共成员变量 读取数据、分词,又要建立倒排索引并写入磁 尽量使用传址 盘,还要读取磁盘文件并检索,太大,太臃肿 如果用不到编译器缺省的成员函数,就明确的禁用它 分割命名空间 写一个好的头文件 一个类应该只提供单一的功能 ·只有一个可能引起修改类代码的原因 ·拆分与合并
界面和排错 王栋 2007.11.21 界面(Interface) 在造墙之前,我必须设法弄清 该把什么放在墙里,什么放在墙外, 最需要防御的又是什么。 确实有些东西不喜欢墙, 总希望它倒下来。 — Robert Frost,《修墙》 什么是界面(Interface) ? ¾ 界面就是接口(Interface) z 向调用者隐藏实现细节 ¾ 程序界面 z 类和函数的接口 z 自己用的小程序中无关紧要 z 在提供给别人用的时候非常有意义的 ¾ 用户界面 z 风格:简单性、清晰性、规范性、统一性、熟悉性和 严谨性 z 鲁棒性:保证在遇到坏的输入时本身不会受到损害 程序界面 ¾ 团队合作 z 课程项目 z 公司项目 z 开源项目 ¾ 目标:让别的程序员无障碍地、安全地使 用你的代码。 z 类和函数的接口 z 错误处理 类和函数的设计原则 ¾ 单一功能原则(Single Responsibility Principle) ¾ 开放/封闭原则(Open/Close Principle) ¾ 最小惊讶原理(Least Surprise Principle) ¾ 完整且最小化(Complete and Minimal) ¾ 尽量使用const ¾ 避免公共成员变量 ¾ 尽量使用传址 ¾ 如果用不到编译器缺省的成员函数,就明确的禁用它 ¾ 分割命名空间 ¾ 写一个好的头文件 类和函数的设计原则(1/10) ¾ 单一功能原则(Single Responsibility Principle) z 处理的事情太多的类是脆弱的。 z 比如,一个全文索引类FullTextSearch,既要 读取数据、分词,又要建立倒排索引并写入磁 盘,还要读取磁盘文件并检索,太大,太臃肿 了。 z 一个类应该只提供单一的功能 • 只有一个可能引起修改类代码的原因 • 拆分与合并
举例:全文索引系统 类和函数的设计原则(2/10) 开放封闭原则( Open/close Principle 一个类应该对扩展开放,对修改关闭 对新的功能的扩充,应该通过增加新类实现,而不是 修改已有类的代码 比如,boss让你编写一 中国个人所得税的计算 器产品,而有一天一个美国客户来了… 好处 低程序各部分之间的耦合性,使程序模块互换成为可能。 使软件各部分便于单元测试 利于实现软件的模块的互换,软件升级时可以只部署发生变化 的部分,而不会影响其它部分 类和函数的设计原则(3/10) 类和函数的设计原则(3/10)cont 最小惊讶原理 Least Surprise Principle) 一致性和规范性 ·程序对用户的响应尽可能不使用户感到惊讶 比如,S∏L容器类提供了非常一致的界面,这 重载函数,覆盖函数,或者子类实现父类虚函 样,即使面对一个不熟悉的函数,预计应该如 数时,应该基本维持函数原来所期望的功能 何使用它也会变得很容易 class Pet void Talk0{cout<<“喵r; rtual Talko class Dog: public Pet 类和函数的设计原则(4/10) 类和函数的设计原则(5/10) 完整且最小化( Complete and Minimal) 尽量使用 const const参数 strcpy(charest, const char'src, int 提供调用者需要的所有合理功能 谨慎对待是否要修改调用者的数据 最小化 尽可能让函数个数少 const成员函数 没有任何两个成员函数的功能是重叠的 多快好省! har& operator (nt position onst char& operator] (int position) const;
举例:全文索引系统 index analysis search Indices store utility Data source Hits Query 类和函数的设计原则(2/10) ¾ 开放/封闭原则(Open/Close Principle) z 一个类应该对扩展开放,对修改关闭 z 对新的功能的扩充,应该通过增加新类实现,而不是 修改已有类的代码 z 比如,boss让你编写一个计算中国个人所得税的计算 器产品,而有一天一个美国客户来了… z 好处 • 降低程序各部分之间的耦合性,使程序模块互换成为可能。 • 使软件各部分便于单元测试。 • 利于实现软件的模块的互换,软件升级时可以只部署发生变化 的部分,而不会影响其它部分。 类和函数的设计原则(3/10) ¾ 最小惊讶原理(Least Surprise Principle) z 程序对用户的响应尽可能不使用户感到惊讶 z 重载函数,覆盖函数,或者子类实现父类虚函 数时,应该基本维持函数原来所期望的功能 class Pet { public: virtual Talk() = 0; }; class Cat: public Pet { public: void Talk() { cout << “喵!"; } } class Dog: public Pet { public: void Talk() { Sleep(); } } 类和函数的设计原则(3/10) cont. ¾ 一致性和规范性 z 比如,STL容器类提供了非常一致的界面,这 样,即使面对一个不熟悉的函数,预计应该如 何使用它也会变得很容易。 类和函数的设计原则(4/10) ¾ 完整且最小化(Complete and Minimal) z 完整 • 提供调用者需要的所有合理功能 z 最小化 • 尽可能让函数个数少 • 没有任何两个成员函数的功能是重叠的 ¾ 多快好省! 类和函数的设计原则(5/10) ¾ 尽量使用const z const参数 • strcpy(char* dst, const char* src, int); • 谨慎对待是否要修改调用者的数据 z const返回值 z const成员函数 class String { public: char& operator[] (int position); const char& operator[] (int position) const; }
类和函数的设计原则(6/10) 类和函数的设计原则(/10) 避免公共的成员变量 >尽量使用传址( pass by reference) 使用函数控制成员变量的存取权限 cC++默认为传值( pass by value) 几乎没有成本,却带来很好的安全性 对复杂的自定义类型,传值的效率太低 class Integer( int computeInt(const T&)( public it getlntvalueo const 小心传回的引用指向不存在的对象! 类和函数的设计原则(8/10) 类和函数的设计原则(9/10) >如果用不到编译器缺省的成员函数,就明 分割命名空间 确的禁用它。 比如拷贝构造函数, operator= const int VERSION const double vERSION=1-0 ·防止调用者无意中用到这些功能,因为一旦你 改写了实现,就可能造成调用者程序出现问 几 const int VERSION =1 ·怎么禁用呢? 声明为 private const double vERsIoN= 1.01 类和函数的设计原则(10/10) 类和函数的设计原则cont 写一个好的头文件 隐蔽的模棱两可 ambiguity)状态 谡舂泽裔禽时重建受数,尤其对多人合作的大和序,可能 头文件 C++ Program Language).#有银计细的说明,感兴趣的同学可以 double d=1.01 public: operator A const; 为防止头文件被重复引用,使用预处理指令 indef INDEX H ass Chad: public Father, public 非 Drooma once和编译器相关,个人认为井不方便 避免对指针类 各必仲用 ifndefldef ne/and,極块内的非公玨 void t(int 头文性可以使用 Eoroomaonce Did f(string str)
类和函数的设计原则(6/10) ¾ 避免公共的成员变量 z 使用函数控制成员变量的存取权限 z 几乎没有成本,却带来很好的安全性 class Integer { public: int getIntValue() const; void setIntValue(int value); private: int value; } 类和函数的设计原则(7/10) ¾ 尽量使用传址(pass by reference) z C/C++默认为传值(pass by value) z 对复杂的自定义类型,传值的效率太低。 template int computeInt(const T&) { ..... } ¾ 小心传回的引用指向不存在的对象! template T& returnValue() { T v; return v; } 类和函数的设计原则(8/10) ¾ 如果用不到编译器缺省的成员函数,就明 确的禁用它。 z 比如拷贝构造函数,operator = z 防止调用者无意中用到这些功能,因为一旦你 改写了实现,就可能造成调用者程序出现问 题! z 怎么禁用呢? • 声明为private 类和函数的设计原则(9/10) ¾ 分割命名空间 z C++ Namespace const int VERSION = 1; const double VERSION = 1.01; namespace Index { const int VERSION =1; } namespace Store { const double VERSION = 1.01 } 类和函数的设计原则(10/10) ¾ 写一个好的头文件 z 不要在头文件中定义全局变量和函数,尤其对多人合作的大程序,可能 引发在编译与链接时期的重复定义。 z 头文件中应该只有声明,不要定义 • 《The C++ Program Language》一书中有很详细的说明,感兴趣的同学可以 看看 z 为防止头文件被重复引用,使用预处理指令 #ifndef INDEX_H #define INDEX_H //头文件内容 class Index { …… #endif z #progma once和编译器相关,个人认为并不方便。 z 建议:对外的接口头文件务必使用ifndef/define/endif, 模块内的非公开 头文件可以使用#progma once 类和函数的设计原则 cont. ¾ 隐蔽的模棱两可(ambiguity)状态! z 隐式转换 z 多继承 ¾ 避免对指针类型和数值类型进行重载 void f(int x); void f(string* str); f(0); class B; class A { public: A(const B&); } claas B { public: operator A() const; } void f(const A&); B b; f(b); void f(int) void f(char) double d = 1.01 f(d); f( (int)d ); //ok! class Child: public Father, public Mother Child d; d.Talk(); d.Father::Talk(); //ok!
错误处理原则 用户界面 一般情况下,应该由调用者决定对错误的 合翻窃药的一部分,与认知学、人机工程学,心理学等学科领域 处理方式,而不该由被调用程序决定 实现技术 MFC: 返回值 抛出异常 Qt:一个多平台的C++图形用户界 忽略并写入日志 由版是为开源软件提供的u11版本 入式自由版是为开发自由软件提供的嵌入式版本 这一点在用户界面设计中也同样适用。 ·鲁棒性:保证在遇到坏的输入时本身不会受到 Ec pse RCP之SMT 损害 Rich Internet Application (1/2) Rich Internet Application(2/2) Java Applet >Flash Action Script Asynchronous Java Script and XML XmlhTtpreQuesT(xhr) JavaScript Toolkit D Prototype http∥doiotoolkitorg 比较底层,提供的高级功能较少 Dojo Demos ·很强大,创建类似桌面应用程序的Ul >Dojo的目标是解决开发 DHTML应用程序遇到的那 树、表等组件 些长期存在的历史问题。 Dojo让你更容易使web页面具有动态能力,或在 比Dojo内存占用较小 任何稳健的支持 JavaScript语言的环境中发挥作 用,利用dojo提供的组件,你可以提升你的web 应用程序可用性、交互能力以及功能上的提高
错误处理原则 ¾ 一般情况下,应该由调用者决定对错误的 处理方式,而不该由被调用程序决定。 z 返回值 z 抛出异常 z 忽略并写入日志 ¾ 这一点在用户界面设计中也同样适用。 z 鲁棒性:保证在遇到坏的输入时本身不会受到 损害 用户界面 ¾ 人机交互设计的一部分,与认知学、人机工程学、心理学等学科领域 有密切的联系。 ¾ 实现技术 z MFC:Microsoft Foundation Classes,是微软提供的,用于在C++环境 下编写应用程序的一个框架和引擎,是 Windows API与C++的结合。 z dotNet Winform:微软力推的新一代的C#应用程序框架。 z Qt:一个多平台的C++图形用户界面应用程序框架。 • 企业版和专业版提供给商业软件开发 • 自由版是为开源软件提供的Unix/X11版本 • 嵌入式自由版是为开发自由软件提供的嵌入式版本 z Java GUI • Swing, AWT, … z Eclipse RCP之SWT z Python:是一种面向对象的解释性语言,具有脚本语言中最丰富和强大 的类库 。 z Web-based • HTML,ASP, JSP….. • Rich Internet Application Rich Internet Application (1/2) Rich Internet Application (2/2) ¾ Java Applet ¾ Flash z ActionScript ¾ Ajax z Asynchronous JavaScript and XML z XMLHttpRequest (XHR) z Web 2.0 JavaScript Toolkit ¾ Prototype z 比较底层,提供的高级功能较少 ¾ Dojo z 很强大,创建类似桌面应用程序的 UI z 树、表等组件 ¾ Rico z 比Dojo内存占用较小 Dojo ¾ http://dojotoolkit.org/ ¾ Dojo Demos z http://www.dojochina.com/dojo/demos/demoEngine.ht ml ¾ Dojo的目标是解决开发DHTML应用程序遇到的那 些长期存在的历史问题。 ¾ Dojo让你更容易使web页面具有动态能力,或在 任何稳健的支持JavaScript语言的环境中发挥作 用,利用dojo提供的组件,你可以提升你的web 应用程序可用性、交互能力以及功能上的提高
leb Application Microsoft Internet Explorer HTML CSs JavaScript 工具≯ terne选项→高级→反选 Ajax开发工具集 禁用脚本调试( nternet Explorer Microsoft Internet Explorer 禁用脚本调试(其它) Charles Http Proxy debugger Microsoft Script Debugger Mozilla Firefox Fire Bug Web Developer Charles Http Proxy debugger Mozilla firefox ≥Add-ons DOM Inspecto 排错 (http://www.mozilla.org/projects/inspectorl) JsvIew( Http: /laddons. mozilla orgen US/firefox/addon/2076) b.机器、计划或其他类似东西中的缺陷、故障或过失。缘自美国 889年《PaMa报》3月11日11,我听说爱迪生先生前两夜都爬 (http:/laddons.mozillaorg/firefox/60) 起来在他的留声机里寻找“bug——这表 困难,说是有什 么想象中的害虫秘密地隐藏在里面并造成了所有的麻烦 Charles HT TP Proxy debug 《牛津英语词典》第2版 错误类型 整体上减少错误的方法 ≯编译错误 追求简单优雅的设计风格 逻辑错误 >减少程序各个部分之间的关联 程序运行结果不对 程序的复杂性与各部件间可能互相作用的途径数目 异常错误 有关 指针异常 方法:信息隐蔽、抽象和界面(很多语言有这方面 内存分配异常 使用错误较少的语言 除0异常 没有任何语言能够防止你犯错误!
Web Application ¾ HTML + CSS + JavaScript ¾ Ajax开发工具集 z Microsoft Internet Explorer • Microsoft JavaScript调试器 • Charles HTTP Proxy debugger z Mozilla Firefox • FireBug • DOM Inspector • JSView • Web Developer • Charles HTTP Proxy debugger Microsoft Internet Explorer ¾ 工具ÆInternet选项Æ高级Æ反选 z 禁用脚本调试(Internet Explorer) z 禁用脚本调试(其它) ¾ Microsoft Script Debugger Mozilla Firefox ¾ Add-ons z Firebug ( http://www.getfirebug.com/ ) z DOM Inspector ( http://www.mozilla.org/projects/inspector/ ) z JSView ( http://addons.mozilla.org/enUS/firefox/addon/2076 ) z Web Developer ( http://addons.mozilla.org/firefox/60 ) z Charles HTTP Proxy debugger ( http://www.xk72.com/charles ) 排错 bug. b. 机器、计划或其他类似东西中的缺陷、故障或过失。缘自美国。 1889年《Pall Mall报》3月11日1/1,我听说爱迪生先生前两夜都爬 起来在他的留声机里寻找“bug”——这表示解决一个困难,说是有什 么想象中的害虫秘密地隐藏在里面并造成了所有的麻烦。 —《牛津英语词典》第2版 错误类型 ¾ 编译错误 ¾ 逻辑错误 z 程序运行结果不对 ¾ 异常错误 z 指针异常 z 内存分配异常 z 除0异常 z …… 整体上减少错误的方法 ¾ 追求简单优雅的设计风格 ¾ 减少程序各个部分之间的关联 z 程序的复杂性与各部件间可能互相作用的途径数目 有关 z 方法:信息隐蔽、抽象和界面(很多语言有这方面 的支持) ¾ 使用错误较少的语言 z 比如,取消指针,取消goto z 没有任何语言能够防止你犯错误!!
便于调试的代码风格 排错的心理准备 尽量不用全局变量 所有变量都要初始化,成员变量在构造函 >排错的时间至少跟写程序一样长 数中初始化 ≯发现错误能够防止类似的错误再发生 尽量使用 const 最好自己发现错误,这样记忆比较牢固 详尽的注释 不要去怀疑编译器和库函数 虽然编译器和库函数可能有bug,但是出现 bug的可能性极其极其小 排错的错误定位 简单错误(1/3) 不要把所有的判断交给编译器和调试器 应该在程序中自己加以程序保护和错误定 大部分错误都是简单错误 ≯逆向推理,是否见过类似的问题 ·参数检查返回值检查 比如c语言中 使用异常机制 使用调试输出 scanf(%d”n) #define PR(x) cout<<#x="<<X<<'In 没有对局部变量初始化 日志输出 ·如果程序中的数值或者输出的结果很怪异,就 Log4cxX 很可能是上面提到的这几种错误 简单错误(2/3) 简单错误(3/3) 不要两次出现同样的错误 简单的排错步骤 ·改正一个错误之后,是否在别处也有过类似的 使用排错系统取得堆栈轨迹和几个变量的值 错误 发现出错的位置 简单代码非常熟悉,使我们放松警惕 读程序,而不是马上去改程序 ·比如多重循环中是否写错(这两个小样还是 ·比如 保证析构函数的异常在析构函数中得到处理 挺像的!) 在析构函数中释放内存时或者关闭s0cket时等 要保证基类的析构函数是虚函数 Father" f= new Child 低级错误: for (unsigned int i=10.p=0,-)-
便于调试的代码风格 ¾ 尽量不用全局变量 ¾ 所有变量都要初始化,成员变量在构造函 数中初始化 ¾ 尽量使用const ¾ 详尽的注释 排错的心理准备 ¾ 排错的时间至少跟写程序一样长 ¾ 发现错误能够防止类似的错误再发生 z 因此最好自己发现错误,这样记忆比较牢固 ¾ 不要去怀疑编译器和库函数 z 虽然编译器和库函数可能有bug,但是出现 bug的可能性极其极其小 排错的错误定位 ¾ 不要把所有的判断交给编译器和调试器, 应该在程序中自己加以程序保护和错误定 位 z 参数检查,返回值检查 z 使用异常机制 z 使用调试输出 #define PR(x) cout=0; i--){…}
处理不可重现错误(1/3) 处理不可重现错误(2/3) 癃目麦和選繪岩的恋韁箕着用对多 动态内存分配要小心 申请了内存空间后,必须检查是否分配成功。 >检查变量是否都初始化 凳類奥捺璿馥折N0让,的止 当程序出现不可理解的异常时,多半是某个地方有 内存越界 前面赋值a=5,后面却发现a的值不为5 选读:cC+ ory CorruptionAndMe 很可能是存储分配错误 oryLeaks ht char'msgoi char c[10]: return C: 1 的同学可以去了解下智能指针( Smart Pointer) for( p=head; p=NULL; p=p->next) free(p) throw exception0;∥此处抛出异常,s指向的内存释放了吗? c-1}=1 delete[s;∥不要写成 delete s; 处理不可重现错误(3/3) VC调试补充 使用函数时注意 C调试 不要使用还没有完全了解输入要求和输出 编写易于调试的VC代码 结果的函数MSDN、 Google) http:/idEv.csdnnet/developlaRticle/17/17148.s ·注意有多个参数类型相同的函数,比如 memset(used,-1, sizeof(used),后两个 ≯VC常用调试工具 参数容易写反 v Standar GDB调试补充 建议 用GDB调试程序 http://kaober.spaceslivecom/blog/cns55 多做笔记 多思考,减少对排错系统的依赖 BE9523CBEFD1DB! 158. entry ≯调试输出,日志帮助错误定位。尽量不要从第- 行代码开始跟踪 推荐几本书 《 Effective c++》 MSDN 《高质量cc++编程指南》
处理不可重现错误(1/3) ¾ 检查日志和调试输出,确定大概出错位置,对多 线程以及长时间运行的服务器程序尤其有用。 ¾ 检查变量是否都初始化 ¾ 当程序出现不可理解的异常时,多半是某个地方有 内存越界 z 前面赋值 a=5,后面却发现a的值不为5 ¾ 很可能是存储分配错误 char* msg() { char c[10]; return c; } for( p=head; p!=NULL; p=p->next ) free(p); c[-1]=1; 处理不可重现错误(2/3) ¾ 动态内存分配要小心! z 申请了内存空间后,必须检查是否分配成功。 z 释放了内存空间后,应该把指向这块内存的指针指向NULL,防止 后面不小心使用了它,尤其对类的指针成员变量。 z 申请的空间必须释放,小心内存泄露。 z 讨论:C++里如何检测内存泄露? z 讨论:Java里怎么发生内存泄露? ¾ 选读:C/C++ Memory Corruption And Memory Leaks http://www.yolinux.com/TUTORIALS/C++MemoryCorruptionAndMe moryLeaks.html ¾ 有兴趣的同学可以去了解下智能指针(Smart Pointer ) z std::auto_ptr char* s = new char[10] ….. throw exception();//此处抛出异常, s指向的内存释放了吗? ….. delete [] s; //不要写成 delete s; 处理不可重现错误(3/3) ¾ 使用函数时注意 z 不要使用还没有完全了解输入要求和输出 结果的函数(MSDN、Google...) z 注意有多个参数类型相同的函数,比如 memset(used , -1, sizeof(used)),后两个 参数容易写反 VC调试补充 ¾ VC调试 ¾ 编写易于调试的VC代码 http://dev.csdn.net/Develop/article/17/17148.s htm ¾ VC常用调试工具 GDB调试补充 ¾ 用GDB调试程序 http://kaober.spaces.live.com/blog/cns!55 BE9523CBEFD1DB!158.entry 建议 ¾ 多实践 ¾ 多做笔记 ¾ 多思考,减少对排错系统的依赖 ¾ 调试输出,日志帮助错误定位。尽量不要从第一 行代码开始跟踪 ¾ 推荐几本书 z 《Effective C++》 z MSDN z 《高质量C/C++编程指南》 z Google
A1评分标准说明 行程序 Compile Error 谢谢! Wrong Answer Presentation error 看代码 看报告
谢谢! A1评分标准说明 ¾ 运行程序 z Compile Error z Wrong Answer z Presentation Error z Time Limit Exceed ¾ 看代码 ¾ 看报告