黑马程序员C语言教程:一定要知道的C++条款之前十个_第1页
黑马程序员C语言教程:一定要知道的C++条款之前十个_第2页
黑马程序员C语言教程:一定要知道的C++条款之前十个_第3页
黑马程序员C语言教程:一定要知道的C++条款之前十个_第4页
黑马程序员C语言教程:一定要知道的C++条款之前十个_第5页
已阅读5页,还剩7页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

条款01:视C++为一个语言联邦为了更好的理解C++,我们将C++分解为四个主要次语言:.C。说到底C++仍是以C为基础。区块,语句,预处理器,内置数据类型,数组,指针统统来自Co.Object-OreintedC++。这一部分是面向对象设计之古典守则在C++上的最直接实施。类,封装,继承,多态,virtual函数等等....TemplateC++。这是C+十泛型编程部分。.STL。STL是个template程序库。容器(containers),迭代器(iterators),算法(algorithms)以及函数对象(functionobjects)...请记住:.这四个次语言,当你从某个次语言切换到另一个,导致高效编程守则要求你改变策略。C++高效编程守则视状况而变化,取决于你使用C++的哪一部分。条款02:尽量以const,enum,inline替换#~6件门3这个条款或许可以改为“宁可以编译器替换预处理器”。即尽量少用预处理。编译过程:.c文件预处理>.i文件编译-—>.o文件一-链接>bin文件预处理过程扫描源代码,对其进行初步的转换,产生新的源代码提供给编译器。检查包含预处理指令的语句和宏定义,并对源代码进行相应的转换。预处理过程还会删除程序中的注释和多余的空白字符。可见预处理过程先于编译器对源代码进行处理。预处理指令是以#号开头的代码行。例:#defineASPECT_RATIO1.653记号名称ASPECT_RATIO也许从未被编译器看见,也许在编译器开始处理源代码之前它就被预处理器移走了。即编译源代码时ASPECT_RATIO已被1.653取代。ASPECT_RATIO可能并未进入记号表(symboltable)。替换:constdoubleAspectRatio=1.653;好处应该有:多了类型检查,因为#define只是单纯的替换,而这种替换在目标码中可能出现多份1.653;改用常量绝不会出现相同情况。常量替换#~6而6两点注意:.定义常量指针:constchar*authorName=“Shenzi”;cosntstd::stringauthorName("Shenzi");.类专属常量:staticconstintNumTurns=5;//static静态常量所有的对象只有一份拷贝。万一你编译器不允许“static整数型class常量”完成“incalss初值设定”(即在类的声明中设定静态整形的初值),我们可以通过枚举类型予以补偿:enum{NumTurns=5};*取一个const的地址是合法的,但取一个enum的地址就不合法,而取一个#define的地址通常也不合法。如果你不想让别人获取一个pointer或reference指向你的某个整数常量,enum可以帮助你实现这个约束。例:#defineCALL_WITH_MAX(a,b)f((a)>(b))?(a):(b))宏看起来像函数,但不会招致函数调用带来的额外开销,而是一种简单的替换。替换:template<typenameT>inlinevoidcallWithMax(cosntT&a,cosntT&b){f(a>b?a:b);}callWithMax是个真正的函数,它遵循作用于和访问规则。请记住:.对于单纯常量,最好以const对象或enums替换#defines;.对于形似函数的宏,最好改用inline函数替换#~6即6$。条款03:尽可能使用constconst允许你告诉编译器和其他程序员某值应保持不变,只要“某值”确实是不该被改变的,那就该确实说出来。关键字const多才多艺:例:chargreeting[]="Hello";char*p=greeting;//指针p及所指的字符串都可改变;constchar*p=greeting;〃指针p本身可以改变,如p=&Anyother;p所指的字符串不可改变;char*cosntp=greeting;〃指针p不可改变,所指对象可改变;constchar*constp=greeting;//指针p及所致对象都不可改变;说明:.如果关键字const出现在星号左边,表示被指物事常量。constchar*p和charconst*p两种写法意义一样,都说明所致对象为常量;.如果关键字const出现在星号右边,表示指针自身是常量。STL例子:conststd::vector<int>::interatoriter=vec.begin();〃作用像T*const,++iter错误:iter是conststd::vector<int>::const_iteratorcIter=vec.begin();〃作用像constT*,*cIter=10错误:*cIter是const以下几点注意:•令函数返回一个常量值,往往可以降低因客户错误而造成的意外,而不至于放弃安全性和高效性。例:constRationaloperator*(constRational&lhs,cosntRational&rhs);•const成员函数使class接口比较容易被理解,它们使“操作const对象”称为可能;说明:声明为const的成员函数,不可改变non-static成员变量,在成员变量声明之前添加mutable可让其在const成员函数中可被改变。const_cast<char&>(static_cast<constTextBlock&>(*this))[position];〃static_cast将TextBlock&转为constTextBlock&;〃const_cast将返回值去掉const约束;请记住:•将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体;•编译器强制实施bitwiseconstness,但你编写程序时应该使用“概念上的车辆"(conceptualconstness);.当cosnt和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。条款04:确定对象被使用前已先被初始化永远在使用对象之前先将它初始化。对于无任何成员的内置类型,你必须手工完成此事。至于内置类型以外的任何其它东西,初始化责任落在构造函数身上,确保每一个构造函数都将对象的每一个成员初始化。赋值和初始化:C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。所以应将成员变量的初始化置于构造函数的初始化列表中。ABEntry::ABEntry(conststd::string&name,conststd::string&address,conststd::list<PhoneNumber>&phones){theName=name;//这些都是赋值,而非初始化theAddress=address;//这些成员变量在进入函数体之前已调用默认构造函数,接着又调用赋值函数,thePhones=phones;//即要经过两次的函数调用。numTimesConsulted=0;}ABEntry::ABEntry(conststd::string&name,conststd::string&address,conststd::list<PhoneNumber>&phones):theName(name),//这些才是初始化theAddress(address),//这些成员变量只用相应的值进行拷贝构造函数,所以通常效率更高。thePhones(phones),numTimesConsulted(0){}所以,对于非内置类型变量的初始化应在初始化列表中完成,以提高效率。而对于内置类型对象,如numTimesConsulted(int),其初始化和赋值的成本相同,但为了一致性最好也通过成员初始化表来初始化。如果成员变量时const或reference,它们就一定需要初值,不能被赋值。C++有着十分固定的“成员初始化次序”。基类总是在派生类之前被初始化,而类的成员变量总是以其说明次序被初始化。所以:当在成员初始化列表中列各成员时,最好总是以其声明次序为次序。请记住:.为内置对象进行手工初始化,因为C++不保证初始化它们;.构造函数最好使用成员初始化列表,而不要在构造函数本体内使用赋值操作。初始化列表列出的成员变量,其排列次序应该和它们在类中的声明次序相同;.为免除“跨编译单元之初始化次序”问题,请以localstatic对象替换non-localstatic对象。二.构造/析构/赋值运算几乎你写的每个类都会有一或多个构造函数、一个析构函数、一个拷贝赋值操作符。如果这些函数犯错,会导致深远且令人不愉快的后果,遍及整个类。所以确保它们行为正确时生死攸关的大事。条款05:了解C++默默编写并调用哪些函数如果你自己美声明,编译器就会为类声明(编译器版本的)一个拷贝构造函数,一个拷贝赋值操作符和一个析构函数。此外如果你没有声明任何构造函数,编译器也会成为你声明一个默认构造函数。所有这些函数都是public且inline。惟有当这些函数被需要(被调用),它们才会被编译器创建出来。即有需求,编译器才会创建它们。默认构造函数和析构函数主要是给编译器一个地方用来放置“藏身幕后”的代码,像是调用基类和非静态成员变量的构造函数和析构函数(要不然它们该在哪里被调用呢??)。注意:编译器产生的析构函数是个non-virtual,除非这个类的基类自身声明有virtual析构函数。至于拷贝构造函数和拷贝赋值操作符,编译器创建的版本只是单纯地将来源对象的每一个非静态成员变量拷贝到目标对象。如一个类声明了一个构造函数(无论有没参数),编译器就不再为它创建默认构造函数。编译器生成的拷贝赋值操作符:对于成员变量中有指针,引用,常量类型,我们都应考虑建立自己“合适”的拷贝赋值操作符。因为指向同块内存的指针是个潜在危险,引用不可改变,常量不可改变。请记住:.编译器可以暗自为类创建默认构造函数、拷贝构造函数、拷贝赋值操作符,以及析构函数。条款06:若不想使用编译器自动生成的函数,就该明确拒绝通常如果你不希望类支持某一特定技能,只要不说明对应函数就是了但这个策略对拷贝构造函数和拷贝赋值操作符却不起作用。因为编译器会“自作多情”的声明它们,并在需要的时候调用它们。由于编译器产生的函数都是public类型,因此可以将拷贝构造函数或拷贝赋值操作符声明为private。通过这个小“伎俩”可以阻止人们在外部调用它,但是类中的成员函数和友元函数还是可以调用private函数。解决方法可能是在一个专门为了阻止拷贝动作而设计的基类。(Boost提供的那个类名为noncopyable)。请记住:.为驳回编译器自动(暗自)提供的机能,可将相应的成员函数声明为private并且不予实现。使用像noncopyable这样的基类也是一种做法。条款07:为多态基类声明virtual析构函数当基类的指针指向派生类的对象的时候,当我们使用完,对其调用delete的时候,其结果将是未有定义--基类成分通常会被销毁,而派生类的充分可能还留在堆里。这可是形成资源泄漏、败坏之数据结构、在调试器上消费许多时间。消除以上问题的做法很简单:给基类一个virtual析构函数。此后删除派生类对象就会如你想要的那般。任何类只要带有virtual函数都几乎确定应该也有一个virtual析构函数。如果一个类不含virtual函数,通常表示它并不意图被用做一个基类,当类不企图被当做基类的时候,令其析构函数为virtual往往是个馊主意。因为实现virtual函数,需要额外的开销(指向虚函数表的指针vptr)。STL容器都不带virtual析构函数,所以最好别派生它们。请记住:.带有多态性质的基类应该声明一个virtual析构函数。如果一个类带有任何virtual函数,它就应该拥有一个virtual析构函数。.一个类的设计目的不是作为基类使用,或不是为了具备多态性,就不该声明virtual析构函数。条款08:别让异常逃离析构函数C++并不禁止析构函数吐出异常,但它不鼓励你这样做。C++不喜欢析构函数吐出异常。如果可能导致异常:.如果抛出异常,就结束程序。(强迫结束程序是个合理选项,毕竟它可以阻止异常从析构函数传播出去。).捕获异常,但什么也不做。如果某个操作可能在失败时抛出异常,而又存在某种需要必须处理该异常,那么这个异常必须来自析构函数以外的某个函数。请记住:•析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序。•如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么类应该提供一个普通函数(而非在析构函数中)执行该操作。条款09:决不让构造和析构过程中调用virtual函数你不该在构造函数和析构函数中调用virtual函数,因为这样的调用不会带来你预想的结果。因为:基类的构造函数的执行要早于派生类的构造函数,当基类的构造函数执行时,派生类的成员变量尚未初始化。派生类的成员变量没初始化,即为指向虚函数表的指针vptr没被初始化又怎么去调用派生类的virtual函数呢?析构函数也相同,派生类先于基类被析构,又如何去找派生类相应的虚函数?唯一好的做法是:确定你的构造函数和析构函数都没有调用虚函数,而它们调用的所有函数也不应该调用虚函数。解决的方法可能是:既然你无法使用虚函数从基类向下调用,那么我们可以使派生类将必要的构造信息向上传递至基类构造函数。即在派生类的构造函数的成员初始化列表中显示调用相应基类构造函数,并传入所需

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论