More Effective C++ 读书摘要(四、效率)Item16 - 24

【信息科学与工程学】【广告科学】第四十四篇 广告平台投放和广告计费类算法01 全行业的中国企业广告投放模型总表。行业分类依据《国民经济行业分类》(GB/T 4754—2017)的门类/大类 ,产品层参考"主营产品统计代码"行业—产品映射 ,平台算法以巨量引擎、腾讯广告等官方文档为硬支撑 ,合规部分严格对齐《互联网广告管理办法》以及2026年市场监管部门最新合规提示 。编号类型(门类)行业(大类)企业及产品内容列表投放模型投放平台投放人群欲望激发维度面向人群模式(地域/文化/年龄/角色)001A 农林牧渔01 农业北大荒/中粮:五常大米、面粉、食用油、杂粮品牌曝光 + 电商转化双轨(C 阅读详情

Item16. 记住80-20准则:


80-20准则说的是大约20%的代码使用了80%的程序资源;大约20%的代码耗用了大约80%的运行时间;大约20%的代码使用了80%的内存;大约20%的代码执行80%的磁盘访问;80%的维护投入于大约20%的代码上;通过无数台机器、操作系统和应用程序上的实验这条准则已经被再三地验证过。80-20准则不只是一条好记的惯用语,它更是一条有关系统性能的指导方针,它有着广泛的适用性和坚实的实验基础。

 

软件整体的性能通常取决于程序中的一小部分代码。

 

正确的方法是用profiler程序识别出令人讨厌的程序的20%部分。不是所有的工作都让profiler去做。你想让它去直接地测量你感兴趣的资源。请记住profiler仅能够告诉你在某一次运行(或某几次运行)时一个程序运行情况,所以如果你用不具有代表性的输入数据profile一个程序,那你所进行的profile也没有代表型。相反这样做很可能导致你去优化不常用的软件行为,而在软件的常用领域,则对软件整体的效率起相反作用(即效率下降)。

 

要防止这种不正确的结果,最好的方法是用尽可能多的数据profile你的软件。

 

Item17. 考虑使用延迟计算:

  

关键是要懒惰Lazy一些。
以下为四个应用场景:

 
引用计数(Reference Counting)
lazy evaluation:除非你却是需要,不要一上来就制作一个拷贝。我们应该是懒惰的,只要可能就共享使用别人的值。

 

区别对待读取和写入operator[]
在Item30中利用代理类可以实现这一点

 

延迟读取(Lazy Fetching)
假设你的程序使用了一些包含许多字段的大型对象。这些对象的生存期超越了程序运行期,所以它们必须被存储在数据库里。每一个对象都有一个唯一的对象标识符,用来从数据库中重新获得对象:因为LargeObject对象实例很大,为这样的对象获取所有的数据,数据库的操作的开销将非常大,特别是如果从远程数据库中获取数据和通过网络发送数据时。而在这种情况下,不需要读去所有数据。

当LargeObject对象被建立时,不从磁盘上读取所有的数据,而是仅仅建立一个对象“壳“,当需要某个数据时,这个数据才被从数据库中取回。这种“demand-paged”对象初始化的实现方法是

延迟表达式计算(Lazy Expression Evaluation)

应该建立一个数据结构来表示m3的值是m1与m2的和,在用一个enum表示它们间是加法操作。很明显,建立这个数据结构比m1与m2相加要快许多,也能够节省大量的内存。考虑程序后面这部分内容,在使用m3之前,代码执行如下:
Matrix<int> m4(1000, 1000);
...                                           // 赋给m4一些值
m3 = m4 * m1;
现在我们可以忘掉m3是m1与m2的和(因此节省了计算的开销),在这里应该记住m3是m4与m1运算的结果。不用说,我们不会马上进行这个乘法。

一个更常见的应用领域是当我们仅仅需要计算结果的一部分时。例如假设我们初始化m3的值为m1和m2的和,然后象这样使用m3:
cout << m3[4];                                // 打印m3的第四行
很明显,我们不能再懒惰了,应该计算m3的第四行值。但是我们也不需要费太大的劲,没有理由计算m3第四行以外的结果;m3其余的部分仍旧保持未计算的状态直到确实需要它们的值。

 

Item18. 分期摊还预期的计算开销:


即时计算(eager evaluation)是在函数被调用时计算。而提前计算(over-eager evaluation)是预计某个计算会被频繁调用时就可以设计一个数据结构高效地处理这些计算需求,这样可以降低每次计算需求的开销。

 

以下程序使用了标准模板库(STL)里的map对象作为本地缓存,这样就不需要每次从数据库中查询。

(有一个代码细节需要解释一下,最后一个语句返回的是(*it).second,而不是常用的it->second。为什么?答案是这是为了遵守STL的规则。简单地说,iterator是一个对象,不是指针,所以不能保证”->”被正确应用到它上面。但是STL明确要求”.”和”*”在iterator上是合法的,所以(*it).second在语法上虽然比较繁琐,但是保证能运行。)

(thy:即空间换时间。如TSP问题中可以提前计算出城市距离矩阵)

 

Item19. 了解临时对象的来源:
在C++中真正的临时对象是看不见的,它们不出现在你的源代码中。一个无命名的非堆(non-heap)对象就是临时对象。这种未命名的对象通常在两种条件下产生:为了使函数成功调用而进行隐式类型转换和函数返回对象时。

仅当通过传值(by value)方式传递对象或传递常量引用(reference-to-const)参数时,才会发生这些类型转换。当传递一个非常量引用(reference-to-non-const)参数对象,就不会发生。

在任何时候只要见到常量引用(reference-to-const)参数,就存在建立临时对象而绑定在参数上的可能性。在任何时候只要见到函数返回对象,就会有一个临时对象被建立(以后被释放)。

 

Item20. 协助编译器实现返回值优化:
相信我:一些函数(operator*也在其中)必须要返回对象。这就是它们的运行方法。不要与其对抗,你不会赢的。
你所应该关心的是把你的努力引导到寻找减少返回对象的开销上来,而不是去消除对象本身。

诀窍就是返回带有参数的构造函数而不是直接返回对象:

通过这个表达式建立一个临时的Rational对象,
Rational(lhs.numerator() * rhs.numerator(),

         lhs.denominator() * rhs.denominator());
函数的返回值正是这人上临时对象的拷贝。C++规则允许编译器优化超出生存周期的临时对象(temporary objects out of existence)。

Rational c = a * b;                          // 在这里调用operator*
编译器就会被允许消除在operator*内的临时变量和operator*返回的临时变量。它们能在为目标c分配的内存里构造return表达式定义的对象。如果你的编译器这样去做,调用operator*的临时对象的开销就是零:没有建立临时对象。

 

Item21. 通过函数重载避免隐式类型转换:

现在考虑下面这些语句:
upi3 = upi1 + 10;
upi3 = 10 + upi2;

这些语句也能够成功运行。方法是通过建立临时对象把整形数10转换为UPInts。还有一种方法可以成功进行operator的混合类型调用,它将消除隐式类型转换的需要。如果我们想要把UPInt和int对象相加,通过声明如下几个函数达到这个目的,每一个函数有不同的参数类型集。

一旦你开始用函数重载来消除类型转换,你就有可能这样声明函数,把自己陷入危险之中:
const UPInt operator+(int lhs, int rhs);           // 错误!

在C++中有一条规则是每一个重载的operator必须带有一个用户定义类型(user-defined type)的参数。
不过,必须谨记80-20规则(参见条款16)。没有必要实现大量的重载函数,除非你有理由确信程序使用重载函数以后其整体效率会有显著的提高。

 

Item22. 考虑使用op=来取代单独的op运算符:

第一、总的来说operator的赋值形式比其单独形式效率更高,因为单独形式要返回一个新对象,从而在临时对象的构造和释放上有一些开销。operator的赋值形式把结果写到左边的参数里,因此不需要生成临时对象来容纳operator的返回值。

 

第二、提供operator的赋值形式以及其标准形式,可以允许类的客户端在便利与效率上做出折衷选择。也就是说,客户端可以决定是这样编写:
Rational a, b, c, d, result;
...
result = a + b + c + d;            // 可能用了3个临时对象,每个operator+ 调用使用1个

 

还是这样编写:
result = a;                                  //不用临时对象
result += b;                                 // 不用临时对象
result += c;                                 //不用临时对象
result += d;                                 //不用临时对象

前者比较容易编写、debug和维护,并且在80%的时间里它的性能是可以被接受的。后者具有更高的效率,估计这对于汇编语言程序员来说会更直观一些。

最后一点,涉及到operator单独形式的实现。由于历史的原因,无名字的对象比有名字的对象更容易清除,因此当我们面对在命名对象和临时对象间进行选择时,用临时对象更好一些。它使你耗费的开销不会比命名的对象更多,特别是使用老编译器时,它的耗费会更少。


Item23. 考虑使用其他等价的程序库:

当把正整数的自然对数传给这个程序,它会这样输出:
0.00000   0.69315   1.09861   1.38629   1.60944
1.79176   1.94591   2.07944   2.19722   2.30259
2.39790   2.48491   2.56495   2.63906   2.70805
2.77259   2.83321   2.89037   2.94444   2.99573
3.04452   3.09104   3.13549   3.17805   3.21888

 

使用iostreams也能这种也能产生fixed-format I/O。当然,远不如  printf("%10.5f", d); 输入方便。
但是操作符<<既是类型安全(type-safe)又可以扩展,而printf则不具有这两种优点。

本节的主旨是:提供类似功能的不同的程序库在性能上采取不同的权衡措施,所以一旦你找到软件的瓶颈(通过进行 profile 参见条款16),你应该知道是否可能通过替换程序库来消除瓶颈。

 

Item24. 理解虚函数、多重继承、虚基类以及RTTI所带来的开销


当调用一个虚拟函数时,被执行的代码必须与调用函数的对象的动态类型相一致;指向对象的指针或引用的类型是不重要的。编译器如何能够高效地提供这种行为呢?大多数编译器是使用virtual table和virtual table pointers。virtual table和virtual table pointers通常被分别地称为vtbl和vptr。


虚函数所需的第一个开销:你必须为每个包含虚函数的类的virtual talbe留出空间。

 

虚函数所需的第二个开销是:在每个包含虚函数的类的对象里,你必须为它的对象付出一定的开销来存放一个额外的指针。

 

为了找到一个虚函数的地址编译器生成的代码会做如下这些事情:
通过对象的vptr找到类的vtbl。找到对应vtbl内的指向被调用函数的指针。
调用第二步找到的的指针所指向的函数。这与非虚函数的调用在效率上相差无几。

 

而虚函数在运行时刻的真正开销与内联函数有关。实际上虚函数不能是内联的。这是因为“内联”是指“在编译期间用被调用的函数体本身来代替函数调用的指令,”但是虚函数的“虚”是指“直到运行时才能知道要调用的是哪一个函数。”如果编译器在某个函数的调用点不知道具体是哪个函数被调用,你就能理解为什么它不会内联该函数的调用。

 

这是虚函数所需的第三个开销:你实际上必须放弃内联函数。(当通过对象调用的虚函数时,它可以被内联,但是大多数虚函数是通过对象的指针或引用被调用的,这种调用不能被内联。因为这种调用是标准的调用方式,所以虚函数实际上不能被内联。)
实际上现在的编译器一般总是忽略虚函数的的inline指令。

多继承的引入,使得事情将会变得更加复杂。

下面这个表是对虚函数、多继承、虚基类以及RTTI所需主要开销的总结:

Feature

Increases
Size of Objects

Increases
Per-Class Data

Reduces
Inlining

Virtual Functions

Yes

Yes

Yes

Multiple Inheritance

Yes

Yes

No

Virtual Base Classes

Often

Sometimes

No

RTTI

No

Yes

No

请记住如果没有这些特性所提供的功能,你必须手工编码来实现。在多数情况下,你的手工模拟可能比编译器生成的代码效率更低,稳定性更差。

联邦学习实验复现指南:FedAvg到FedOur三组对比与避坑 联邦学习作为一种分布式机器学习范式,其核心在于多客户端协同训练与模型聚合。通过本地更新与全局聚合的迭代,能在保护数据隐私的同时提升模型泛化能力。在工程实践中,数据划分、客户端采样和聚合策略直接影响实验复现效果。本文基于PyTorch实现,详细解析Cifar-10、MedMNIST和Chest X-Ray三组对比实验,涵盖FedAvg、FedPer、FedRep与FedOur算法,并提供环境搭建、参数配置及常见问题排查指南,助力快速验证算法改动。 阅读详情

相关推荐

Docker一键部署NextCloud+OnlyOffice:打造企业级私有云办公环境(含HTTPS配置)

本文详细介绍了如何使用Docker Compose一键部署NextCloud与OnlyOffice,构建安全、高效的企业级私有云办公平台。内容涵盖从架构设计、Docker编排配置、HTTPS证书自动化管理,到系统初始化、性能优化及高级功能扩展的全栈实战指南,帮助企业实现数据自主可控与高效的在线文档编辑协作。

Apple的专栏 1242

More Effective C++

一、基础议题 Item 1. 区分指针和引用 Item 2. 优先考虑C++风格的类型转换 Item 3. 决不要把多态用于数组 Item 4. 避免不必要的默认构造函数 二、运算符 Item 5. 小心用户自定义的转换函数 Item 6. 区分自增运算符和自减运算符的前缀形式与后缀形式 Item 7. 不要重载"&&"、"||"和"," Item 8. 理解new和dele

kobejayandy的专栏 868

XLSX Workbench 在 ABAP 世界的定位与实战:从概念到完整示例

XLSX Workbench 在可视化、易维护与批量后台生成三方面填补了 ABAP 报表到 Excel 的空白。它与官方类及社区明星 abap2xlsx 形成了“模板驱动、代码驱动、二者兼顾”的互补格局。掌握 Workbench 后,开发者能把精力集中在业务逻辑以及数据质量,而把排版细节交给图形化工具,大幅加速交付与迭代。通过本文提供的示例,你可以立即在项目中落地并尝到效率提升的红利。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 525

读书笔记 effective c++ Item 25 实现一个不抛出异常的swap

1. swap如此重要 Swap是一个非常有趣的函数,最初作为STL的一部分来介绍,它已然变成了异常安全编程的中流砥柱(Item 29),也是在拷贝中应对自我赋值的一种普通机制(Item 11)。Swap非常有用,恰当的实现swap是非常重要的,与重要性伴随而来的是一些并发症。在这个条款中,我们将探索这些并发症以及如何处理它们。 2. swap的傻瓜实现方式及缺陷 2.1 swap函数的默认

何晓龙的博客 312

【35】软考软件设计师——设计模式代码填空(C++版)满分精讲|第5/6题二选一必考

本文是《软件设计师·50讲通关|从零基础到工程师职称》专栏第35篇,聚焦下午选做题**第5/6题C++版设计模式代码填空**,与上一篇Java版形成双子配套,单题分值稳定12~15分。全文超4000字深度拆解C++独有核心考点:虚函数/纯虚函数、构造与析构函数规范、继承权限、模板类编程在设计模式中的应用;精讲单例、工厂、策略、观察者、适配器五大高频模式的C++专属代码写法,标注所有填空核心点位、语法陷阱、内存安全规范

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 545

Python爬虫实战:使用最新技术爬取路透社新闻数据

网络爬虫(Web Crawler)是一种自动获取网页内容的程序,是搜索引擎的重要组成部分。随着大数据和人工智能技术的发展,爬虫技术在数据采集、市场分析、舆情监控等领域发挥着越来越重要的作用。requests:同步HTTP请求库aiohttp:异步HTTP请求库:HTML解析库lxml:高效的HTML/XML解析库selenium:浏览器自动化工具playwright:现代浏览器自动化工具pandas:数据处理和分析库scrapy:专业的爬虫框架:生成随机User-Agentredis:缓存和分布式爬虫支持。

2201_76125261的博客 1204

读书笔记 effective c++ Item 12 拷贝对象的所有部分

1.默认构造函数介绍 在设计良好的面向对象系统中,会将对象的内部进行封装,只有两个函数可以拷贝对象:拷贝构造函数和拷贝赋值运算符。我们把这两个函数统一叫做拷贝函数。从Item5中,我们得知,如果需要的话编译器会为你生成这两个拷贝函数,并且编译器生成的版本能够精确的做到你想做的:它们拷贝了对象的所有数据。 2.自己实现构造函数有可能出现问题 当你声明自己...

weixin_34143774的博客 84

智能车图像处理21-进阶篇13--正入十字补线2

这篇文章主要的正入十字补线与1基本相同,主要防止图像中双十字的问题。

taiyuezyh的博客 2034

读书笔记 effective c++ Item 50 了解何时替换new和delete 是有意义的

1. 自定义new和delete的三个常见原因 我们先回顾一下基本原理。为什么人们一开始就想去替换编译器提供的operator new和operator delete版本?有三个最常见的原因: 为了检测内存使用错误。不能成功delete new出来的内存会造成内存泄漏。在new出来的内存上使用多于一次的delete会产生未定义行为。如果operator new持有一份内存分配的列表,并且ope...

HarlanC的专栏 176

微信小程序 - 详细解决web-view公众号文章或第三方网站分享转发后打开失败,页面显示“无法打开该页面,不支持打开“、“页面不存在“报错提示,苹果IOS系统手机打开是空白页,安卓手机系统会有提示!

微信小程序,公众号文章,第三方H5网页,内嵌网站分享后打不开,web-view,页面分享,小程序里公众号文章转发后无法打开,白屏,页面空白什么都没有,不支持打开,无法打开该页面,页面不存在,业务域名配置好了还是不行,公众号和小程序已经关联,iOS端苹果真机打开空白页,安卓手机,页面分享后,用户打不开提示错误,提示页面不存在,如何解决页面提示无法打开该页面,微信小程序web-view无法打开该页面不支持打开的解决方法,无法打开该页面不支持打开,最新详细解决方案,分享之后无法打开页面,提示无法打开该页面,不支持

🏆 前端领域优质创作者 1万+

强化学习算法案例——一个经典的走迷宫任务。

作者:禅与计算机程序设计艺术 1.简介 在强化学习(Reinforcement Learning, RL)中,有一个经典的任务——走迷宫,其原理简单易懂,但它的实际意义却深远,它让计算机学习如何通过策略网络(policy network),以最佳的方式解决复杂的任务。本文将介绍如何用强化学习解决这个经典

AI天才研究院 3218

读书笔记 effective c++ Item 21 当你必须返回一个对象的时候,不要尝试返回引用

1. 问题的提出:要求函数返回对象时,可以返回引用么? 一旦程序员理解了按值传递有可能存在效率问题之后(Item 20),许多人都成了十字军战士,决心清除所有隐藏的按值传递所引起的开销。对纯净的按引用传递(不需要额外的构造或者析构)的追求丝毫没有懈怠,但他们的始终如一会产生致命的错误:它们开始传递指向并不存在的对象的引用。这可不是好事情。 考虑表示有理数的一个类,它包含将两个有理数相乘的函数(

天空永远蔚蓝的博客 393

YOLO26涨点改进 | 全网独家,注意力创新改进篇 | Arxiv2025 | YOLO26引入MSLA多尺度线性注意力模块,高效提取多尺度特征和建模长程依赖,含多种创新改进,助力有效涨点

本文提出了一种多尺度线性注意力模块(MSLA)来优化YOLO26模型。MSLA通过并行使用不同尺寸的深度可分离卷积(3×3至9×9)提取多尺度特征,并结合线性注意力机制高效建模全局依赖关系。该模块显著提升了模型对多尺度目标的检测能力,同时保持了轻量化特性。实验表明,MSLA能有效增强小目标检测和复杂场景下的分割精度。文章详细介绍了MSLA的网络结构、核心优势及实现代码,并提供了两种集成MSLA的YOLO26改进方案,为实时目标检测任务提供了高效解决方案。

Ai缝合怪、助力小伙伴高效发论文 302

读书笔记 effective c++ Item 21 当你必须返回一个对象的时候,不要尝试返回引用...

1. 问题的提出:要求函数返回对象时,可以返回引用么? 一旦程序员理解了按值传递有可能存在效率问题之后(Item 20),许多人都成了十字军战士,决心清除所有隐藏的按值传递所引起的开销。对纯净的按引用传递(不需要额外的构造或者析构)的追求丝毫没有懈怠,但他们的始终如一会产生致命的错误:它们开始传递指向并不存在的对象的引用。这可不是好事情。 考虑表示有理数的一个类,它包含将两个有理数相乘的函数(...

HarlanC的专栏 166

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

[规范] c++Windows平台代码规范

微软风格=匈牙利风格=函数大驼峰+变量小驼峰+标明类型 例如InitTool(), mIsOpen, bOpen等谷歌偏c风格 下划线很多 谷歌风格linux使用比较多,例如init_tool(),m_is_open, is_open等好的编程风格有利于快速理解代码,好的编码习惯也是编码的基本。

二次元怪兽的博客 2842

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

More Effective C++ 读书摘要(目录索引)

一、基础议题 Item 1. 区分指针和引用Item 2. 优先考虑C++风格的类型转换Item 3. 决不要把多态用于数组Item 4. 避免不必要的默认构造函数二、运算符Item 5. 小心用户自定义的转换函数Item 6. 区分自增运算符和自减运算符的前缀形式与后缀形式Item 7. 不要重载"&&"、"||"和","Item 8. 理解new和del

thy专栏 1844

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

More Effective C++ 读书摘要(auto_ptr的一个实现)

下面是两个auot_ptr的实现。第一个版本文档化了类的接口并在类的定义体外面实现了所有的成员函数。第二个版本将所有的成员函数都实现在定义体内了。 在风格上,第二个实现不如第一个,因为它没有将类的接口从实现中分离出来。但是auto_ptr只是一个简单的类,所以第二个实现比第一个清晰得多。这是把auto_ptr的接口和实现分开的版本:templateclass auto_ptr {

thy专栏 1250

More Effective C++ 读书摘要(五、技巧1)Item25 - 27

 Item25. 使构造函数和非成员函数具有虚函数的行为:有三个类,注意其继承关系:class NLComponent { //用于 newsletter components public: // 的抽象基类 ... //包含只少一个纯虚函数}

thy专栏 1077
上一篇: More Effective C++ 读书摘要(三、异常)Item9 - 15
下一篇: More Effective C++ 读书摘要(五、技巧1)Item25 - 27
thy38
博客等级 码龄19年 184粉丝 116原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值