您好,欢迎访问三七文档
C++编程经验总结C++的优缺点•C++是一门庞大的语言,它既具有数据底层操作的能力(如指针),也有面向对象的高层抽象能力(如类)。•掌握这些特性的人往往会欣赏这种能力,但是初学者则往往觉得无所适从。下面是一些例子:高手菜鸟指针很灵活,可以提高性能指针访问容易越界,还要释放,不敢用宏定义可以提高开发效率都不知道那个宏代表什么,还是老老实实写代码算了面向对象是一种好的设计方法写类真麻烦,成员函数名字都得加类名::,还是写C函数比较方便主要内容下面从以下三个方面谈谈C++的开发经验•写程序•看程序•调程序写程序写程序的一些技巧•对于缺少开发经验的人来说,主要有以下障碍:1.C++语言特性太多,不能恰当使用2.对面向对象思维方式不熟悉,无法设计复杂的程序3.无法发挥性能优势•针对这些问题,以下从几个方面谈谈解决办法1.养成良好的编程习惯2.学会面向对象的思维3.提高性能的简单技巧良好的编程习惯•C++语言特性太多,初学者往往会掉进一些陷阱中。•解决的办法是:通过良好的编程习惯,减少出错机会,减轻记忆负担。具体有以下几点:1.熟练使用IDE2.合理命名3.代码分段、对齐4.对容易错的地方加上详细注释5.把数据打包6.有规律地使用指针VisualC++快捷键•推荐安装VC插件VisualAssistX(VA)•VC、VA一些常用快捷键•Alt+G:跳到符号的定义、声明•Alt+Shift+F:查找符号的所有出现位置,在看代码、调试是经常用到•Ctrl+减号:跳到光标前一个位置•Ctrl+Shift+减号:跳到光标后一个位置•Ctrl+F:查找•Ctrl+H:替换,快速生成大量结构相似的代码时(例如xyz、rgb)常用调用堆栈•下层是调用者,上层是被调用者•可以双击跳到任意调用层次•去除冗余信息监视窗口•任意输入变量名•红色值表示新改变的内容•查看数组、Eigen矩阵、向量完整内容:变量名,长度•可以直接修改变量的值•更多技巧见这里控制代码的执行•跳过或重复执行某些代码,直接拖动黄色箭头•运行时修改代码管理代码文件•VS文件夹(筛选器)命名命名要遵循某些习惯。•类成员变量以前缀m_开头,静态变量以s_开头,全局变量以g_开头•类名各个单词首字母大写,其余小写•函数名各个单词首字母大写,其余小写,第一个单词首字母小写;或者所有字母小写,用_隔开各个单词•指针以p开头,如pVtx,pMesh;或Ptr结尾•宏、枚举值所有字母大写,用_隔开变量命名•变量命名含义要明确•以下是一些规则,实际命名时可以灵活运用类型命名方式类型命名方式向量、矩阵*Vec、*Mat个数n*、num*网格、顶点、边、面*Mesh、*Vtx、*Edge、*Face数组、标准库vector*Array标准库map*Map循环变量i,j,复杂时可用ith*索引、句柄*Idx、*Handle、h*类的对象从类名提取出关键词命名图片*Img指针p*函数命名•函数命名应该透露出函数的功能。函数功能命名函数功能命名获取、设置数据get*、set*分配、释放内存allocate*、free*进行计算(暗示需要一定时间)compute*、build*、solve*数据格式转换to*、*2*打印、显示数据print*、show*、display*、draw*添加、删除、清空add*、remove*、clear*回调函数、Qt槽on*查找find*判断is*、has*读、写文件read*、load*、write*、save*刷新update*、refresh*、flush*常数命名•对于经常使用的常数,可以定义成宏•有特殊含义的,或需要保持前后一致的,定义成宏•表示多种类型、状态的,定义成枚举类型函数命名•不会修改的参数,加const,这样可以给用户暗示函数的输入与输出,如:voidarray2Mat(constfloat*array,Matrix&mat);•不修改类对象的函数,定义为常成员函数,如floatgetTime()const;•有可能失败的计算过程,返回一个bool,true代表成功,false代表失败。如:boolcomputePos();•若某些参数难以调节或不需要调节,可以提供默认参数,如voidcompute(floatsize=1.f);•若可以生成一些结果,也可以不生成,提供默认为NULL的指针参数,如:voidcompute(double&val,Vector*grad=NULL);这也是少数的必须用指针的场合。•能用指针也能用引用的时候,用引用,如:voidpca(constMat&data,Mat&axis);•对指针修改时,用指针的引用,如:voidallocate(float*&buffer);代码分段•先写公有成员、再写保护和私有成员•同类访问类型的,先写函数、再写变量•把功能相近的函数、变量写在一起;不同的隔一行,适当注释代码分段•对于一些长的函数,往往可以分成几个阶段•可以把不同阶段分开,隔一行,并适当注释代码分段代码对齐•结构相近的代码,考虑对齐•头文件的函数、变量声明对齐•多个if条件对齐•对齐的好处:1.容易看出错误2.几句代码相近时,可以复制+替换(善用Ctrl+H)注释•每个分段开头有注释•对于容易误解的变量,加注释。例如:字符串长度?长度+1?顶点数?顶点数x3?行优先?列优先?•返回值有歧义,注释。例如:0是正常?1是正常?•涉及复制、内存分配、或可能被误解为涉及的,注释•函数、参数意义容易忘记或误解的,对输入有要求的,注释•涉及特别的、难懂的算法,注释打包数据•某几个数据经常需要整体读写,打包成结构或类。这样不容易漏掉,且容易增删数据•数据有内部复杂结构的,用typedef或先打包成结构•类内部反复利用一些数据结构,定义成内部结构或内部类•要进行复杂的索引计算的,或访问代码很长的,用引用、指针打包代码•某些简单代码块经常要插入,打包成宏•某些工具函数(例如:保存矩阵、格式转换)定义为工具类的静态成员有规律地使用指针•new、delete成对出现•必要时用注释明确内存分配、释放责任•类的成员指针初始化为NULL,定义clear函数统一释放•某些数据有特殊的分配策略(例如字节对齐),定义allocate、free函数对•用注释说明指针指向的数组长度。例如:点数?点数x3?•表示数组长度的变量要保持与指针同步,可以专门定义函数去管理•利用空指针表示“没有”。例如,默认参数为NULL的指针•需要进行复杂的索引计算时,用指针或引用存放计算结果总结•以上这些习惯都是经验性、指导性的,编程时要灵活运用,无需死守•养成这些习惯的目的是:通过习惯来减轻记忆负担,降低出错机会。例如:漏掉某些步骤、误解了某个函数、变量•有些习惯会使得开发变慢,但这是值得的面向对象的思维方式•初学者眼中的C++程序1.一个程序就是一堆类、数据、函数2.写程序就是把算法流程翻译成函数调用•有经验的人眼中C++的程序1.一个程序就是一个虚拟世界,例如:一个工厂2.写程序就是用代码构造那个虚拟世界,例如:建立一个工厂,安装需要的机器,让它们分工合作,生成你需要的产品3.设计程序就是要定义虚拟世界的事物(类)、制定虚拟世界的规则(类之间的合作方式)一种视角•图形学的开发经常要实现一些算法•可以想象自己在建立一个工厂,这个工厂接受一些原材料(输入数据),按照给定步骤加工(算法),返回所需的产品(输出)。•工厂里面安装了很多机器,每台机器接受其他机器传过来的原料,只管制造符合标准的产品,不过问产品的用途(显示在界面?继续处理?不用管)•机器要起一个像机器的名,如xxxProcessor、xxxSimulator、xxxComputer•这样通常的类结构如下:classMyProcessor{public:voidsetXXX();//输入参数(原料)voidcompute();//计算过程(加工)voidgetXXX();//输出参数(产品)private:voidcomputeA/B/C...();//内部流程A/B/C(工序)double*vtx;//各种内部数据Window*dialog;//绝对不能有,交由负责界面的机器处理};一种视角•机器加工的原料、中间产物、产品如果比较复杂,可以考虑装箱打包(必要时把输入、输出数据分别定义成结构或类)•机器的部分功能部件,如果很复杂,或者需要做成可替换的,可以考虑单独定义(某些次要算法,可以封装成另一个类,把这个类作为机器类的成员)classMyProcessor{public:classResult{…};//把结果装箱成一个ResultvoidgetResult(Result&res);(空一行哦)voidsetProcessorA(ProcessorA*a);//机器内部有个零件A//(A也可以看成一个小机器)private:structDataA{…};//中间产物,装箱打包,不容易丢}一种视角•某几种机器做的事情很相似(例如,计算拉普拉斯矩阵),仅仅具体方法有不同,让它们继承自基类,例如:LaplacianMaker•基类提供接口,例如:setMesh/getMesh/compute)•基类还可以提供一些默认的工序,例如,给定数组创建Eigen稀疏矩阵•派生的机器实现自身特有的处理步骤,例如:GraphLaplacian、CotangentLaplacian•机器的内部构造要隐藏在外壳内(封装),例如:我内部用数组还是vector你不用管,反正我给装箱的Result你就是了。•机器的操作要简单,不容易误解(函数接口尽可能简单,必要时加注释)一种视角•某些事物,虽然他们本质上是不同的,但是都有某些相同的行为,可以设法抽象出这种行为•例如:家具摆放算法,有时候需要移动单个家具,有时候需要整体移动几个家具•自然的想法是:所有家具都派生自一个Furniture类,整体移动的时候就写个循环•能不能把几个家具看成一个大家具?能!例如:桌子四周通常会摆椅子,可以把他们看成一个整体“桌椅”•定义一个FurnitureGroup继承自Furniture类(Furniture类或者改名成MovableObj类),可以加入任意的Furniture*对象•于是,整体移动的时候,就把他们加入一个Group,再移动Group养成预先设计的习惯•对于编程经验较少的人,一下子掌握面向对象、掌握设计模式并不容易,但是可以按照以上的思路,慢慢培养这种思维方式•写一个程序之前,要先想想,切忌盲目动手•可以在纸上写出你要设计的“工厂”,包括里面有什么“机器”、“产品”(类),这些机器怎样分工合作(类的使用关系)几点注意•设计“机器”、“产品”时要考虑以下这些问题:•要留出余地。例如:用get、set函数,不要直接读写成员。这样你以后改内部的实现时(例如数组改成vector),修改的范围仅限于类内部,只要get、set函数相应修改,类就能正常工作•要归纳出共同的行为。例如:家具的整体“移动”与单独“移动”•要避免可能出现的错误。例如:矩阵尺寸不对、指针所有权不明确性能问题•C++既具有面向对象的高层抽象能力,也具有C语言底层操作的能力•但正是由于C++太自由,导致初学者无所适从,往往写出低效的代码•下面介绍一些提高性能的小技巧:1.指针操作2.位运算与枚举定义3.联合与结构4.减少使用不必要的高级数据类型5.减少内存分配释放6.先进行简单的优化,后进行复杂的优化指针操作•C++的指针可以灵活地访问数据•对大量小数据做相同操作时,用指针。例如:自己实现图像模糊•性能要求较高的场合,优先使用指针而不是索引。例如:树遍历的过程中,如果节点直接存下级节点指针,比存索引要快•利用指针减少数据复制。一个例子:用链表实现聚类算法的分裂,通过修改指针的指向,避免数据复制•利用指针实现数据含义的重新解释,例如:unsigned解释成char[4](必要时考虑联合)
本文标题:c++编程经验.
链接地址:https://www.777doc.com/doc-2901823 .html