
Effective C++
文章平均质量分 80
《Effective C++》中介绍了大量实际项目开发过程中的注意事项,书中绝大多数条款在项目中都能用到,当本人看到该书中的条款时感慨万千,相见恨晚,要是早点拜读此书,并应用在项目中,此时,薪资就不止于此了~~
OODeveloper
2015-6~2017-3参与Android项目开发,开发工具Android studio;
2017-3~2019-5参与C++项目开发,开发平台QT;
2019-5~2020-7 参与iOS项目开发,开发语言Object-c,开发环境Xcode。
展开
-
不要轻忽编译器的警告——条款53
许多程序员习惯性地忽略编译器警告。他们认为,毕竟,如果问题很严重,编译器应该给一个错误信息而非警告信息,不是吗?这种想法对其他语言或许相对无害,但在C++,我敢打赌编译器作者对于将会发生的事情比你有更好的领悟。例如:class B {public: virtual void f() const;};class D: public B {public: virtual void f();}; 这里希望以D::f重新定义virtual函数B::f...原创 2020-10-11 13:54:03 · 249 阅读 · 0 评论 -
写了placement new也要写placement delete——条款52
placementnew和placementdelete并非C++兽栏中最常见的动物,如果你不熟悉它们,不要感到挫折或忧虑。回忆条款16和17,当你写一个new表达式像这样:Widget* pw = new Widget;共有两个函数被调用:一个是用以分配内存的operatornew,一个是Widget的default构造函数。 假设其中第一个函数调用成功,第二个函数却抛出异常。既然这样,步骤一的内存分配所得必须取消并恢复旧观,否则会造成内存泄漏。在这个时候...原创 2020-10-03 11:32:12 · 328 阅读 · 0 评论 -
编写new和delete时需固守常规——条款51
条款50已解释什么时候你会想要写个自己的operatornew和operatordelete,但并没有解释当你那么做时必须遵守什么规则。这些规则不难奉行,但其中一些并不直观,所以知道它们究竟是些什么很重要。 让我们从operatornew开始。实现一致性operatornew必须得返回正确的值,内存不足时必得调用new-handling函数(见条款 49),必须有对付零内存需求的准备,还需避免不慎掩盖正常形式的new——虽然比较偏近class的接口要求而非实现要求。...原创 2020-09-29 21:39:28 · 209 阅读 · 0 评论 -
了解new和delete的合理替换时机——条款50
让我们暂时回到根本原理。首先怎么会有人想要替换编译器提供的operatornew或operatordelete呢?下面是三个最常见的理由:用来检测运用上的错误。如果将“new所得内存”delete掉却不幸失败,会导致内存泄漏(memoryleaks)。如果在“new所得内存”身上多次delete则会导致不确定行为。如果operatornew持有一串动态分配所得地址,而operatordelete将地址从中移走,倒是很容易检测出上述错误用法。 为了强化效能。编译器所带的oper...原创 2020-09-20 10:41:24 · 271 阅读 · 0 评论 -
了解new-handler的行为——条款49
operatornew无法满足某一内存分配需求时,它会抛出异常。以前它会返回一个null指针,某些旧式编译器目前也还那么做。你还是可以取得旧行为,但本条款最后才会进行这项讨论。 当operatornew抛出异常以反映一个未获满足的内存需求之前,它会先调用一个客户指定的错误处理函数,一个所谓的new-handler。为了指定这个“用以处理内存不足”的函数,客户必须调用set_new_handler,那是声明于<new>的标准程序库函数:namespace...原创 2020-09-13 12:10:36 · 185 阅读 · 0 评论 -
需要类型转换时请为模板定义非成员函数——条款46
条款24讨论过为什么唯有non-member函数才有能力“在所有实参身上实施隐式类型转换”,该条款并以Rationalclass的operator*函数为例。我强烈建议你继续看下去之前先让自己熟稔那个例子,因为本条款首先以一个看似无害的改动扩充条款24的讨论:本条款将Rational和operator*模板化了:template<typename T> class Rational { public: Rational(const T&am...原创 2020-09-06 12:31:45 · 287 阅读 · 1 评论 -
运用成员函数模板接受所有兼容类型——条款45
所谓智能指针是“行为像指针”的对象,并提供指针没有的机能。例如条款13曾经提及std::auto_ptr和tr1::shared_ptr如何能够被用来在正确时机自动删除heap-based资源。STL容器的迭代器几乎总是智能指针;无疑地你不会奢望使用“++”将一个内置指针从linkedlist的某个节点移到另一个节点,但这在list::iterators身上办得到。 真实指针做得很好的一件事是,支持隐式转换。Derivedclass指针可以隐式转换为baseclass...原创 2020-08-30 11:08:24 · 239 阅读 · 0 评论 -
将与参数无关的代码抽离templates——条款44
Templates是节省时间和避免代码重复的一个奇方妙法。不再需要键入20个类似的classes而每一个带有15个成员函数,你只需键入一个classtemplate,留给编译器去具现化那20个你需要的相关classes和300个函数。(classtemplates的成员函数只有在被使用时才被暗中具现化,所以只有在这300个函数的每一个都被使用,你才会获得这300个函数。)Functiontemplates有类似的诉求。替换写许多函数,你只需要写一个functiontemplates,...原创 2020-08-23 22:19:25 · 314 阅读 · 0 评论 -
学习处理模板化基类内的名称——条款43
假设我们需要撰写一个程序,它能够传送信息到若干不同的公司去。信息要不译成密码,要不就是未经加工的文字。如果编译期间我们有足够信息来决定哪一个信息传至哪一家公司,就可以采用基于template的解法:class CompanyA {public: ... void sendCleartext(const std::string& msg); void sendEncrypted(const std::string& msg); ...}...原创 2020-08-16 11:08:20 · 215 阅读 · 0 评论 -
了解typename的双重意义——条款42
提一个问题:以下template声明式中,class和typename有什么不同?template<class T> class Widget; //使用"class"template<typenameT> class Widget; //使用"typename" 答案:没有不同。当我们声明template类型参数,class和typename的意义完全相同。某些程序员始终比较喜欢class,因为可以少打几个字。其他人比较喜欢typena...原创 2020-08-09 11:03:22 · 199 阅读 · 0 评论 -
了解隐式接口和编译期多态——条款41
面向对象变成世界总是以显式接口(explicit interfaces)和运行期多态(runtime polymorphism)解决问题。举个例子,给定这样(无甚意义)的class:class Widget {public: Widget(); virtual ~Widget(); virtual std::size_t size() const; virtual void normalize(); void swap(Widget& ...原创 2020-08-02 15:20:25 · 266 阅读 · 0 评论 -
明智而审慎地使用多重继承——条款40
当多重继承(multiple inheritance;MI)进入设计景框,程序有可能从一个以上的base classes继承相同名称(如函数、typedef等等)。那会导致较多的歧义机会。例如:class BorrowableItem { // 图书馆允许你借某些东西public: void checkOut(); // 离开时进行检查 ...};class ElectronicGadget {private: bool...原创 2020-07-26 15:35:08 · 244 阅读 · 0 评论 -
明智而审慎地使用private继承——条款39
条款32曾经论证过C++如何将public继承视为is-a关系。在那个例子中我们有个继承体系,其中class Student以public形式继承class Person,于是编译器在必要时刻(为了让函数调用成功)将Students暗自转换为Persons。现在我们再重复该例的一部分,并以private继承替换public继承:class Person { ... };class Student:private Person { ... }; // 这次改用private继承...原创 2020-07-19 09:41:08 · 255 阅读 · 0 评论 -
通过复合塑模出has-a或“根据某物实现出”——条款38
复合(composition)是类型之间的一种关系,当某种类型的对象内含它种类型的对象,便是这种关系。例如:class Address { ... }; // 某人的住址class PhoneNumber { ... };class Person {public: ...private: std::string name; // 合成成分物(composed object) Address address; // 同...原创 2020-07-12 10:02:12 · 335 阅读 · 0 评论 -
绝不重新定义继承而来的缺省参数值——条款37
让我们一开始就将讨论简单化。你只能继承两种函数:virtual和non-virtual函数。然而重新定义继承而来的non-virtual函数永远是错误的(见条款36),所以我们可以安全地将本条款的讨论局限于“继承一个带有缺省参数值的virtual函数”。 这种情况下,本条款成立的理由就非常直接而明确了:virtual函数系动态绑定(dynamically bound),而缺省参数值却是静态绑定(statically bound)。 对象的所谓静态类型(st...原创 2020-07-06 21:28:11 · 350 阅读 · 0 评论 -
绝不重新定义继承而来的non-virtual函数——条款36
假设我告诉你,class D系有class B以public形式派生而来,class B定义有一个public成员函数mf。由于mf的参数和返回值都不重要,所以我假设两者皆为void。换句话说我的意思是:class B {public: void mf(); ...};class D: public B { ... }; 虽然我们对B,D和mf一无所知,但面对一个类型为D的对象x:D x; // x是一个类型为D的对象 如果以下行为...原创 2020-06-27 17:33:41 · 498 阅读 · 1 评论 -
考虑virtual函数以外的其他选择——条款35
假设你正在写一个视频游戏软件,你打算为游戏内的人物设计一个继承体系。你的游戏术语暴力砍杀类型,剧中人物被伤害或因其他因素而降低健康状态的情况并不罕见。你因此决定提供一个成员函数healthValue,它会返回一个整数,表示人物的健康程度。由于不同的人物可能以不同的方式计算他们的健康指数,将healthValue声明为virtual似乎是再明白不过的做法:class GameCharacter {public: virtual int healthValue() const; //...原创 2020-06-26 13:53:04 · 300 阅读 · 0 评论 -
区分接口继承和实现继承——条款34
表面上直截了当的public继承概念,经过严密的检查之后,发现它由两部分组成:函数接口(function interfaces)继承和函数实现(function implementations)继承。这两种继承的差异,很像本书导读所讨论的函数声明与函数定义之间的差异。身为class设计者,有时候你回希望derived classes只继承成员函数的接口(也就是声明);有时候你又希望derived classes同时继承函数的接口和实现,但又希望能够覆写(override)它们所继承的实现;有时候你希望de原创 2020-06-17 21:34:49 · 290 阅读 · 0 评论 -
避免遮掩继承而来的名称——条款33
这个题材和继承无关,而是和作用域有关,我们都知道在诸如这般的代码中:int x; // global变量void someFunc(){ double x; // local变量 std::cin >> x; // 读一个新值赋予local变量x} 这个读取数据的语句指涉的是local变量x,而不是global变量x,因为内层作用域的名称会遮掩外围作用域的名称。我们可以这样看本列的作用域形式:...原创 2020-06-07 10:45:56 · 243 阅读 · 0 评论 -
确定你的public继承塑模出is-a关系——条款32
如果你令class D(“Derived”)以public形式继承class B(“Base”),你便是告诉C++编译器(以及你的代码读者)说,每一个类型为D的对象同时也是一个类型为B的对象,反之不成立。你的意识是B比D表现出更一般化的概念,而D比B表现出更特殊化的概念。你主张“B对象可派上用场的任何地方,D对象一样可以派上用场”,因为每一个D对象都是一种B对象。反之如果你需要一个D对象,B对象无法效劳,虽然每个D对象都是一个B对象,反之并不成立。 C++对于“publ...原创 2020-05-30 10:15:34 · 321 阅读 · 0 评论 -
将文件间的编译依存关系降至最低——条款31
假设你对C++程序的某个class实现文件做了些轻微修改。注意,修改的不是class接口,而是实现,而且只改private成分。然后重新建置这个程序,并预计只花数秒就好。毕竟只有一个class被修改。你按下“Build”按钮或键入make(或其它类似命令),然后大吃一惊,然后感到窘困,因为你意识到整个世界都被重新编译和连接了!当这种事情发生,难道你不气恼吗? 问题出在C++并没有把“将接口从实现中分离”这事做得很好。Class的定义式不只详细叙述了class接口,还包括十足...原创 2020-05-26 22:02:34 · 315 阅读 · 0 评论 -
透彻了解inlining的里里外外——条款30
Inline函数,多棒的点子!它们看起来像函数,动作像函数,比宏好得多(见条款2),可以调用它们又不需要蒙受函数调用所招致的额外开销。你还能要求更多吗? 你实际获得的比想到的还多,因为“免除函数调用成本”只是故事的一部分而已。编译器最优化机制通常被设计用来浓缩那些“不含函数”调用的代码,所以当你inline某个函数,或许编译器就因此有能力对它(函数本体)执行语境相关最优化。 然而编写程序就像现实生活一样,没有白吃的午餐。inline函数也不例外。inlin...原创 2020-05-17 11:28:28 · 329 阅读 · 0 评论 -
为“异常安全”而努力是值得的——条款29
异常安全性(Exception safety)有几分像是......呃......怀孕。但等等,在我们完成求偶之前,实在无法确定地谈论生育。 假设有个class用来表现夹带背景图案的GUI菜单。这个class希望用于多线程环境,所以它有个互斥器(mutex)作为并发控制(concurrency control)只用:class PrettyMenu {public: ... void changeBackground(std::istream& imgS...原创 2020-05-16 09:09:56 · 316 阅读 · 0 评论 -
避免返回handles指向对象内部成分——条款28
假设你的程序涉及矩形。每个矩形由其左上角和右下角表示。为了让一个Rectangle对象尽可能小,你可能会决定不把定义矩形的这些点放在Rectangle对象内,而是放在一个辅助的struct内再让Rectangle去指它:class Point { // 这个class用来表述“点”public: Point(int x, int y); ... void...原创 2020-05-04 10:54:14 · 352 阅读 · 0 评论 -
尽量少做转型动作——条款27
C++规则的设计目标之一是,保证“类型错误”绝不可能发生。理论上如果你的程序很“干净地”通过编译,就表示它并不企图在任何对象身上执行任何不安全、无意义、愚蠢荒谬的操作。这是一个极具价值的保证,可别草率地放弃它。 不幸的是,转型(casts)破坏了类型系统(type system)。那可能导致任何种类的麻烦,有些容易辨识,有些非常隐晦。让我们首先回顾转型语法,因为通...原创 2020-05-02 09:42:06 · 269 阅读 · 0 评论 -
尽可能延后变量定义式的出现时间——条款26
只要你定义了一个变量而其类型带有一个构造函数或析构函数,那么当程序控制流(control flow)到达这个变量定义式时,你便得承受构造成本;当这个变量离开其作用域时,你便得承受析构成本。即使这个变量最终未被使用,仍需耗费这些成本,所以你应该尽可能避免这种情形。 或许你会认为,你不可能定义一个不使用的变量,但话不要说得太早!考虑下面这个函数,它计算通行密码的加密...原创 2020-04-28 11:19:42 · 224 阅读 · 0 评论 -
考虑写出一个不抛异常的swap函数——条款25
swap是个有趣的函数。原本它只是STL的一部分,而后成为异常安全性编程(exception-safe programing,见条款29)的脊柱,以及用来处理自我赋值可能性(见条款11)的一个常见机制。由于swap如此有用,适当的实现很重要。 所谓swap(置换)两对象值,意思是将两对象的值彼此赋予对方。缺省情况下swap动作可由标准程序库提供的swap算法完成。...原创 2020-04-25 21:35:53 · 278 阅读 · 0 评论 -
令operator=返回一个reference to *this——条款10
关于赋值,有趣的是你可以把它们写成连锁形式: int x, y, z; x = y = z = 15; 同样有趣的是,赋值采用右结合律,所以上述连锁赋值被解析为: x = (y = (z = 15)); 这里15先被赋值给z,然后其结果(更新后的z)再被赋值给y,然后其结果(更新后的y)再被赋...原创 2019-09-15 13:54:49 · 197 阅读 · 0 评论 -
绝不在构造函数和析构函数中调用virtual函数——条款09
本条款开始前我要先阐述重点:你不该在构造函数和析构函数期间调用virtual函数,因为这样的调用不会带来你预想的结果,就算有你也不会高兴。实例:#include <iostream>using namespace std;class AppLogController { public: AppLogController(); virtual...原创 2019-09-15 13:54:36 · 182 阅读 · 0 评论 -
别让异常逃离析构函数——条款08
C++并不禁止析构函数吐出异常,但它不鼓励你这样做。这是有理由的。考虑以下代码:class Widget { public: ... ~Widget(){...}};void doSomething(){ std::vector<Widget> v;} 当vector v被销毁,它有责任销毁其内含的所有Wi...原创 2019-08-27 21:27:31 · 215 阅读 · 0 评论 -
为多态基类声明virtual析构函数——条款07
c++面向对象特性其中有多态这个特性,平时使用时用基类指针指向子类对象。当我们进行内存回收的时候调用 delete (基类指针),此实如果基类有一个non-virtual析构函数。这种情况下会导致派生类的对象没有被销毁,只有基类部分会被销毁掉,于是造成一个诡异的“局部销毁”对象。这样的后果会形成资源泄露、数据结构被破坏、而且很难找出原因。 消除这个问题的做法很简单...原创 2019-07-31 22:31:05 · 213 阅读 · 0 评论 -
若不想使用编译器自动生成的函数,就该明确拒绝——条款06
从之前条款05中了解到c++自动声明构造函数、复制构造函数、赋值操作符、析构函数,并且这些函数都是public修饰。当我们在设计类的时候可能不想使用c++提供的函数。这时候我们就需要自己来声明了。比如在设计单例类的时候,我们肯定不希望对象被复制,应该只有一个对象,所以我们会声明private 构造函数,并且不予实现即可。 通常我们开发过程中单例模式会用到该条款,但...原创 2019-07-28 21:57:40 · 219 阅读 · 0 评论 -
了解C++默默编写并调用哪些函数——条款05
什么时候empty class(空类)不再是个empty class呢?当C++处理过它之后。是的,如果你自己没声明,编译器就会为它声明(编译器版本的)一个copy构造函数、一个copy assignment操作符和一个析构函数。此外如果没有声明任何构造函数,编译器也会为你声明一个default构造函数。所有这些函数都是public且inline(见条款30).因此,如果你写下:...原创 2019-06-30 23:13:13 · 286 阅读 · 0 评论 -
确定对象被使用前已先被初始化——条款04
在C++开发中经常会使用成员变量,有时候会看到既存代码中,在构造函数对内置类型的成员变量进行赋值操作,而没有使用成员初始列。有的甚至没有初始过成员变量,声明之后就直接使用了。 有的人可能认为内置类型会被自动初始化,所以认为没必要,但是这个可能在某些平台开发时不会自动初始化,因此最好手动初始化所有成员,通常编程规范中也会有这种要求。 下面主要区分一下赋值和初始...原创 2019-06-30 22:00:22 · 319 阅读 · 0 评论 -
尽可能的使用const——条款03
在c++开发中,经常会看到const关键字,比如copy构造函数、copy assignment函数中参数都有const关键字。 在条款02中也提到过用const来替换#define修饰常量,所以const关键字是用来表示常量,即被修饰的变量初始化之后不可被修改。const修饰变量 const修饰带指针变量的时候经常会弄混淆,所以需要仔细分辨,一般分为三种情况:...原创 2019-08-27 20:43:52 · 258 阅读 · 0 评论 -
尽量以const、enum、inline替换 #define——条款02
这个条款或许改为“宁可以编译器替换预处理器”比较好,因为或许 #define 不能被视为语言的一部分。那正是问题所在。比如定义一个宏:#define ASPECT_RATIO 1.653 这个ASPECT_RATIO也许从未被编译器看见。也许在编译器开始处理源码之前就被预处理器移走了。于是记号名称ASPECT_RATIO有可能没进入记号表内,当你运用此常量...原创 2019-05-31 20:27:54 · 276 阅读 · 0 评论 -
复制对象时勿忘其每一个成分——条款12
设计良好之面向对象系统(OO-systems)会将对象的内部封装起来,只留两个函数负责对象拷贝(复制),那便是带着适切名称的copy构造函数和copy assignment操作符,我称它们为copying函数。条款5观察到编译器会在必要的时候为我们的classes创建copying函数,并说明这些“编译器生成版”的行为:将被拷对象的所有成员变量都做一份拷贝。 如果...原创 2019-10-27 13:25:29 · 188 阅读 · 0 评论 -
在operator=中处理“自我赋值”——条款11
“自我赋值”发生在对象被赋值给自己时:class Widget {...};Widget w;...w = w; //赋值给自己 这看起来有点愚蠢,但它合法,所以不要认定客户绝不会那么做。此外赋值动作并不总是那么可被一眼辨识出来,例如: a[i] = a[j];//潜在的自我赋值 如果i和j有相同的值,这便是自我赋值。再看: *px =...原创 2019-10-26 17:57:42 · 228 阅读 · 0 评论 -
若所有参数皆需类型转换,请为此采用non-member函数——条款24
令classes支持隐式类型转换通常是个糟糕的注意。当然这条规则有其例外,最常见的例外是在建立数值类型时。假设你设计一个class用来表现有理数,允许整数“隐式转换”为有理数似乎颇为合理。的确,它并不比C++内置从int至double的转换来得不合理,而还比C++内置从double至int的转换来得合理些。假设你这样开始你的Rational class:class Ratio...原创 2020-04-12 16:48:29 · 330 阅读 · 0 评论 -
宁以non-member、non-friend替换member函数——条款23
想象有个class用来表示网页浏览器。这样的class可能提供的众多函数中,有一些用来消除下载元素高速缓存区(cache of download elements)、清除访问过的URLs的历史记录(history of visited URLs)、以及移除系统中的所有cookies:class WebBrowser {public: ... void clearCach...原创 2020-04-07 21:41:39 · 317 阅读 · 1 评论