• Effective Modern C++
21年品牌 40万+商家 超1.5亿件商品

Effective Modern C++

全新正版 现货速发

37.8 3.8折 99 全新

库存29件

江苏无锡
认证卖家担保交易快速发货售后保障

作者(美)斯科特·迈耶(Scott Meyers) 著;高博 译

出版社中国电力出版社

ISBN9787519817749

出版时间2018-04

装帧平装

开本16开

定价99元

货号1201699996

上书时间2024-12-06

翰林文轩旗舰店

三年老店
已实名 已认证 进店 收藏店铺

   商品详情   

品相描述:全新
商品描述
作者简介
20多年来,斯科特·迈耶的Effective C++丛书(包括《Effective C++》、《More Effecitve C++》和《Effective STL》)已经为C++程序设计指南的业界设立标杆。他清晰明了引人入胜的、对复杂技术材料进行条分缕析的阐释为他赢得了世界范围内的称誉,也使他成为一名广受欢迎的培训师、咨询顾问和会议讲师。他拥有布朗大学计算机科学专业的博士学位。

目录
出版商声明 1
致谢 3
绪论 7
第1章 型别推导 15
条款1:理解模板型别推导 15
条款2:理解auto型别推导 23
条款3:理解decltype 28
条款4:掌握查看型别推导结果的方法 35
第2章 auto 41
条款5:优先选用auto,而非显式型别声明 41
条款6:当auto推导的型别不符合要求时,使用带显式型别的初始化物习惯用法 46
第3章 转向现代C++ 52
条款7:在创建对象时注意区分()和{} 52
条款8:优先选用nullptr,而非0或NULL 61
条款9:优先选用别名声明,而非typedef 64
条款10:优先选用限定作用域的枚举型别,而非不限作用域的枚举型别 68
条款11:优先选用删除函数,而非private未定义函数 74
条款12:为意在改写的函数添加override声明 79
条款13:优先选用const_iterator,而非iterator 85
条款14:只要函数不会发射异常,就为其加上noexcept声明 89
条款15:只要有可能使用constexpr,就使用它 95
条款16:保证const成员函数的线程安全性 101
条款17:理解特种成员函数的生成机制 106
第4章 智能指针 113
条款18:使用std::unique_ptr管理具备专属所有权的资源 115
条款19:使用std::shared_ptr管理具备共享所有权的资源 120
条款20:对于类似std::shared_ptr但有可能空悬的指针使用std::weak_ptr 129
条款21:优先选用std::make_unique和std::make_shared,而非直接使用new 133
条款22:使用Pimpl习惯用法时,将特殊成员函数的定义放到实现文件中 141
第5章 右值引用、移动语义和完美转发 150
条款23:理解std::move和std::forward 151
条款24:区分多能引用和右值引用 156
条款25:针对右值引用实施std::move,针对多能引用实施std::forward 161
条款26:避免依多能引用型别进行重载 169
条款27:熟悉依多能引用型别进行重载的替代方案 175
条款28:理解引用折叠 187
条款29:假定移动操作不存在、成本高、未使用 193
条款30:熟悉完美转发的失败情形 196
第6章 lambda表达式 204
条款31:避免默认捕获模式 205
条款32:使用初始化捕获将对象移入闭包 212
条款33:对auto&&型别的形参使用decltype,以std::forward之 217
条款34:优先选用lambda式,而非std::bind 220
第7章 并发API 228
条款35:优先选用基于任务而非基于线程的程序设计 228
条款36:如果异步是必要的,则指定std::launch::async 232
条款37:使std::thread型别对象在所有路径皆不可联结 236
条款38:对变化多端的线程句柄析构函数行为保持关注 243
条款39:考虑针对一次性事件通信使用以void为模板型别实参的期值 247
条款40:对并发使用std::atomic,对特种内存使用volatile 254
第8章 微调 263
条款41:针对可复制的形参,在移动成本低并且一定会被复制的前提下,考虑将
其按值传递 263
条款42:考虑置入而非插入 273

内容摘要
想要理解C++11和C++14,不可止步于熟悉它们引入的语言特性(例如,auto型别推导、移动语义、lambda表达式,以及并发支持)。挑战在于高效地运用这些特性,从而使你的软件具备正确性、高效率、可维护性和可移植性。这正是本书意欲达成的定位。它描述的正是使用C++11和C++14(即现代C++)来编写真正很好的软件之道。涵盖以下主题:大括号初始化、noexcept规格、完美转发,以及智能指针的make函数的优缺点。std::move、std::forward、右值引用和多能引用之间的联系。编写整洁、正确,以及高效的lambda表达式的方法。std::atomic和volatile有怎样的区别,它们分别用于什么场合,以及它们和C++的并发API有何联系。“旧”C++程序设计(即C++98)中的实战要求在现代C++的软件开发中作出哪些修订。

