
读书笔记
记录阅读各类技术书籍后学习到的主要内容和心得体会
张科扬
想做程序员,目前擅长开关机。
展开
-
《Effective C++》Item19:设计类之前需要深思熟虑
本条款在原书中的名称是:设计类犹如设计类型。C++就像其他支持面向对象的语言一样。当我们定义了一个新的类,也就定义了一个新的类型。身为C++程序员,我们的许多时间主要用来扩张我们自己的类型系统。这意味着我们并不只是类的设计者,还是类型的设计者。重载函数和操作符、控制内存的分配和回收、定义对象的初始化和析构等等,这些操作全部交给我们来控制。因此,我们应该带着和“语言设计者当初设计语言内置类型时”一样的谨慎来研讨类的设计。设计优秀的类库是一项艰巨的工作,因为设计好的类型是一项艰巨的工作。好的类型有自然的.原创 2020-07-11 16:05:13 · 164 阅读 · 0 评论 -
《Effective C++》Item18:让接口容易被正确使用,不易被误用
C++中充满了接口:function接口、class接口、template接口等,每一种接口都是客户与我们的代码互动的手段。假设我们面对的是一群“讲道理”的人,这部分客户希望能够正确地使用我们提供的借口。在这种情况下,如果它们对其中的任何一个接口的用法不正确,那么我们作为接口的设计者,也应该承担一些连带的责任。因为理论上,如果客户企图使用某个接口,但是却没有实现他所预期的行为,那么这段代码就不应该通过编译;反过来说,只要代码通过了编译,那么程序的行为就应该是用户所期望的那样。想要开发一个“容易被正确使用、原创 2020-07-10 15:07:22 · 238 阅读 · 0 评论 -
《Effective C++》Item17:使用独立的语句将底层资源放入RTTI对象中
假设现在我们有一个函数用来处理程序的优先级,另一个函数用来在某个动态分配所得的对象上进行一些带有优先级的处理:int priority();void processObject(std::shared_ptr<Object> obj, int priority);由于我们时刻牢记“使用对象管理资源”的智慧铭言,因此决定对动态分配获得的对象使用智能指针。现在考虑如何来调用processObject函数:processObject(std::shared_ptr<Object>原创 2020-07-09 18:26:27 · 200 阅读 · 1 评论 -
《Effective C++》Item16:成对使用new/delete和new[]/delete[]
以下代码有什么问题?std::string *stringArray = new std::string[100];//...delete stringArray;每件事情看起来都井然有序。使用了new,也搭配了对应的delete。但还是有一点完全错误:程序的行为是未定义的。在最好的情况下,stringArray所包含的100个string对象中的99个不太可能被正确地回收,因为它们的析构函数很可能没有被调用。当使用了new的时候,有两件事情发生:调用operator new分配内存。对象原创 2020-07-09 17:56:34 · 222 阅读 · 0 评论 -
《Effective C++》Item15:在资源管理类中提供对原始资源的访问
资源管理类很棒,它们是我们对抗资源泄露的堡垒,可以完全依赖它们回收底层资源。然而,当代码需要向后兼容,或者引入第三方库的时候,仍然会遇到麻烦:许多现有的API需要直接提供底层资源。举个例子,条款13曾经引入了一个例子,使用智能指针auto_ptr来保存工厂函数createInvestment返回的指针:std::auto_ptr<Investment> invest(createInvestment());假设现在引入了一个旧的API,或者是第三方库:int daysHeld(cons原创 2020-07-09 16:03:45 · 219 阅读 · 0 评论 -
《Effective C++》Item14:在资源管理类中当心拷贝操作
条款13中引入了这样的概念:资源取得的时机便是初始化资源管理对象的时机。然而,资源的种类是很多的,我们总是需要编写自己的资源管理类。例如,假设我们使用C风格的API来处理类型为Mutex的互斥量对象,提供了两种操作:锁定操作lock,以及解锁操作unlock。void lock(Mutex *mutex);void unlock(Mutex *mutex);为了确保绝对不会忘记将一个被锁定的互斥量解锁,我们需要建立一个资源管理类:class MutexGuard{public: ex原创 2020-07-09 15:19:00 · 153 阅读 · 0 评论 -
《Effective C++》Item13:使用RAII对象管理资源
所谓的“资源”,指的就是那些“一旦使用,将来必须要归还给操作系统”的组件。在C++程序当中,最常使用的资源就是堆内存;然而内存只是我们必须管理的众多资源之一,其它常用的资源还包括:文件描述符、线程互斥量、网络连接等等。无论是哪一种资源,在使用完毕后都必须要归还给服务提供者。尝试在任何情况下都手工维护资源,实际是一件很困难的事。因为一旦引入了多种执行流、程序异常等特性,那么手中的资源就很容易失去控制。假使我们使用一个用来抽象投资行为的类库,其中各种各样的投资类型都继承自同一个基类Investment:c原创 2020-07-09 14:34:36 · 272 阅读 · 0 评论 -
《Effective C++》Item12:复制对象时切勿忘记每一个成分
设计良好的面向对象软件系统会将对象的内部封装起来,只留两个函数负责对象的拷贝,那便是拷贝构造函数和拷贝赋值运算符。在条款5中,我们已经指出编译器会在必要的时候为我们的类生成这两个函数,在其中对每个成员变量进行浅拷贝。一旦我们声明了自己的拷贝构造函数或者拷贝赋值运算符,编译器不仅不再负责对应函数的生成,甚至连检查都完全不进行。这在某些时候将会导致问题。例如,我们写了一个类Customer来表示顾客,并且手工书写了拷贝构造函数和拷贝赋值运算符,用于打印日志:class Customer{public:原创 2020-07-07 19:25:51 · 148 阅读 · 0 评论 -
《Effective C++》Item11:在operator=中处理自我赋值
所谓的“自我赋值”发生在对象被赋值给自己时:class Object { /* .. */ }int main(){ Object o; o = o; //自我赋值。 return 0;}这种写法看上去没有任何意义,但是它是可以通过编译的;最关键的是,这种操作往往不会显示地发生,例如:int main(){ Object o; //... Object *p1 = &o; Object *p2 = &o; //原创 2020-07-07 18:25:49 · 167 阅读 · 0 评论 -
《Effective C++》Item10:让所有与赋值相关的运算符返回指向当前对象的引用
在C++中,赋值可以是递归的:int x, y, z;x = y = z = 10;并且,赋值是遵循右结合律的,所以上面的赋值语句等价于:x = (y = (z = 10));所以,为了实现这样的连锁赋值方式,类的拷贝复制运算符必须要返回一个指向当前对象的引用:class Object{ //...public: Object &operator=(const Object &other) { //... retu原创 2020-07-07 16:41:40 · 235 阅读 · 0 评论 -
《Effective C++》Item9:不要在构造和析构函数中调用虚函数
这是一条在C++中非常重要的条款,也是C++和其他更高级的语言(例如Java、C#等)不同的地方。假设现在我们编写了一些类,用于抽象股市中买卖股票的订单:class Transaction{public: Transaction() { //... this->logTransaction(); } virtual void logTransaction() const = 0;};class BuyTransactio原创 2020-07-07 15:53:22 · 197 阅读 · 0 评论 -
《Effective C++》Item8:不要让析构函数抛出异常
从语法层面来看,C++是允许在析构函数中抛出异常的,但是并不鼓励我们这样做。因为在一些特殊情况下,析构函数抛出的异常将直接引发程序的崩溃。看一个例子:书中提供了一个示例程序,但我认为不足以说明问题。因此我提供了另外一个更明显的例子。class Object{public: ~Object() { throw "There is an exception"; }};int main(){ try { Object o; // ... throw "There is原创 2020-07-07 14:55:07 · 1043 阅读 · 0 评论 -
《Effective C++》Item7:为多态基类声明虚析构函数
多态的特点是:调用相同的接口,程序的运行时行为取决于动态绑定在这个指针或者引用上的实现。例如,为了抽象出获取时间的方法,可以设计一个基类用于规定一个统一的接口,而不同的派生类用于实现各自的计时方式:class Timer{public: Timer(); ~Timer(); //...};class UTCTimer : public Timer{public: //...};class AtomicTimer : public Timer{pub原创 2020-07-06 17:19:31 · 158 阅读 · 0 评论 -
《Effective C++》Item6:如果不想使用编译器提供的默认函数,就该明确拒绝
对于某些单例而言,其对象应是不允许被拷贝的。然而,编译器却会默认为我们生成拷贝构造函数和拷贝赋值运算符。如何才能关闭这一特性呢?答案的关键是,所有编译器的产出函数都是public。为了阻止这些函数,我们要得自行声明它们。因此,我们可以将它们声明为private。这样,编译器就不再有理由为我们合成默认的函数了:因为我们已经自己提供了它们的声明。另一个关键的技巧是声明它们,却不提供实现。因为成员函数和友元函数当中可能会利用实现来复制出多份对象的副本。这样一来,如果客户的代码尝试拷贝单例对象,则会导致链接过程原创 2020-07-06 16:20:44 · 1326 阅读 · 0 评论 -
《Effective C++》Item5:了解C++默默编写并调用了哪些函数
如果一个C++类没有声明自己的构造函数、拷贝构造函数、拷贝赋值运算符和析构函数的话,那么编译器就会为我们生成一个。所有这些由编译器为我们生成的函数都是public且inline的。这些函数只有当被调用到的时候,才会被创建出来。但是这些函数都做了什么呢?主要是处理一些程序幕后的工作,即:编译器向这些函数中安插了额外的代码来处理一些常规事务,例如:调用父类的构造函数和析构函数、初始化虚函数表指针等。至于拷贝构造函数和拷贝赋值运算符,编译器只是简单地将对象中的成员变量进行浅拷贝。例如下面的这个类:temp原创 2020-07-06 15:36:08 · 195 阅读 · 0 评论 -
《Effective C++》Item4:确保对象在使用之前已经被初始化
在C++中,对象的定义和初始化是两码事。普通对象的初始化例如,如果写出这样的代码:int x;编译器将仅仅为变量x在栈上分配一块内存空间,但是不会向其中写入初始值。对象的定义指的是为其分配内存的过程,往往由全局的operator new函数完成;对象的初始化指的是向这块内存中写入恰当的值,让对象变为可用状态的过程,往往由对象的构造函数完成。这也是体现C++复杂的一个方面。例如,C++完全继承了C语言中数组的特性。因此,仅定义的一个数组是仅定义而未被初始化的;但是STL中的各种容器都实现了自原创 2020-07-06 12:16:30 · 287 阅读 · 0 评论 -
《Effective C++》Item3:尽量使用const
const的一个奇妙的特性是,他允许你指定一个语义约束,即被const修饰的对象都是不可修改的,而编译器会帮助你在编译时维护这个性质,这能让你更早地发现程序设计过程中的漏洞和缺陷。因此,只要允许,就应该明确地使用const对变量进行限定。const对象只要是对象,那么不管它在哪,都可以添加const修饰;对于指针,还可以分别指定指针本身以及指针所指向的对象是否可以修改:char greeting[] = "hello";char *p1 = greeting;const char *p2 = gr原创 2020-07-06 11:10:52 · 1611 阅读 · 0 评论 -
《Effective C++》Item2:尽量以const、enum、inline代替#define
宏的问题在于:处理它的是预处理器,而不是编译器。考虑如下的定义:#define PI 3.1415926在预处理器工作时,会将实际代码中的所有PI都直接替换为3.1415926,所以编译器在工作的时候根本看不到PI这个记号。由此产生的问题是:PI没有进入编译器的符号表。于是当你使用PI但是不幸收到一个编译错误时,其错误信息可能包含3.1415926,而不是PI。如果这个宏不是由你定义的,那么这个错误很可能给你造成很大的困扰。解决这个问题的一个方法是使用常量替换掉这个宏:const double P原创 2020-07-06 11:09:08 · 160 阅读 · 0 评论 -
《Effective C++》Item1:视C++为一个语言联邦
条款1:视C++为一个语言联邦一开始,C++只是C加上一些面向对象特性。C++最初的名称C with class也反映了这一点。但是当这个语言逐渐成熟,它变得更活跃更无拘束,开始接受各种新观念、新特性和新的编程战略。异常(exception)对结构化程序引入了不同的做法,而模板(template)为我们带来了新的思考方式。今天的C++已经是个多重范型编程语言,一个同时支持面向过程、面向对象、模板、模板元编程的语言。这些能力和弹性使得C++已经成为了一个无可匹敌的工具,但也可能会引发某种疑惑:所有的“适原创 2020-07-06 11:07:00 · 301 阅读 · 0 评论