![](https://img-blog.csdnimg.cn/20201014180756916.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
《Effective C++》
最近开始看C++经典书籍《Effective C++》,此栏用于记录。
China_JerryYuan
这个作者很懒,什么都没留下…
展开
-
条款43:学会处理模板化基类内的名称
1、问题引入假如我们需要这样一个程序:它能够传递不同的信息到不同的公司去。所需要传递的信息分为两类:加密信息、未加密的文本信息。针对上述需求我们可以采用template的解法:首先写多个公司类,类中有发送加密信息和未加密信息的方法。其次写一个信息发送类模板,其模板参数为不同的公司类。这个类用于根据模板参数向不同的公司传递信息。2、问题升级上述解法可以解决我们所提出的需求。但是现在,...原创 2020-02-16 21:47:02 · 172 阅读 · 0 评论 -
条款41:了解隐式接口和编译期多态
1、显式接口和运行期多态(1)简介面向对象编程总是以显式接口和运行期多态来解决问题。例如:class Widget{ public: Widget(); virtual ~Widget(); virtual std::size_t size() const; virtual void normalize(); virtual swap(Wi...原创 2020-01-27 17:23:35 · 237 阅读 · 0 评论 -
条款30:透彻了解inlining的里里外外
1、inline函数的优点看起来像函数动作像函数比宏要好没有函数调用所需要的开销可以享受编译器语境的优化(一般编译器不会对outlined函数调用执行最优化 )2、inline的函数缺点可能造成程序体积较大,即使拥有虚拟内存,inling的代码膨胀也会导致额外的换页行为,降低高速缓存装置的击中率,以及伴随而来的效率损失。3、关于inline的一些规则总规则:inline只是对...原创 2020-01-07 23:02:42 · 236 阅读 · 0 评论 -
条款31:将文件间的编译依存关系降至最低
1、文件间依存度高的话带来的影响?假如你修改了C++ class实现文件,修改的仅仅是实现,而没有修改接口,而且只修改private部分。此时,重新构建这个程序时,会发现整个文件、以及用到该class 的文件都被会被重新编译和连接,这不是我们想要看到的。2、出现上述问题的原因问题出在C++没有把关于接口与实现相分离这件事做好。C++ 的class 的定义式中不仅定义了接口,还定义了实现细目(...原创 2020-01-08 22:10:54 · 194 阅读 · 0 评论 -
0 导读
1、本书的目的告诉你如何高效运用C++,使你的软件易理解、易维护、可移植、可扩充、高效、并且有着你预期的行为。如果任何时间都遵守每一条准则,不太可能掉入C++常见的陷阱中。2、一些术语声明式:告诉编译器某个东西的名称和类型,但是略去细节。(例如:对象、函数、类、模板声明式)签名式:指的是函数的声明式,包含函数参数列表和返回值类型。定义式:提供声明式所遗漏的细节。于对象,为对象拨发内存。...原创 2019-12-03 21:11:40 · 68 阅读 · 0 评论 -
条款01:视C++为一个语言联邦
C++由C发展而来,渐渐支持了更多新特性。可以从四个角度去学习它。C(结构化编程)面向对象编程(封装,继承,多态,虚函数等等)泛型编程(TMP,模板元编程)学习模板库(STL)...原创 2019-12-03 21:22:15 · 575 阅读 · 0 评论 -
条款02:尽量以const,enum,inline替换#define(宁可编译器替代预处理器)
1、使用const定义常量与#define定义常量的对比(1)使用#define定义的常量只是简单的替换,并不编入符号表。简单替换导致该数据在内存中多份存在,浪费内存。当由于该常量导致程序错误时,只显示数值,不显示符号。(正是因为没有编入符号表)。因此排查错误变得非常困难。(2)使用const定义常量常量被编入符号表,该数据仅有一份,节约内存。排错时,以符号名称为提示,相对检查...原创 2019-12-03 22:12:45 · 171 阅读 · 0 评论 -
条款03:尽可能使用const
1、为什么尽可能使用const?const指定语义约束,即某个对象不应该被改变,并且编译器会强制执行这条约束。如果某个对象确实不应该被改变,那么我们就应该将其生命为const,这样一来,如果违背了这个约束,编译器将帮助我们检测出来。2、界定常量指针和指向常量的指针const Type * pt;//(1)Type const *pt;//(2)Type* const pt;//(3)...原创 2019-12-04 19:25:21 · 114 阅读 · 0 评论 -
详细解读《Effective C++》条款04 :确定对象被使用前先被初始化
1、为什么要确定对象被使用前先被初始化?在不同的语境中,同一个对象,可能被编译器给默认初始化,可能编译器没有执行默认初始化。使用没有被初始化的代码就可能导致“不确定行为”。我们虽然有关于什么时候对象被编译器初始化,什么时候不被编译器初始化的规则,但是这些规则比较复杂,记忆起来比较困难。这些规则总结起来就是:c part of c++ 部分,如果初始化的话可能招致运行期成本,因此不保证初始化。...原创 2019-12-05 22:29:54 · 211 阅读 · 0 评论 -
条款05:了解C++默默编写并调用哪些函数
1、编译器可能默默生成哪些函数及特点(1)编译器可能默认生成的函数默认构造函数(无参构造函数)copy构造函数(赋值构造函数)copy赋值函数(等号运算符符号函数)析构函数(2)这些函数的特点都是public(公共)且是inline(内联)的只有这些函数被调用的话,编译器才会生成2、什么情况下编译器会默默生成上述每一种函数?(1)默认构造函数如果一个类没有写任何构造...原创 2019-12-09 10:18:02 · 110 阅读 · 0 评论 -
条款 06:若不想使用编译器自动生成的函数,就该明确拒绝
1、事情的起因有些类的对象应该是独一无二的,不应该进行对象之间的赋值,也不应该使用复制构造函数在创建对象时进行初始化。但是,如果我们不写赋值符号函数、赋值构造函数的话,编译器就会为我们自动生成这样的函数。而我们如果写了这样的函数,又违背了类的本意。那么我们该如何处理这种情况呢?也就是如何明确的拒绝使用编译器自动生成的函数呢?2、问题的解决方案(1)将赋值符号函数或复制构造函数定义为priva...原创 2019-12-11 21:46:34 · 102 阅读 · 0 评论 -
条款07:为多态基类声明virtual析构函数
1、为什么要给多态基类声明virtual析构函数?考虑一个场景:有一个基类指针指向了一个派生类对象,而这个派生类对象创建在堆内存上。当通过delete关键字回收这块内存时,delete的是基类的指针。此时,就会发生一种怪异的现象:该派生类的基类部分被回收了,但是派生类部分没有被回收。即:导致了内存的泄露。实际上,此时只执行了基类的析构函数,没有执行派生类的析构函数。因此,即使该对象没有在堆内存上...原创 2019-12-16 21:09:16 · 194 阅读 · 0 评论 -
条款09:绝不在构造和析构过程中调用virtual函数
1、为什么不能在构造函数中调用virtual函数?(1)考虑下面的场景:class Transaction { //所有交易的base classpublic: Transaction(); virtual void logTransaction() const = 0; //做出一份因为类型不同而不同的日志记录,目前是一个纯虚函数 ...};...原创 2019-12-17 20:38:45 · 275 阅读 · 0 评论 -
条款10:令赋值运算符(opeator=)返回一个reference to * this
1、为什么令赋值运算符(opeator=)返回一个reference to * this?(1)首先这是一个协议,cpp内置类型都遵循这个协议。(2)其次,他有一定的意义。设计成返回reference to * this,可以完成连等的需求。2、不仅是operator=,包括其它赋值符号(+=,-=,*=等等)也应该遵循上述约定...原创 2019-12-16 21:18:57 · 185 阅读 · 0 评论 -
条款11: 在operator=中处理“自我赋值”
1、什么是自我赋值?(1)情况一:最简单的自我赋值class Widget { ... };Widget w;...w = w; //赋值给自己(2)情况二:复杂一些的自我赋值a[i] = a[j]; //潜在的自我赋值如果i和j具有相同的值时,这就是一个自我赋值。*px = *py; //潜在的自我赋值如果指针px和py恰巧指向同一个东西,这也是...原创 2019-12-17 21:49:57 · 578 阅读 · 0 评论 -
条款12:复制对象时勿忘其每一部分
1、所谓copying函数是指:复制构造函数赋值符号函数二者统称为:copying函数。2、两个函数的特点一个类中一定会有这两个函数,如果没有手动写,那么编译器则会自动生成缺省的这两个函数。如果手动写的话,则需要引起一定的重视,即:复制对象时勿忘其每一部分。3、被遗漏的部分通常指的是什么情况下,哪里被遗漏?情况一:为一个类添加新的成员变量时。此时,必须更新所有的构造函数,使新添...原创 2019-12-18 21:27:51 · 70 阅读 · 0 评论 -
条款13:以对象管理资源
1、常见的需要管理的资源有哪些?最常用的资源:内存文件描述器互斥锁图形界面中的字型和笔刷数据库链接网络sockets2、传统的管理资源的方法的缺点考虑以下情况: class Investment{...}; //"投资类型",继承体系中的root class 假设这个程序库通过一个工厂函数提供我们特定的Investment对象 Inv...原创 2019-12-28 20:52:12 · 74 阅读 · 0 评论 -
条款14:在资源管理类中小心copying行为
1、为什么需要自定义资源管理类?条款13里讲了两种智能指针对象,它们都是用于管理动态内存的。然而,它们并不适用于管理别的资源,比如说互斥器。因此,有些时候我们需要自定义自己的资源管理类。2、自定义资源管理类需要考虑的一个问题:copying 问题copying包括:copy 构造函数、copy赋值符号函数。在自定义资源管理类时,结合不同的问题(不同场景),处理copying 函数可能采取下述...原创 2019-12-28 22:30:55 · 127 阅读 · 0 评论 -
条款 15:在资源管理类中提供对原始资源的访问
1、为什么要在资源管理类中提供对原始资源的访问?使用资源管理类来管理资源,使得我们不必直接处理资源,而交由类来管理,这很好。但是,有些API需要直接访问资源,你不能给它传递一个这样的资源管理类对象。这就使得我们的资源管理类能够解开类的封装,能够做到直接访问资源。2、 如何做到直接资源访问、获取?需要一个成员函数,将RAII class对象转换为其所内含的原始资源,可以通过两种途径:(1)显...原创 2019-12-29 10:35:03 · 102 阅读 · 1 评论 -
条款16:成对使用 new 和delete时要采用相同形式
1、使用new和delete时有哪些形式?new和delete分别有两种形式:(1)对象数组:type*x = new array[];delete [] array;(2)单一对象:type*x = new singleObject;delete singleObject;2、使用new和delete分别做了什么?(1)使用new时,有两件事发生:内存被分配出来(通过 ...原创 2019-12-29 19:29:09 · 122 阅读 · 0 评论 -
条款17:以独立语句将newed 对象置入智能指针
1、为什么要以独立语句将newed 对象置入智能指针?考虑以下场景:假设我们有个函数用来显示处理函数的优先权,另一个函数(processWidget())用来在某动态分配所得的Widget上进行某些带有优先权的处理,函数接口如下:int priority();void processWidget(std::tr1::shared_ptr<Widget> pw, int prio...原创 2019-12-29 20:34:43 · 169 阅读 · 0 评论 -
条款18: 让接口容易被正确使用,不易被误用
1、理想上,如果客户企图使用某个接口而却没有获得他所预期的行为,这个代码不该通过编译;如果代码通过了编译,它的作为就该是客服所想要的。想要开发出来一个“容易被正确使用,不易被误用”的接口,首先必须考虑客户可能做出什么样的错误。预防客户端错误的手段:(1)导入新类型。确定参数类型,使得接口的参数类型不会发生错误。(2)限定参数的取值范围以函数代替对象。enum(不好,能作为int访问...原创 2019-12-29 22:46:06 · 151 阅读 · 0 评论 -
条款19:设计class犹如设计type
C++就像在其他OOP(面向对象编程语言)一样,当你定义一个新class,也就 定义了一个新type。身为C++程序员,你的许多时间主要用来扩张你的类型系统(type system)。这意味你并不只是class设计者,还是type设计者。重载(overloading)函数和操作符、控制内存的分配和归还、定义对象的初始化和终结…全都在你手上。因此你应该带着和“语言设计者当初设计语言内置类型时”一样的...原创 2019-12-30 09:29:51 · 163 阅读 · 2 评论 -
条款20: 宁以pass-by-reference-to-const 替换 pass-by-value
1、为什么要宁以pass-by-reference-to-const 替换 pass-by-value效率方面缺省情况下,C++以by value 方式传递对象至(或来自)函数。除非你另外指定,否则函数参数都是以实际实参的副本为初值,而调用段所获得的亦是函数返回值的一个副本。这些副本都是由对象的copy构造函数产出的,这可能使得pass-by-value 称为昂贵的(费时的)操作。考虑下述...原创 2019-12-31 10:14:43 · 160 阅读 · 0 评论 -
条款21:必须返回对象时,别妄想返回其reference
1、关于reference所谓reference 只是个名称,代表某个既有对象。任何时候看到一个reference声明式,你都应该立刻问自己,它的另一个名称是什么?因为它一定是某物的另一个名称。2、通过引用 避免构造函数和析构函数被调用,从而节约成本的几个不切实际的想法(1)在stack空间创建在函数体内的stack空间创建变量,创建的就是一个local变量,返回时,如果返回referen...原创 2019-12-31 10:59:27 · 135 阅读 · 0 评论 -
条款22:将成员变量声明为private
为什么要将成员变量声明为private?角度一:语法一致性将所有成员变量都声明private,剩下的成员就是成员函数了。那么外部访问时都得要带括号,因为只可以访问成员函数。角度二:使用函数可以让你对成员变量的处理有更精确的控制。如果令成员变量为public,每个人都可以读写它,但是如果你以函数取得或设定其值,你就可以实现出“不可访问”、“只读访问”、“读写访问”。角度三:封装将成员...原创 2019-12-31 11:40:41 · 230 阅读 · 0 评论 -
条款23:宁以non-member、non-friend替换member函数
1、问题引出(1)假设有一个WebBrower类,代表浏览器,其中有的成员方法有:清除缓存清除url清除cookiesclass WebBrower{public: void ClearCach(); void ClearHistory(); void RemoveCookies();};(2)现在有一个需求:把这三个同时同时清除。但是我们实现上述需...原创 2020-01-01 16:29:04 · 384 阅读 · 0 评论 -
条款24:若所有参数皆需类型转换,请为此采用non-member函数
1、问题引出通常,令class支持隐式转换是不好的设计。但是也有例外,最常见的例外是在建立数值类型时。例如设计一个类表示有理数时,允许整数隐式转换为有理数是合理的。class Rational{public: Rational(int numerator = 0, int denominator = 1); //刻意不为explicit;允许int-to-Rational隐式转换 ...原创 2020-01-01 19:13:00 · 204 阅读 · 0 评论 -
条款25: 考虑写出一个不抛异常的swap函数
1、内容引出swap()属于STL的一部分(算法),而后成为异常安全性编程的脊柱,以及用来处理自我赋值的可能性的一个常见的机制。它是如此的有用,适当的实现就显得十分重要。然而在它的实现的复杂度也比较高。这个条款就在讲swap()函数的实现问题。2、 widget 和widgetImpl 都是class时,如何写出高效的swap()STL 缺省情况下,由STL提供的swap算法完成,如下:n...原创 2020-01-02 10:38:01 · 225 阅读 · 1 评论 -
条款26:尽可能延后变量定义式的出现时间
1、为什么尽可能延后变量定义式的出现时间?主要是效率上的考虑。直到必须使用变量的时候才对其进行定义,这样可以减定义对象时候需要的构造函数和销毁变量时析构函数的开销。(定义了变量不一定会用)有初值实参的时候再进行定义变量,比直接使用默认构造函数再赋值的效率要高。考虑下述代码:void encrypt(std::string &s){ //具体加密操作;}std:...原创 2020-01-02 11:29:46 · 127 阅读 · 0 评论 -
条款27:尽量少做转型动作
1、关于C++的转型动作C++的设计目标之一是,保证“类型错误”绝对不可能发生。理论上如果你的程序很“干净地”通过编译,就表示它并不企图在任何对象身上执行任何不安全、无意义、愚蠢荒谬的操作。这是一个极具价值的保证,可别草率的放弃它。不幸的是,转型破坏了类型系统。那可能导致任何种类的麻烦,有的容易辨识,有些非常隐晦。C、java、c#语言中可能转型是必要的、无法避免的,相比于C++也比较不那么危...原创 2020-01-02 15:43:38 · 181 阅读 · 0 评论 -
条款28:避免返回handles 指向对象内部成分
1、什么是handles?2、返回handles 指向对象内部成分 可能带来的问题一:自相矛盾3、改进版 返回const handles 指向对象内部成分 可能带来的问题:解决了子项矛盾,却可能形成虚吊问题4、最终的结论5、本条款的例外情况...原创 2020-01-07 21:09:01 · 197 阅读 · 0 评论 -
条款32:确定你的public继承塑模出is-a关系
1、何谓is-a关系?比如说两个类A和B,假如B公开继承自A,那么就意味着:B一定属于类A,但是A却不一定属于B。一个简单的例子,假如A是塑模的是人,而B塑模的是学生。那么一般来讲:学生一定属于人,人却不一定属于学生。2、编程时,is-a关系可能是违背由现实生活得来的经验直觉的例子1:企鹅与鸟现实生活中的经验:企鹅是一种鸟,但是编程时却不能将二者塑模成is-a 关系。因为一般而言鸟都是会...原创 2019-12-18 22:28:20 · 129 阅读 · 0 评论 -
条款33:避免遮掩继承而来的名称
1、C++的名称遮掩规则名称遮掩规则做的事就是:遮掩名称。假如这个名称是一个变量,那么无论这两个变量的类型是否相同,都会被遮掩。假如这个名称是一个函数,那么无论函数的参数有几个,无论这个同名的函数有几个重载的版本,这些函数全都会被遮掩。2、派生类的成员函数内查找名称的顺序先在成员函数体内查找(local作用域)。找不到的情况下,在派生类内查找。找不到的情况下,在派生类所继承的基...原创 2019-12-19 10:11:54 · 145 阅读 · 0 评论 -
条款34:区分接口继承和实现继承
1、public继承细分实际上细分为:函数接口继承和函数实现继承。这两种细分更像是函数声明和函数定义之间的差异。从这两个角度出发,public继承可以分为:只继承接口同时继承接口和实现,且继承而来的实现能够被覆写同时继承接口和实现,且继承而来的实现不能够被覆写2、三种继承对应的成员函数的写法(1)只继承接口对应的成员函数的写法为:pure virtual。特点:此类继承派生...原创 2019-12-19 16:05:43 · 145 阅读 · 0 评论 -
条款35:考虑virtual函数以外的其他选择
1、考虑virtual函数以外的其他选择是什么意思?大多时候,我们会自然而然的想到使用virtual手法来塑模现实中的类。但是,实际上也有别的方案可以替代virtual手法的,即:考虑virtual函数以外的其他选择。下面介绍的便是几种可以替代virtual的方案。2、第一种方案:通过Non-Virtual Interface (NVI)手法实现Template Method 模式(1)No...原创 2019-12-23 10:55:43 · 249 阅读 · 0 评论 -
条款36:绝不重新定义继承而来的non-virtual函数
1、为什么要绝不重新定义继承而来的non-virtual函数?考虑下述情况://情况一class B{public: void mf(); ...};class D:public B{...};D x;B *pB = &x;pB -> mf();//经由该指针调用 B::mfD *pD = &x;pD -> mf();//经由该指...原创 2019-12-23 11:49:09 · 205 阅读 · 0 评论 -
条款37:绝不重新定义继承而来的缺省参数值
1、绝不重新定义继承而来的缺省参数值所牵涉的范围?继承的函数分为两种:non-virtual 和virtual。而non-virtual函数是完全不能重新定义的(函数体、缺省参数都不可以),因此我们所说的绝不重新定义继承而来的缺省参数值实际指的是virtual函数。2、为什么不能重新定义继承而来的缺省参数值?因为virtual 函数体是动态绑定的,但是virtual 函数的缺省参数是静态绑定...原创 2019-12-24 10:34:25 · 158 阅读 · 0 评论 -
条款38:通过复合塑模出has-a 关系或 is-impemented-in-terms-of关系
1、什么是复合?复合是类型之间的一种关系,当某种类型的对象包含其它类型对象时,便是这种关系。2、复合描述的关系的细分(1)复合意味着两种关系has-a 关系is-impemented-in-terms-of关系(2)细分依据区分这两种关系的依据:根据程序处理的不同领域。(3)应用域和实现域应用域:客观的,描述现实世界的东西的类。(比如:一辆汽车,一条狗)。应用域类型之间的复...原创 2019-12-24 11:14:26 · 177 阅读 · 0 评论 -
条款 39:明智而审慎地使用private继承
priority_queue原创 2019-12-24 15:09:01 · 249 阅读 · 0 评论