精彩内容
绪论如果你是一名身经百战的C++ 程序员,又恰好和我有那么点儿气味相投,你会在和C++11 最初打上交道时暗忖:“嗯,我明白,这还是C++,锦上添花而已。”但随着学习的深入,你会对变化之纵深吃惊不小:a u t o 声明式、基于范围的循环、lambda表达式,以及右值引用。这些已经改变了C++ 的面貌,这还没有把全新的并发特性计算以内。随之而来的还有诸多习惯用法的改变。0 和typedef 已经过时,nullptr 和别名声明式大行其道。枚举量如今要限定作用域。相对于内建指针而言,智能指针成为优选。在正常情况下,对象的移动语义要好过复制语义。C++11 已有大量内容要学习,C++14 自然更不必说。更关键的是,想要高效地利用这些新能力,任重而道远。如果你仅仅想了解“现代”C++特性的基本信息,资料俯拾皆是。但如果你想要寻觅如何采用这些特性去创建正确、高效、易维护、可移植的软件,恐怕就没那么容易了。本书正是应此而生。本书的写作目的并非对于C++11 和C++14 特性的泛泛介绍,而是为了揭示它们的高效应用。本书中的信息被分解成若干准则,称为条款。你想要理解型别推导的各种形式吗?又或者想要知道何时该使用(或不该使用)a u t o 声明式?你是否有兴趣知道为何const 成员函数应该保证线程安全,或如何使用std::unique_ptr 实现Pimpl 习惯用法,或为何在lambda 表达式中应该避免默认捕获模式(default capture mode),或std::atomic 和volatile 的区别?答案统统在本书中。还不止如此,这些答案都与平台无关,且符合标准。本书是围绕着可移植的C++ 展开的。本书中的条款都是准则,并非规则,因为准则允许有例外。条款给出的建议并非最要紧的部分,建议背后的原理才是精华。只有掌握了原理,你才能判定,你的项目面临的具体情况是否真的违反了条款所指。本书的真正目标并不在于告诉你什么该做,什么不该做,而是想要传达对C++11 和C++14 运作原理的更深入理解。术语和惯例为保证理解的一致性,厘清对于一些术语的认识很有必要。首先要说清楚的就是“C++”这个词,是不是有点儿讽刺意味呢。C++ 有四个官方钦定版本,都是以ISO 标准被接受的年份命名的,它们是C++98、C++03、C++11 和C++14。C++98 和C++03 仅有一些技术细节上的不同,所以在本书中我将它们统称为C++98。当提到C++11 时,我的意思是C++11 和C++14 都适用,因为C++14 实际上是C++11 的超集。而如果写的是C++14,我仅指C++14。如果我说的是什么都不附加的C++,意思就是适用一切语言版本。术语 意指的语言版本C++ 所有版本C++98 C++98 和C++03C++11 C++11 和C++14C++14 C++14依照上面的约定,我可能会有这样的表述:C++ 很好重视运行效率(所有版本都成立),C++98 缺乏并发支持(仅对C++98 和C++03 成立),C++11 支持lambda 表达式(对C++11 和C++14 成立),C++14 提供了广义返回值型别推导(仅对C++14 成立)。C++11 被最广泛接受的特性可能莫过于移动语义,而移动语义的基础在于区分左值表达式和右值表达式。因为,一个对象是右值意味着能够对其实施移动语义,而左值则一般不然。从概念上说(实践上并不总是成立),右值对应的是函数返回的临时对象,而左值对应的是可指涉的对象,而指涉的途径则无论通过名字、指针,还是左值引用皆可。有一种甄别表达式是否左值的实用方法富有启发性,那就是检查能否取得该表达式的地址。如果可以取得,那么该表达式基本上可以断定是左值。如果不可以,则其通常是右值。这种方法之所以说富有启发性,是因为它让你记得,表达式的型别与它是左值还是右值没有关系。换言之,给定一型别T,则既有T 型别的左值,也有T 型别的右值。这一点在处理右值引用型别的形参时尤其要注意,因为该形参本身是个左值。class Widget {public:Widget(Widget&& rhs); // rhs 是个左值,… // 尽管它具有右值引用型别};在Widget 的移动构造函数内部对rhs 取址接近没有问题,所以rhs 是个左值,尽管它的型别属于右值引用(基于类似的理由,我们可以得知,任何形参都是左值)。上述代码片断还演示了我通常会遵循的若干惯例:? 类名是W i d g e t。每当我想要一个任意用户自定义型别时,我就会使用W i d g e t这个名字。并且,除非我需要展示类的特殊细节,我可能会不经声明就使用Widget 类。? 我有时会采用rhs(“right-hand side”,右侧)这个形参名,尤其是用作移动操作(即移动构造函数和移动赋值运算符),以及复制操作(即复制构造函数和复制赋值运算符)的形参名。这个名字我也会用于二元运算符的右侧形参:Matrix operator+(const Matrix& lhs, const Matrix& rhs);上面的 lhs 表示“left-hand side”(左侧),这很好理解吧。? 我会在代码和注释的某些部分使用特殊的排版,以吸引你去注意它们。在上述W i d g e t 移动运算符中,我将r h s 的声明,以及注释中强调其是一个左值的部分排成了突显格式。突显的代码既不是说明它好,也不是说明它坏,只是说明这部分代码需要引起重视。? 我使用“…”来表示“这里可能有其他代码”,这种全角省略号和C++11 中变长模板源码中所使用的半角省略号“...”是接近不同的。译注1 听上去有点儿容易混淆,其实不然:template // 这两行中的省略号void processVals(const Ts&... params) // 都是源码组成部分{… // 而这一行中的省略号// 则表示“这里可能有其他代码”}译注1:原文中是采用“窄省略号”和“宽省略号”来区分两者的;简体中文版中则采用全角和半角来区分,这样更易辨识。在模板processVals 的声明语句中,可以看到我使用的是typename 来声明模板中的型别形参,这仅仅是我的个人偏好。此处使用关键字class 也同样可以。但当我演示C++ 标准中的代码片断时,我会使用c l a s s 来声明型别形参,因为标准中采用的就是那样的做法。若某对象是依据同一型别的另一对象初始化出来的,则该新对象称为提供初始化依据的对象的一个副本,即使该副本是由移动构造函数创建的。这样称呼情有可原,因为C++ 中并无术语用以区分某对象到底是经由复制构造函数创建的副本,还是经由移动构造函数创建的副本。译注2void someFunc(Widget w); // someFunc 的形参w 按值传递Widget wid; // wid 是Widget 型别的某个对象someFunc(wid); // 在这个对someFunc 的调用中,// w 是wid 经由复制构造函数创建的副本someFunc(std::move(wid)); // 在这个对someFunc 的调用中,// w 是wid 经由移动构造函数创建的副本右值的副本通常经由移动构造函数创建,而左值的副本通常经由复制构造函数创建。这也就是说,如果你仅仅了解到某个对象是另一对象的副本,则还不能判断构造这个副本要花费多少成本。例如,在上述代码中,如果不知道传入s o m e F u n c 的究竟是右值还是左值,就无法计算创建形参w 所花费的成本(你还需要知道移动或复制Widget型别对象的具体成本)。在函数调用中,调用方的表达式,称为函数的实参。实参的用处,是初始化函数的形参。在上面s o m e F u n c 的第一次调用中,实参是w i d。而在第二次调用中,实参则是std::move(wid)。在两次调用中,形参都是w。实参和形参有着重大的区别,因为形参都是左值,而用来作为其初始化依据的实参,则既可能是右值,也可能是左值。这一点在完美转发(perfect forwarding)的过程中尤其关系重大,在这样的一个过程中,传递给某个函数的实参会被传递给另一函数,并保持其右值性(rvalueness)或左值性(lvalueness)(完美转发会在条款30 中详细讨论)。设计良好的函数都是异常安全的,这意味着它们至少会提供基本异常安全保证(即基本保证)。提供了基本保证的函数能够向调用者确保即使有异常抛出,程序的不变量不会受到影响(即不会有数据结构被破坏),且不会发生资源泄漏。而提供了强异常安全保证(即强保证)的函数则能够向调用者确保即使有异常抛出,程序状态会在调用前后保持不变。译注2:在原文中,“副本”和“复制构造函数”中的“复制”是同一个词“copy”。当提及函数对象时,我通常意指某个对象,其型别支持operator() 成员函数。换言之,就是说该对象表现得像个函数。我偶尔也会在一个更加广义的情形下使用该术语,意指任何可以采用非成员函数语法[即形如“function Name(arguments)”]调用之物。这种更宽泛的定义不仅涵盖了支持operator() 的对象,还有标准函数以及C 风格的函数指针(狭义定义来自C++98,而广义定义来自C++11 译注3)。进一步泛化这一术语的话,就涵盖了指涉到成员函数的指针,从而得到了所谓的可调用物。一般情况下,你可以不用关心这些含义之前的细微差别,函数指针也好,可调用物也罢,你只需知道它们在C++ 中表示某种函数调用语法加以调用就行了。经由lambda 表达式创建的函数对象称为闭包,将lambda 表达式和它们创建的闭包区分开来,意义不大,所以我经常把它们统称为lambda 式。相似地,我也很少区分函数模板(即用以生成函数的模板)和函数模板(即从函数模板生成的函数)。类模板和模板类的情形同上。C++ 中有很多事物能够加以声明和定义。声明的作用是引入名字和型别,而不给出细节,如存储位置或具体实现:extern int x; // 对象声明class Widget; // 类声明bool func(const Widget& w); // 函数声明enum class Color; // 限定作用域的枚举声明(参见条款10)定义则会给出存储位置和具体实现的细节:int x; // 对象定义class Widget { // 类定义…};bool func(const Widget& w){ return w.size() < 10; } // 函数定义enum class Color{ Yellow, Red, Blue }; // 限定作用域的枚举定义定义同时也可以当声明用。所以,除非某些场合非给出定义不可,我倾向于只使用声明。译注3:此句的意义并不是说C++98 中没有标准函数和C 风格的函数指针这两种语言特性,而是说在C++98 的语境中使用函数指针这一术语时,一般不特指这两者。我把函数声明的形参型别和返回值型别这部分定义为函数的签名,而函数名字和形参名字则不属于签名的组成部分。在上例中,func 的签名是bool(const Widget&)。函数声明除形参型别和返回值型别的其他组成元素(即可能存在的n o e x c e p t 或constexpr)则被排除在外(noexcept 和constexpr 的内容详见条款14 和条款15)。“签名”的官方定义与我给出的稍有不同,但在本书中,我的定义更加实用(官方定义有时会省去返回值型别)。通常来说,在C++ 旧标准下写就的代码在C++ 新标准下仍然会保有合法性,但有时标准化委员会也会废弃某些特性。这些特性在标准化的前途上已是穷途末路,并有可能在未来的标准中被去除。编译器可能会,也可能不会对于在代码中使用这些已废弃特性的行为给出警告,但是你应该尽力避免使用它们。它们不仅会造成未来的可移植性问题,而且还不如那些替换掉它们的新特性好用。例如,在C++11 标准中,std::auto_ptr 就是个被废弃的特性,因为std::unique_ptr 可以完成同样的任务,并且使用起来更方便。标准有时会把某个操作的结果说成是未定义行为。意思是,其运行期行为不可预测,你当然会对这样的不确定性敬而远之。未定义行为的例子有,在方括号(“[]”)内使用越界值作为std::vector 的下标,对未初始化的迭代器实施提领操作,或者进入数据竞险(即两个或更多线程同时访问同一内存位置,且其中至少有一个执行写操作的情形)。我将内建的指针,就是n e w 表达式返回的那些指针,称为裸指针。而与裸指针形成对照的,则是智能指针。智能指针通常都重载了指针提领运算符(o p e r a t o r - > 和operator*),不过条款20 会解释为什么std::weak_ptr 是个例外。在源代码注释中,我有时会将“构造函数”简写为“ctor”,将“析构函数”简写为“dtor”。译注4提交缺陷报告和改进建议我已尽力使得本书中的信息清晰、准确、实用,但是改进空间是一定少不了的。如果你在本书中发现任何谬误(无论是技术的、文字的,还是排版的),或有任何改进建议,请发电子邮件给我:emc++@aristeia.com。每次重印时我都有机会修订EffectiveModern C++,但如果我不知道哪里有问题,也无从着手。译注4:简体中文翻译时,不作此区别。“ctor”一律译为“构造函数”,“dtor”一律译为“析构函数”。欲查看已知问题,请参考本书勘误页面:http://www.aristeia.com/BookErrata/emc++-errata.html。

   相关推荐   

—  没有更多了  —

以下为对购买帮助不大的评价

此功能需要访问孔网APP才能使用
暂时不用
打开孔网APP