`

Effective C++--阅读笔记(原创)

c++ 
阅读更多

由  王宇 原创并发布

 

1章、让自己习惯C++

    条款01:视C++为一个语言联邦

        C++高效编程守则视状况而变化,取决于你使用C++的那一部分

    条款02:尽量以const,enum,inline替换#define

        对于单纯常量,最好以const对象或enums替换#define

    条款03:尽可能使用const

        将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。

        编译器强制实施bitwiseconstness(二进制常量),但你编写程序时应该使用“概念上的常量性(conceptualconstnes)”   

            成员函数如果是const意味什么?这有两个流行概念:bitwistconstness和logicalconstness

            bitwistconstness认为const成员函数不可以更改对象内任何non-static成员变量

            logicalconstness认为一个const成员函数可以修改它所处理的对象内的某些bit,但是有在客户端侦测不出的情况下才得如此:具体方法是在对象内定义的变量前叫mutalbe

        当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复

    条款04:确定对象被使用前已先被初始化

        **为内置型对象进行手工初始化,因为C++不保证初始化它们。

            classfuns{inti=0;}(inti;这种情况下i不保证被初始化)

        构造函数最好使用成员初始化值列(memberinitializationlist),而不要在构造函数本体内使用赋值操作,初值列列出的成员变量,其排列次序应该和它们在class中的声明次序相同

            高效

            必要性

        为免除“跨编译单元之初始化次序”问题,请以localstatic对象替换non-localstatic对象

            编译单元:指产出单一目标文件的源码

            localstatic:函数内的static对象,其他static对象称为:non-localstatic对象

            C++对“定义于不同的编译单元内的non-localstatic对象”的初始化相对次序并无明确定义。原因:决定它们的初始化次序相当困难,非常困难,根本无解

       

2章、构造,析构,赋值运算

    条款05:理解C++默默编写并调用哪些函数


        编译器可以暗自为class创建default构造函数、copy构造函数、copyassignment操作

    条款06:若不想使用编译器自动生成的函数,就该明确拒绝

        为驳回编译器自动(暗自)提供的机能,可将相应的成员函数声明为private并且不予实现。使用像Uncopyable这样baseclass也是一种做法

    条款07:为多态基类声明virtual析构函数

        polymorphic(多态)baseclasses应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数

            原因是将析构降级到派生实现类中。

        Class的设计目的如果不是作为baseclasses使用,或不是为了具备多态性,就不该声明virtual析构函数

            编译器在其中寻找适当的函数指针

            其对象的体积会增加

            不再具有移植性

            purevirtual析构函数,导致abstract类,不可实例化:virtual~AWOV()=0;

    条款08:别让异常逃离析构函数***

        析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们或结束程序    原因是:当一组实例放在vector中,有可能造成资源泄露

            如果程序遭遇一个“于析构期间发生的错误”后无法继续执行,“强迫结束程序”是个合理选择

            一般而言,将异常吞掉(记录下来)是个怀主意

            总结以上两种方式都不能很好的解决析构函数中的异常,所以尽可能不在析构中处理异常

            问题:如何处理异常

        如果客户需要某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作

    条款09:决不再构造和析构过程中调用virtual函数

        在构造和析构期间不要调用virtual函数,因为这类调用从不下降到derivedclass(比起当前执行构造函数和析构函数的那层)

    条款10:令operator=返回一个referenceof*this

        令赋值(assignment)=操作符返回一个referenceto*this

    条款11:在operator=中处理“自我赋值”(不理解)

       

    条款12:复制对象时勿忘其每一个成分

        Copying函数应该确保复制"对象内的所有成员变量"及"所有baseclass成分"

        不要尝试以某个copying函数实现另一个copying函数。应该将共同能放进第三个函数中,并由两个copying函数共同调用

3章、资源管理

    条款13:以对象管理资源

        资源:动态分配内存,文件描述器,互斥锁,图形界面中的字型、笔刷、数据库连接、以及网络socket

        为防止资源泄露,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源

            RAII守则:资源在构造期间获得,在析构期间释放

        两个常被使用的RAIIclasses分别是trl::share_ptr和auto_ptr前者通常是较佳选择,因为其copy行为比较直观。若选择auto_ptr,复制动作会使它(被复制物)指向null

            std::auto<Class1>p1(newClass1());

            std::auto<Class2>p2(newClass2());

            p1=p2;//p1指向newClass2,p2为null如果是share_ptr则p1指向newClass2,p2为newClass2,模块解释时,p1p2全部释放

    条款14:在资源管理类中小心copying行为

        复制RAII对象必须一并复制它所管理的资源,所以资源的copying行为决定RAII对象的copying行为

            例如:Mutex,构造期间lock(),析构期间unlock,不是释放资源,所以建立自己的资源管理类

        普通而常见的RAIIclasscopying行为是:抑制copying、施行引用计数法(referencecounting)不过其他行为也都可能被实现

    条款15:在资源管理类中提供对原始资源的访问

        APIs往往要求访问原始资源(rawresources)(指针),所以每一个RAIIclass应该提供一个“取得其所管理之资源”的办法

            原因:share_prt或auto_ptr返回的是对象类型,而不是指针,有时需要指针   

            显式转换:obj.get()

            隐式转换:操作符:operator->operator*

        对原始资源的访问可能经由显式转换或隐式转换。一般而言显式转换比较安全,但隐式转换对客户比较方便

    条款16:成对使用new和delete时要采取相同形式

        如果你在new表达式中使用[],必须在相应的delete表达式中也使用[].如果你在new表达式中不使用[],一定不要在相应的delete表达式中使用[]

    条款17:一独立语句将newed对象置入智能指针

        以独立语句将newed对象存储于(置入)智能指针内。如果不这样做,一旦异常被抛出,有可能导致难以察觉的资源泄露   

            processWidget(std::tr1::shared_ptr<Widget>(newWidget));有可能造成内存泄露,需要分解成两句:

            std::tr1::shared_ptr<Widget>pm(newWidget);

            processWidget(pm);

            原因:对象实例化,shared_ptr的构造,processWidget的调用顺序不确定

4章、设计与声明

    条款18:让接口容易被正确使用,不易被误用

        好的接口很容易被正确使用,不容易被误用。你应该在你的所有接口中努力达成这些性质

        促进正确使用的办法包括接口的一致性,以及与内置类型的行为兼容

        阻止错误的办法包括建立新类型、限制类型上的操作,束缚对象值,以及消除客户的资源管理责任(Don'tunderstand)

        tr1::shared_ptr支持定制型删除器。这可防治DLL问题,可被用来自动解除互斥锁等等(Don'tunderstand)

       

    条款19:设计class犹如设计type

        重载函数和操作符、控制内存的分配和归还、定义对象的初始化和终结...全部在你手上,所以要谨慎来讨论class的设计

        几乎每一个class都要求你面对以下提问:

            新type的对象应该如何被创建和销毁?

            对象的初始化和对象的赋值该有什么样的差别?

            新type的对象如果被passedbyvalue(以值传递),意味着什么?

            什么是新type的合法值

            你的新type需要配合某个继承图系吗?

            你的新type需要什么样的转换?

            什么样的操作符和函数对此新type而言是合理的?

            什么样的标准函数应该驳回?

            谁该取用新type的成员?

            什么是新type的未声明接口?

            你的新type有多么一般化?

            你真的需要一个新type吗?

            以上给容需要反复回顾和推敲。

    条款20:宁以pass-by-reference-to-const替换pass-by-value

        尽量以pass-by-reference-to-const替换pass-by-value前者通常比较高效,并可避免切割问题

            避免构造函数的性能开销

        以上规则并不适用于内置类型,以及STL的迭代器和函数对象,对它们而言,pass-by-value往往比较适当

        需要搞清楚:模板、泛化、迭代

    条款21:必须返回对象时,别妄想返回其reference

        条款20,带来的问题

        绝不要返回pointer或reference指向一个localstack对象,或返回reference指向一个heap-allocated对象,或返回pointer或reference指向一个localstatic对象而有可能同时需要多个这样的对象

            不要轻易在堆栈(静态)中建立对象后,通过函数返回.pass-by-reference要有始有终,最好是穿越而过

    条款22:将成员变量声明为private

        此条的重点是封装性

        切记将成员变量声明为private.这可赋予客户访问数据的一致性、可细微划分访问控制、允诺约束条件获得保证,并提供class作者以充分的实现弹性

        protected并不比public更具封装性

    条款23:宁以non-member(非成员函数)non-friend替换member函数

        宁可拿non-membernon-friend函数替换member函数。这样做可以增加封装性、包括弹性和机能扩展性:原因是non-member不增加“能够访问class内之private成分”的函数数量

            愈多东西被封装,我们改变那些东西的能力也就愈大。这就是我们首先推崇封装的原因:它使我们能够改变事物而只影响有限客户

    条款24:若所有参数皆需类型转换,请为此采用non-member函数

    如果你需要为某个函数的所有参数(包括被this指针所指的那个隐喻参数)进行类型转换,那么这个函数必须是个non-member

    条款25:考虑写出一个不抛异常的swap函数

        39条款以后,在看

5章、实现

    条款26:尽可能延后变量定义式的出现时间

        尽可能延后变量定义式的出现.这样做可增加程序的清晰度并改善程序效率

    条款27:尽量少做转型动作

        如果可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_casts.如果有个设计需要转型动作,试着发展无需转型的替代设计

        如果转型是必要的,试着将它隐藏于某个函数背后。客户随后可以调用该函数,而不需将转型放进他们自己的代码内

        宁可使用C++-style(新式)转型,不要使用旧式转型。前者很容易辨识出来,而且也比较有着分门别类的职掌

        旧式转型(C风格):(T)expression

        新式转换:T(expression)

            const_cast:consttonon-const

            dynamic_cast:安全向下转型

            reinterpret_cast(新解释):低级转换,取决于编译器,很少使用

    **        static_cast:non-consttoconst;inttodouble

    条款28:避免返回handles指向对象内部成分

        避免返回handled(包括references、指针、迭代器),遵守这个条款可增加封装性,帮助const成员函数的行为像个const,并将发生“虚吊号码牌”的可能性降至最低   

    条款29:为“异常安全”而努力是值得的

        异常安全函数即使发生异常也不会泄漏资源或允许任何数据结构败坏。这样的函数区分为三种可能的保证:基本型、强烈型、不抛异常型

        “强烈保证”往往能够以copy-and-swap实现出来,但“强烈保证”并非对所有函数都可实现或具备现实意义

        函数提供的“异常安全保证”通常最高只等于其所调用之各个函数的“异常安全保证”中的最弱者

    条款30:透彻了解inling的里里外外

        将大多数inling限制在小型、被频繁调用的函数身上。这可使日后的调用过程和二进制升级更容易,也可以潜在的代码膨胀问题最小化,使程序的速度提升机会最大化

        不要只因为functiontemplates出现在头文件,就将它们声明为inline

    条款31:将文件间的编译依存关系降至最低

        支持“编译依存性最小”的一般构想是:相依于声明式,不要相依于定义式。基于此构想的两个手段是Handleclass和Interfaceclasses

        程序库头文件应该以“完全且仅有声明式”的形式存在。这种做法不论是否涉及templates都适用

        不理解

6章、继承与面向对象设计

    条款32:确定你的public继承塑模出is-a关系

        "public继承"意味is-a。适用于baseclasses身上的每一件事情一定也适用于derivedclasses身上,因为每一个derivedclass对象也都是一个baseclass对象,反之不成立

    条款33:避免掩盖继承而来的名称

        derivedclasses内的名称会遮掩baseclasses内的名称,在public继承下从来没有人希望如此(在public模式下,不要重载继承函数,这样会违反is-a)

        为了让被遮掩的名称再见天日,可使用using声明式或转交函数

    条款34:区分接口继承而来的名称

        接口继承和实现继承不同。在public继承之下,derivedclasses总是继承baseclass的接口

            public继承概念分为:函数接口继承和函数实现继承

            purevirtual(纯虚)使类成为一个抽象类,必须被任何“继承了它们”的具体对象class重新申明

        purevirtual函数只具体指定接口继承

        简朴(非虚)impurevirtual函数具体指定接口继承及缺省实现继承

        non-virtual函数具体指定接口继承以及强制性实现继承

    条款35:考虑virtual函数以外的其他选择

    条款36:绝不重新定义继承而来的non-virtual函数

        绝对不要重新定义继承而来的non-virtual函数

            违反public继承,is-a原则

            如果必须重新定义,需使用virtual函数

    条款37:绝不重新定义继承而来的缺省

        绝对不要重新定义一个继承而来的缺省参数值,因为缺省参数值都是静态绑定,而virtual函数--你唯一应该覆写的东西--却是动态绑定

            意思是你可能会在“调用一个定义于derivedclass内的virtual函数”的同时,却使用baseclass为它所指定的缺省参数值

    条款38:通过复合塑模出has-a或“根据某物实现出”

        复合的意义和public继承完全不同。复合has-a(有一个,例如person有一个addressandaname)public继承is-a(是一种)

        在应用域,复合意味has-a;在实现域,复合意味根据某物实现出

    条款39:明智而审慎地使用private继承

        private继承意味“根据某物实现出”。它通常比复合的级别低。但是当derivedclass需要访问protectedbaseclass的成员,或需要重新定义继承而来的virtual函数时,这么设计是合理的

            private继承特性:

                编译器不会自动将一个derivedclass对象转换为一个baseclass对象

                继承而来的所有成员,在derivedclass中都会变成private属性,纵使它们在bassclass中原本是protected或public属性

            private继承在软件“设计”层面上没有意义,其意思只及于软件实现层面

        和复合不同,private继承可以造成emptybase最优化,这对致力于“对象尺寸最小化”的程序库开发者而言,可能很重要

    条款40:明智而审慎地使用多重继承

        多重继承比单一继承复杂。它可能导致新的歧义性,以及对virtual继承的需要

        virtual继承会增加大小、速度、初始化复杂度等等成本。如果virtualbaseclasses不带任何数据,将是最具实用价值的情况

        多重继承的确有正当用途,其中一个情节涉及"public继承某个Interfaceclass"和“private继承某个协助实现的class”的两相组合

7章、模板与泛型编程

    条款41:了解隐式接口和编译期多态

        class和template都支持接口和多态

        对class而言接口是显式的,template参数而言,接口是隐式的

    条款42:了解typename的双重意义

        声明template参数时,前缀关键字class和typename可互换

        请使用关键字typename标识嵌套从属类型名称;但不得在baseclasslists(基类列)或memberinitializationlist(成员初值列)内以它作为baseclass修饰符

    条款43:学习处理模板化基类内的名称

        可在derivedclasstemplates内通过"this->"指涉baseclasstemplate内的成员名称。

    条款44:将与参数无关的代码抽离templates

        Template生成多个classes和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依关系

        因非类型模板参数而造成的代码膨胀,往往可消除,做法是以函数参数或class成员变量替代template参数

        因类型参数而造成的代码膨胀,往往可降低,做法是让带有完全相同二进制表述的具体类型共享实现码(不理解)

    条款45:运用成员函数模板接受所有兼容类型(基类模板与派生类模板,实现转换)

        请使用memberfunctiontemplate(成员函数模板)s生成“可接受所有兼容类型”的函数

        如果你声明membertemplates用于“泛化copy构造”或“泛化assignment操作”,你还是需要声明正常的copy构造函数和copyassignment操作符

    条款46:需要类型转换时请为模板定义非成员函数

        当我们编写一个classtemplate,而它所提供之"与此template相关的"函数支持“所有参数之隐式类型转换”时(重载操作符=),请将那些函数定义为“classtemplate”内部的friend函数

    条款47:请使用traitsclass表现类型信息

        Traitsclasses使得“类型相关信息”在编译期可用。它们以template和“templates特化”完成实现

        整合重载技术(overloading)后,traitsclasss有可能在编译期对类型执行if...eles测试

        五种迭代器:input(一次一步读),output(一次一步写),forward(可以读或写一次以上),Bidirectional(可以先前或先后移动),random(在常量时间内,向前或向后移动任意距离)

    条款48:认识template元编程

        Templatemetaprogramming(TMP模板元编程)可将工作由运行期移往编译期,因而得以实现早期错误侦测和更高的执行效率

        TMP可被用来生成“基于政策选择组合”的客户定制代码,也可用来避免生成对某些特殊类型并不适合的代码

8章、定制new和delete

    条款49:了解new-handler的行为

        set_new_handler允许客户指定一个函数,在内存分配无法获得满足时被调用。

        Nothrownew是一个颇为局限的工具,因为它只适用于内存分配;后续的构造函数调用还是可能抛出异常

    条款50:了解new和delete的合理替换时机

        有许多理由需要写个自定的new和delete,包括改善效能、对heap运行错误进行调试、收集heap使用信息

    条款51:编写new和delete时需固守常规

        operatornew应该内含一个无穷循环,并在其中尝试分配内存,如果它无法满足内存需求,就该调用new-handler.它也应该有能力处理0bytes申请。Class专属版本则还应该处理“比正确大小更大的(错误)申请”

        operatordelete应该在收到null指针时不做任何事。Class专属版本则还应该处理“比正确大小更大的(错误)申请”

    条款52:写了placementnew也要写placementdelete          

9章、杂项讨论

    条款53:不要轻忽编译器的警告

    条款54:让自己熟悉包含TR1在内的标准程序库

    条款55:让自己熟悉Boost:http://boost.org

分享到:
评论

相关推荐

    effective c++读书笔记

    《Effective C++》是一本由Scott Meyers所著的关于C++编程的书籍,该书广泛被认为是对C++程序员提高编程水平和解决实际问题提供了极好的指导。从给出的部分内容来看,读书笔记主要聚焦于以下几个知识点: 1. C++...

    Effective C++第三版及详细笔记

    内容简介:有人说C++程序员可以分为两类,读过Effective C++的和没读过的。当您读过《Effective C++:改善程序与设计的55个具体做法(第3版)(中文版)(双色)》之后,就获得了迅速提升自己C++功力的一个契机。

    Effective C++学习笔记

    Effective C++是一本深入探讨C++编程实践的书籍,它提供了许多提高代码质量和效率的建议。以下是基于标题、描述和部分内容的关键知识点: 1. **虚函数的声明与使用**: - 在C++中,虚函数是实现多态性的关键。它们...

    Effective Modern C++学习笔记

    《Effective Modern C++:改善C++11和C++14的42个具体做法(影印版)(英文版)》中包括以下主题:剖析花括号初始化、noexcept规范、完美转发、智能指针make函数的优缺点;讲解std∷move,std∷forward,rvalue引用和全局...

    effective C++ 读书笔记 PPT

    摘录了《Effective C++》 (Scott Meyers 著)中有参考价值的编写代码建议,方面阅读

    Effective C++学习笔记.doc

    自己整理的侯捷版本&lt;&lt;Effective C++ 改善程序与设计的55个具体做法&gt;&gt; 学习笔记.

    effective C++ (3rd) 学习笔记

    通过这些学习笔记,我们可以了解到《Effective C++》不仅为我们提供了C++编程中的一些基本和高级技巧,也教会我们如何遵循最佳实践,编写出更加高效、健壮的C++代码。学习这些知识点,对于提高C++编程能力和解决实际...

    Effective C++笔记1

    Effective C++笔记1 本笔记概括了 Effective C++ 中的四十个规则的第一个部分,涵盖了 C++ 语言的基本概念、const 的使用、inline 函数的应用、对象的初始化等方面。 规则 1:将 C++ 视为语言联合体 -------------...

    effective C++ 读书笔记

    effective C++ 很好 很不错 最好的C++进阶资料

    effective c++笔记1

    Effective C++笔记1 本笔记总结了Effective C++的第1到第11条款,涵盖了C++语言的多个方面,包括构造函数、拷贝构造函数、拷贝赋值函数、const关键字、enum、inline函数、定义域、static变量、初始化、编译器生成的...

    Effective_C++_3rd笔记.pdf

    ### Effective_C++_3rd笔记的关键知识点概览 #### 视C++为一个语言联邦(条款01) C++作为一门复杂且功能丰富的语言,它实际上涵盖了多种编程范型,包括过程式、面向对象、函数式、泛型以及元编程。这种多样性的...

    effective C++读书笔记

    Effective C++读书笔记 本文总结了Effective C++读书笔记,涵盖了C++的四个主要次语言:C、Object-Oriented C++、Template C++和STL。同时,文章还讨论了高效编程守则,包括使用const、enum和inline替换#define,...

    Effective C++ Roy T 读书笔记。

    《Effective C++》是Scott Meyers撰写的一本经典C++编程指南,旨在帮助开发者写出更高效、更可维护的代码。这本书由一系列的编程实践建议组成,每一条建议都被称为一个"项",覆盖了C++语言的多个方面,包括对象、类...

    Effective_C++3 学习笔记.pdf.rar

    通过阅读这份详尽的《Effective C++3 学习笔记》,读者不仅可以深入了解C++语言的高级特性,还能掌握编写高效、安全的C++代码的最佳实践。对于任何渴望提升C++编程技能的开发者来说,这是一份不可或缺的参考资料。

    C++书籍阅读笔记

    其次,"Effective STL"和"more effective c++"这两本书籍的笔记关注的是标准模板库(STL)的高效应用。STL是C++的一个重要组成部分,提供了容器(如vector、list、set)、迭代器、算法和函数对象等工具,极大地提高...

    Effective_C++_笔记

    通常C++要求你对所使用的任何东西提供一个定义式,但如果它是个class专属常量 又是static且为整数类型(integral type,例如int,char,bool),则可特殊处理。只要不取它们 的地址,你可以声明并使用它们而无需提供...

    高级java笔试题-Lookoop:学习笔记

    c++笔记归纳 Data Structures and Algorithm Analysis 数据结构与一些算法,来自算法导论,数据结构与算法分析-C语言描述,C Primer Plus, 数据结构-python描述,博客 ADT链表(c)-抽象链表实现 geometry(c++)-...

Global site tag (gtag.js) - Google Analytics