软件开发、软件设计培训笔记

一、代码质量的提高培训笔记(2013.11.29):

部分内容摘自网络:

1.struct:注意成员排列顺序。

2.#pragma pack(1) :指定按1字节对齐。

3.任何*都是占4字节:sizeof(int*)与sizeof(char*)大小都是4字节。

4.函数指针 vs. 回调函数:(1)、函数指针,相当于取别名,表示一个(类)函数的入口地址,标志(* ,如:int(*ptr)(int, int);(2)、回调函数:就是一个通过函数指针调用的函数。假如那你把A函数的指针当作参数传给B函数,然后在B函数中通过A函数传进来的这个指针调用A函数,那么这就是回调机制。B函数就是回调函数,而通常情况下,A函数是系统在符合你设定条件的情况下会自动执行。

5.内部连接:如果一个名称对于它的编译单元来说是局部的,并且在连接时不会与其它编译单元中的同样的名称相冲突,那么这个名称有内部连接。

外部连接:在一个多文件程序中,如果一个名称在连接时可以和其它编译单元交互,那么这个名称就有外部连接。

全局变量、全局函数默认是外连接的;类定义、全局常量、const对象(变量)、typedef类型、宏定义默认为内连接的。默认为内连接的标识符,如果显示加上extern关键字,即变成外连接。

6.全局变量不可定义在头文件里,只在头文件声明。建议:全局变量尽量少用。

7.union:主要是共享内存,分配内存以其最大的结构或对象为大小,当多种类型、多个对象、多个事物只取其一时(n选1),可以使用联合体来节约内存。

8.宏:分三类,(1)、常量宏:#define PI3.14 ,从宏名可以判断其意,尽量用const代替(尽量将工作交给编译器,而不是预处理器);(2):函数宏: #defineADD(a, b) ((a)+(b)) ,要使用完备的括号,尽量使用内联函数代替;(3)控制宏:#define_OPENMP,#ifdef _OPENMP … #else … #endif ,应该将所有控制宏放在一个固定的头文件中,统一管理。

9.stack vs. heap:(1)、stack:存放着程序中的局部数据,读取速度快,由编译器自动管理,无需手工控制,栈空间未初始化显示字符“烫”;(2)、heap:存放的是动态内存,供程序随机申请使用,由new分配的内存块,释放工作由程序员控制,容易产生memoryleak,堆都是动态分配的,没有静态分配的堆,堆的效率比栈要低的多,堆容易造成内存碎片,堆空间未初始化显示字符“屯”。

10.static vs. 全局变量:(1)、静态局部变量:生存期为整个源程序,其作用域只能在定义该变量的函数内使用,对基本类型的静态局部变量若在声明时未赋以初值,则系统自动赋予0值;(2)、静态全局变量:在全局变量的声明之前加static,非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。全局变量在程序开始之前初始化。

11.函数参数,传值vs. 传地址:(1)、传值:单向的“值传递”,大小有可能大于4字节;(2)、传地址:大小sizeof(int),传指针要另外开辟内存单元,其内容为地址,而传地址不单独占内存单元,函数返回值一般用作状态标志,优先选择传地址。

12.可以使用enum来定义类内常量。

13.动态内存分配:(1)、malloc/free->标准库函数,new/delete->C++的运算符;(2)、对于非内部数据类型的对象,对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free,new=malloc + construct; delete=destruct + free。new并不一定成功,多次new会造成内存碎片。

14.拷贝构造函数:对于类,它会隐式产生一个类的默认构造函数,拷贝构造函数,拷贝赋值运算符和析构函数。默认的拷贝构造函数属于位拷贝(浅拷贝,类似于memcpy)。在某些需要禁止对象复制操作的情形下,可以将这个类相应的拷贝构造函数、赋值操作符operator=声明为private,只声明而不定义(不给出实现)。如果类内部出现了动态配置的资源,就不得不自定义实现其拷贝函数(拷贝构造函数,赋值运算符)。

15.自增自减运算符重载:T&operator++();//++前缀,const Toperator++(int);//++后缀,对后缀操作增加一个标识参数,以示区分,这个参数在运算中没有任何用途,只是从语法上区分函数名。前缀操作重载时返回引用,后缀操作重载时返回值应该为一个const对象。优先使用前缀操作。自减运算符类似。

16.reference vs. pointer:(1)、引用被创建的同时必须被初始化(指针可以在任何时候初始化);(2)、不能有NULL引用,引用必须与合法的存储单元关联(指针可以是NULL);(3)、一旦引用被初始化,就不能改变引用的关系(指针则可以随时改变所指的对象);(4)、referenceis a safe pointer。

17.首选初始化列表实现类成员的初始化:(1)、const成员变量只能用成员初始化列表来初始化,而不能在构造函数内赋值;(2)、如果类B中含有A类型的成员变量,而类A中又禁止了赋值操作,此时要想顺利地完成B中成员变量的初始化,就必须采用初始化列表方式;(3)、要保证成员变量声明的顺序与初始化列表顺序一致。

18.注意构造函数里的内存申请,不要把与构造无关的操作放在构造函数里。

19.一步构造vs. 两步构造:(1)、一步构造:用一个步骤构造并初始化对象,全部过程用构造函数完成;(2)、两步构造:用两个单独的步骤构造和初始化对象。构造函数创建对象,初始化函数初始化对象。两步构造总是比较安全,在一步构造中,如果提供不正确的参数或者内存分配失败,构造函数可能引发异常。使用两步构造则可避免这个问题,虽然确实不得不检查是否失败。这两种情况下,销毁对象的过程相同。

20.const用法:(1)、定义常量,如果const出现在*左边,表示所指数据为常量;如果出现在*右边,表示指针自身是常量;(2)、修饰函数形式的参数:输入参数采用“指针传递”或“引用传递”以防止被意外改动;(3)、修饰函数返回值,用于返回常量,如给”指针传递”的函数返回值加const,则返回值不能被直接修改,且该返回值只能被赋值给加const修饰的同类型指针;(4)、修饰成员函数:表示在函数体中成员变量不能改变,const成员函数不允许对数据成员进行任何修改;const对象只能访问const成员函数,而非const对象可以访问任意的成员函数;const对象的成员是不可修改的,然而const对象通过指针维护的对象却是可以修改的;const成员函数不可以修改对象的数据,不管对象是否具有const性质。

21.继承:(1)、对象的构造先基类后子类,如果含有多个基类,那么就按照声明顺序由前及后执行,析构函数则严格按照构造的逆序执行;(2)、慎用私有继承;(3)、尽量少使用多重继承;(4)、不要做消弱基类接口的事情。

22.继承vs. 组合:(1)、继承:在逻辑上表达的是“是一种(Is-A)”的关系;若在逻辑上B是一种A,并且A的所有功能和属性对B而言都有意义,则允许B继承A的功能和属性;继承属于“白盒”复用;(2)、组合:在逻辑上表示的是“有一个(Has-A)”的关系,即A是B的一部分;组合属于“黑盒”复用。优先选择组合使用。

23.聚合vs. 组合:(1)、聚合,指的是整体与部分的关系。通常在定义一个整体类后,再去分析这个整体类的组成部分。从而找出一些组成类,该整体类和组成类之间就形成了聚合关系;(2)、组合,也表示类之间整体和部分的关系,但是组合关系中部分和整体具有统一的生存期。一旦整体对象不去存在,部分对象也将不存在。部分对象与整体对象之间具有共生死的关系。它们区别在于:聚合关系是”has-a”关系,组合关系是”contains-a”关系,聚合关系表示整体与部分的关系比较弱,而组合比较强;聚合关系中代表部分事物的对象与代表聚合事物的对象的生存期无关,一旦删除了聚合对象不一定就删除了代表部分事物的对象。组合中一旦删除了组合对象,同时也就删除了代表部分事物的对象。

24.多态:C++多态性是通过虚函数来实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为覆盖(override)或者称为重写。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。(1)、静多态:让相同的实现代码应用于不同的场合;(2)、动多态:让不同的实现代码应用于相同的场合;(3)、动多态会在运行时报错,而静多态则在编译时报错;(4)、多态基类的析构函数应该为虚;(5)、多态时注意不要发生拷贝构造函数。

25.动态绑定(晚绑定) vs. 静态绑定(早绑定):为了支持C++的多态性,才用了动态绑定和静态绑定。对象的静态类型:对象在声明时采用的类型,是在编译期确定的。对象的动态类型:目前所指对象的类型,是在运行期决定的,对象的动态类型可以更改,但是静态类型无法更改。(1)、静态绑定:绑定的是对象的静态类型,某特性(比如函数)依赖于对象的静态类型,发生在编译期,编译时绑定,通过对象调用;(2)、动态绑定:绑定的是对象的动态类型,某特性(比如函数)依赖于对象的动态类型,发生在运行期,运行时绑定,通过地址实现。

26.虚函数表vs. 虚指针:(1)、虚函数表:虚函数机制的实现是通过虚函数表和指向虚函数表的指针来完成的。关键字virtual告诉编译器该函数应该实行晚绑定,编译器对每个包含虚函数的类创建虚函数表VTable,以放置类的虚函数地址。编译器秘密放置了指向虚函数表的指针VPtr,当多态调用时,它会使用VPtr在VTable表中查找要执行的函数地址;(2)、虚指针指向虚函数表,虚指针存在于每个对象中;(3)、一个类对应一个虚函数表;一个对象对应一个虚指针。

27.抽象类:纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加”=0”。含有纯虚函数的类称为抽象类。抽象类不能被实例化。

28.对外提供接口的头文件里不要暴露任何数据成员。

29.父类中若有虚函数,则此父类中的析构函数也应为虚。

30.在性能问题出现之前不考虑性能。

31.模板:以关键字template开始,后接模板形参表,模板形参表是用尖括号括住的一个或者多个模板形参的列表,形参之间用逗号分隔。模板类型不是一种实类型,它必须等到类型绑定后才能确定最终类型。

32.STL:vector多用于访问;list多用于插入。

33.命名空间(namespace):(1)、表示标识符的可见范围,可以更好地控制标识符的作用域。一个标识符可在多个命名空间定义,它在不同命名空间中的含义是互不相干的。这样,在一个新的命名空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,因为已有的定义都处于其它命名空间中。防止类名或函数名冲突。(2)、有两种形式的命名空间,有名的和无名的。(3)、不能在命名空间的定义中声明(另一个嵌套的)子命名空间,只能在命名空间的定义中定义子命名空间。(4)、命名空间是开放的,即可以随时把新的成员名称加入到已有的命名空间中去,方法是,多次声明和定义同一命名空间,每次添加自己的新成员和名称。

34.设计模式:分为三大类,创建型模式、结构型模式和行为型模式。

二、代码质量评审培训笔记(2014.04.12):

1、保证软件质量的基本条件:编程好习惯
2、高质高效软件开发:(1)、行为:意识、价值观、工作习惯,愿景、协作、文化;(2)、能力:分析、抽象、总结、判断、推理,编码、差错、学习,口头表达、文档编写;(3)、方法:代码审查、单元测试、静态分析、动态分析、性能分析、配置管理、缺陷跟踪、SCRUM,Gcov、Lint、Valgrind、Git、Bugzilla,平台框架。
3、编程行为准则:(1)、承担责任:对copy-paste的代码负责、对后来的维护者负责;(2)、让代码逻辑清晰地“说话”:关注命名、适当注释,去除冗余、重视格式;(3)、积极思考;(4)、及时改善。
3.1、(1)、使用空格增加可读性;(2)、判断失败而非成功;(3)、利用sizeof减少内存操作失误;(4)、屏蔽编程语言特性;(5)、合理运用数组;(6)、利用编程语言特性提高效率;(7)、借助隐式初始化简化程序逻辑;(8)、精确包含头文件;(9)、恰当使用goto;(10)、引入中间变量;(11)、青睐小密度锁;(12)、以逆序方式释放分配获得的资源;(13)、永远将头文件作为桥梁。
3.2、(1)、让模块的对外头文件保持简洁;(2)、只暴露必要的变量和函数,运用static关键字;(3)、清除编译器报告的所有警告,尽量采用最新版本的编译器;(4)、在模块对外接口中防范错误:避免盲目检查所有函数参数的有效性、内部函数使用assert。
4、软件设计是一系列的创作活动,是借助编程语言以简单和优雅的方式表达并解决现实需求的一门科学和艺术。代码不是设计,而是设计表达。
5、如何提高设计能力:(1)、持续追求软件设计之美(原动力);(2)、实践加模仿(关键途径):掌握现成的好设计、通过模仿加以巩固;(3)、思考(突破):培养洞察力:难题总是以简单的方法解决、方法是否人性化;形成自己的设计思想(终极目标):掌握设计原则。
6、智能指针的使用。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值