当前位置: 首页 > article >正文

More Effective C++:改善编程与设计(上)

More Effective C++

目录

More Effective C++:

条款1:仔细区别pointers和 references

条款2:最好使用C++转型操作符

条款3:绝对不要以多态方式处理数组

条款4:非必要不要提供default constructor

条款5:对定制的“类型转换函数”保持警觉

条款6:区别increment/decrement操作符的前置和后置形式

条款7:千万不要重载&&,||和,操作符

条款8:了解各种不同意义的new和delete

条款9:利用destrucotors避免泄漏资源

条款10:在constructors内阻止资源泄漏

条款11:禁止exception流出destructors之外

条款12:了解“抛出一个exception”与“传递一个参数”或“调用一个虚函数”之间的差异

条款13:以by reference方式捕捉exceptions

条款14:明智运用exception specifications

条款15:了解异常处理的成本

条款16:谨记80-20法则

条款17:考虑使用lazy evaluation

条款18:分期摊还预期的计算成本


条款1:仔细区别pointers和 references

        引用不存在空引用,而指针可以存在空指针,并且指针的值是可以修改的,但是引用总是会代表某个对象。
        结论:当你知道需要指向某个东西,而且绝不会改变指向其他东西,或者当你实现一个操作符而其语法需求无法用指针达成,比如:operator */operator [],你应该选择引用,其他时候请采用指针。

条款2:最好使用C++转型操作符

        C语言旧式转型允许你将任何类型转换为任何其他类型,并且难以辨识,为此C++引入四个转型操作符:
static_cast,
reinterpret_cast,
const_cast,
dynamic_cast
        过去的(type)expression 应该改为 static_cast<type>(expression) ;
        四个转型操作符已经在前面讲过,现在来总结一下应用场景:
  • static_cast:可以进行类型具有一定相关性的转换,以及不涉及继承机制的类型执行转型动作;
  • const_cast:用来改变表达式中的常量性或变易性;
  • dynamic_cast:用来执行继承体系中“安全的向下转型或跨系转型动作”,如果转型成功则是正常指针,失败会以一个空指针或者异常表现出来,但是只能用于继承体系中,无法应用在缺乏续虚函数的类型上,也不能改变常量性
  • reinterpret_cast:通常用于函数指针转型动作,并不具备移植性,慎用!

条款3:绝对不要以多态方式处理数组

        假如你有一个class BST以及一个继承自BST的class BalancedBST,如果你将一个BalancedBST对象所组成的数组交给一个BST类型的变量进行打印,编译能够通过,但是当你试图用opearator []去访问数组时会出现问题,指针会假设访问的对象每一个都是BST的大小,但实际上每一个元素大小是BalancedBST的大小,由于派生类比基类占用的空间更大,因此访问出错。
总结:多态和指针算术不能够混用,数组对象几乎总是会涉及指针的算术运算,所以数组
和多态不要混用。

条款4:非必要不要提供default constructor

        在类中到底需不需要提供默认构造函数呢?这取决于具体情况,一方面如果要求类可以合理地从无到有生成对象,并且要依赖于模版,亦或是要实现一个虚基类,那么就需要提供默认构造函数;但如果必须有某些外来信息才能生成对象的类,则不需要提供默认构造函数,因为构造出来对象包含没有任何意义的值,无意义的默认构造函数也会影响代码运行效率,视情况而定。

条款5:对定制的“类型转换函数”保持警觉

        C++允许编译器在不同类型之间支持隐式类型转换,可以通过两种函数实现:
单参数构造函数以及隐式类型转换操作符,
        后者更加容易引发问题:形如operator 类型名称函数,它的出现可能导致错误的函数调用:
Rational r(1,2); count<<r;
你希望打印出r的信息,但是如果没有提供operator <<却提供了operator double,那么编
译仍会通过,但打印出的却不是期望信息。
总结:只要不声明隐式类型转换操作符即可解决,对于单参数构造函数建议使用explicit关键字来避免隐式类型转换,同时支持显式转换。

条款6:区别increment/decrement操作符的前置和后置形式

        C++扩充了++和—操作符的两种形式:
UPInt& operator++(); 前置式;
const UPInt operator++(int); 后置式;
UPInt& operator—(); 前置式;
const UPInt operator—(int); 后置式;
        后置式的int参数的唯一目的是为了区别前置式和后置式,并且返回const UPInt,这是为了
防止++++的情况发生,使用时应该尽可能使用前置式。

条款7:千万不要重载&&,||和,操作符

        原因如下:C++对于真假值表达式采用“骤死式”评估方式,在&&和||操作符左边的表达
式的真假值一旦确定即使右边的表达式尚未检验,整个语句结束,但是一旦你重载了操作符函
数,两边的表达式都需要完成评估,不仅如此,还无法确定评估顺序。至于,同样如此,你绝
对无法保证左侧的表达式一定比右侧的表达式更早被评估。
总结:如果你将它们重载,就没有办法提供程序员预期的某种行为模式,所以请不要重载
这三个操作符函数。

条款8:了解各种不同意义的new和delete

        在你new一个类型的时候,这种操作被称为new operator,会进行两件事情:
        1.调用operator new来分配足够的内存;
        2.调用构造函数,为分配的内存中的那个对象呢设定初始值。
        介绍一下operator new: void* operator new(size_t size);你可以将其重载。
        当你需要在已经分配好的原始内存上构建对象,那么可以使用定位new(Placement new):
        假设A为一个类,如果要在已经有地址为buffer的内存上构建size个对象,可以这样:
A* constructor(void* buffer,size_t size)
{return new(buffer)A(size);
}
        你可以理解为定位new的作用就是将获得的指针再返回,来作为放置的地址,定位new会去
调用operator new,看起来像这样:
void* operator new(size_t,void* location)
{return location;
}
        这里的size_t参数没有用到但一定要有,
小总结:如果你希望将对象产生于堆,使用new operator,如果你只是打算分配内存,使用operator new,如果你打算自己决定内存分配方式,可以重载一个operator new,如果你打算在已分配的内存中构造对象,使用placement new,它会去调用构造函数。
        在你delete一个类型的时候,这种操作被称为new delete,会进行两件事情:
        1.调用析构函数;
        2.调用operator delete释放内存,
        如果你使用placement new,你应该避免使用delete operator,因为它会调用operator delete来释放内存,但是起初内存不是由operator new得来的,你应该直接调用该对象的析构函数。
补充:如果是new一个数组,那么会调用operator new[],再依次调用构造函数,delete []
时会依次调用析构函数,最后调用operator delete[]。

条款9:利用destrucotors避免泄漏资源

        当你在一个函数中实例化一个类后,你想要调用类中的函数并delete掉它,但如果函数调用
时抛出了exception,那么delete语句会被跳过,造成内存泄露, 因此你通常可以通过捕获执行
函数的异常来在throw语句之前执行delete语句,还可以使用智能指针 :采用RAII资源获取即初 始化的思想,将资源封装在对象内,通常可以在exceptions出现时避免泄漏资源。

条款10:在constructors内阻止资源泄漏

        如果你设计的类中constructors调用了new,在构造时申请空间失败,抛出了一个
exception会怎么样呢?C++只会析构已经构造完成的对象,现在constructor没有执行完毕,
不会调用类中的析构函数,会发生内存泄露,解决办法可以如下: 将所有exception捕捉起
来,执行某种清理工作,然后重新抛出exception,而执行清理工作不必刻意进行,可以使用
智能指针来管理资源。

条款11:禁止exception流出destructors之外

        在destructors中抛出异常,会导致资源释放不完全,导致内存泄露,并且如果没有try…
catch语句捕捉异常,会导致terminate函数在exception传播过程中的栈展开机制中被调用,
程序立即终止。

条款12:了解“抛出一个exception”与“传递一个参数”或“调用一个虚函数”之间的差异

        “传递对象到函数去,或是以对象调用虚函数”和“将对象抛出成为一个exception”之间,有
三个主要的差异。
  • 第一,exception object总是会被复制,不论是自定义类型,内置类型或是静态类型,catch端捕捉的永远是throw端的副本,发生修改也只会影响到副本,如果以by value的方式捕捉,甚至会被复制两次。但是传递给函数参数的对象则不一定得复制。
  • 第二,“被抛出成为exception的对象,其被允许的类型转换动作,比“被传递到函数去”的对象少。
  • 第三,catch子句以其“出现于源代码的顺序”被编译器检验比对,其中第一个匹配成功者便执行;而当我们以某对象调用一个虚函数,被选中执行的是那个“与对象类型最佳吻合”的函数,不论它是不是源代码所列的第一个。

条款13:以by reference方式捕捉exceptions

        exception objects有三种方式传递到子句:by pointer,by value,by reference
        如果是by pointer,类似这样: static exception ex; throw &ex; 但是往往掉了static会使得指针指向不复存在的对象,即便是正确接收,那么是否应该调用delete?对于分配于heap的指针没有问题,其他则会招致未受定义的程序行为,所以无论如何必须以by value或by reference的方式捕捉它们。  
Catch-by-value中每当exception object被抛出,就会被复制两次,此外也会引起切割问题,将throw端的派生类切割赋值给catch端时,catch端将失去原派生类的派生成分,并且无法触发多态,重写的虚函数不能发挥作用。 Catch-by-reference,不会发生对象删除问题,没有切割问题,而且exception object也只会被复制一次, 也可以正常出发多态调用,所以强烈建议使用Catch exceptions by reference!

条款14:明智运用exception specifications

        异常声明确实对于“函数希望抛出什么样的exceptions”提供了卓越的说明,但是编译器允
许你调用“可能违反当前函数本身的异常声明”的函数,并且由于如此的调用行为可能导致程序
被迫中止,为了unexpected函数被调用有三个做法:
  • 不应该将templates和exception specifications混合使用;
  • 函数内调用函数无异常声明,那么函数本身也不应该有异常声明;
  • 处理系统可能抛出的exceptions,异常声明是一把双面刃,请谨慎使用!

条款15:了解异常处理的成本

        exception的处理机制首先需要付出 一些空间 来放置某些数据结构(记录哪些对象已被完全
构造妥当)和 一些时间 ,随时保持那些数据结构的正确性;其次try语句块和异常声明需要的成
本相似,对于这块,你只需要了解异常的处理需要消耗部分性能即可。

条款16:谨记80-20法则

        法则内容: 一个程序80%的资源用于20%的代码上, 重点在于:软件的整体性能聚会总是
由其构成要素的一小部分决定。为了提升程序的性能,你可以使用程序分析器或者尽可能使用
最多的数据来分析软件,专注于特别耗时的地方来加以改善。

条款17:考虑使用lazy evaluation

        缓式评估可以在多种场合派上用场:
  • Reference Counting(引用计数):举例string的写时拷贝,如果任何拷贝赋值都采用深拷贝,这就是急式评估,会消耗很多空间,但如果采用数据共享,使用引用计数的方式管理内存,直到字符串需要被修改发生深拷贝就可以节省大量空间。
  • 区分读和写:以string为例,如果只是读取就不需要进行深拷贝,但如果要写入则需要
  • Lazy Fetching(缓式取出):如果想要引入大型对象,只需产生该对象的外壳,不从磁盘中读取任何字段数据,当对象内的某个字段被需要了,程序才从数据库中取回对应的数据
  • Lazy Expression Evaluation(表达式缓评估):如果用m3来表示大型对象m1和m2的某种运算结果,可以用m3来存储一个数据结构,包含两个指针和一个enum,前者指向m1和m2,后者用来指示运算动作是什么,如果在m3被使用之前,被赋予了新的值,或者接受另一个复杂运算结果,那么就节省了运算m1和m2的成本。
注意:缓式评估并不会影响到必需的对象复制,如果计算是必要的,缓式评估并不会为你的程序节省任何工作,只有当计算可以被避免时,缓式评估才会发挥真正的用处。

条款18:分期摊还预期的计算成本

        这里用到的思想是:over-eager evaluation,如果你预期程序常常会用到某个计算,你可
以降低每次计算的平均成本,办法就是设计一份数据结构以便能够极有效率的处理需求。
        其中一个简单做法就是“将已经计算好而可能再次被需要”的数值保留下来,方便下一次拿取来降低平均成本,称之为Caching;还有将数组扩容时,将空间调整到所需大小更大一些的做法,被称为Prefetching 前者会消耗较多内存,但可以降低那些已被计算出的结果重新计算的时间,后者需要一些空间来放置被预先取出的东西,但可降低访问它们所需的时间。

相关文章:

More Effective C++:改善编程与设计(上)

More Effective C&#xff1a; 目录 More Effective C&#xff1a; 条款1&#xff1a;仔细区别pointers和 references 条款2:最好使用C转型操作符 条款3:绝对不要以多态方式处理数组 条款4:非必要不要提供default constructor 条款5:对定制的“类型转换函数”保持警觉 …...

TNNLS-2020《Autoencoder Constrained Clustering With Adaptive Neighbors》

核心思想分析 该论文提出了一种名为ACC_AN&#xff08;Autoencoder Constrained Clustering with Adaptive Neighbors&#xff09;的深度聚类方法&#xff0c;旨在解决传统子空间聚类方法在处理非线性数据分布和高维数据时的局限性。核心思想是将深度自编码器&#xff08;Auto…...

SpringAI

机器学习&#xff1a; 定义&#xff1a;人工智能的子领域&#xff0c;通过数据驱动的方法让计算机学习规律&#xff0c;进行预测或决策。 核心方法&#xff1a; 监督学习&#xff08;如线性回归、SVM&#xff09;。 无监督学习&#xff08;如聚类、降维&#xff09;。 强化学…...

lua 作为嵌入式设备的配置语言

从lua的脚本中获取数据 lua中栈的索引 3 | -1 2 | -2 1 | -3 可以在lua的解释器中加入自己自定的一些功能,其实没啥必要,就是为了可以练习下lua...

ERP系统源码,小型工厂ERP系统源码,CRM+OA+进销存+财务

ERP系统源码&#xff0c;小型工厂ERP系统源码&#xff0c;ERP计划管理系统源码&#xff0c;CRMOA进销存财务 对于ERP来说&#xff0c;最为主要的作用就是能够强调企业的计划性&#xff0c;通过以业务订单以及客户的相关需求来作为企业计划的基础&#xff0c;并且还能够对企业现…...

基于EFISH-SCB-RK3576/SAIL-RK3576的矿用本安型手持终端技术方案‌

&#xff08;国产化替代J1900的矿山智能化解决方案&#xff09; 一、硬件架构设计‌ ‌本安型结构设计‌ ‌防爆防护体系‌&#xff1a; 采用铸镁合金外壳复合防爆玻璃&#xff08;抗冲击能量>20J&#xff09;&#xff0c;通过GB 3836.1-2021 Ex ib I Mb认证 全密闭IP68接口…...

配置文件介绍xml、json

#灵感# 常用xml&#xff0c; 但有点模棱两可&#xff0c;记录下AI助理给我总结的。 .xml XML&#xff08;eXtensible Markup Language&#xff0c;可扩展标记语言&#xff09;是一种用于存储和传输数据的标记语言。它与 HTML 类似&#xff0c;但有以下主要特点和用途&#xf…...

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】附录-D. 扩展插件列表(PostGIS/PostgREST等)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 附录D. PostgreSQL扩展插件速查表一、插件分类速查表二、核心插件详解三、安装与配置指南四、应用场景模板五、版本兼容性说明六、维护与优化建议七、官方资源与工具八、附录…...

Qt笔记---》.pro中配置

文章目录 1、概要1.1、修改qt项目的中间文件输出路径和部署路径1.2、Qt 项目模块配置1.3、外部库文件引用配置 1、概要 1.1、修改qt项目的中间文件输出路径和部署路径 &#xff08;1&#xff09;、为解决 “ 输出文件 ” 和 “ 中间输出文件 ”全部在同一个文件夹下的问题&am…...

【Liblib】基于LiblibAI自定义模型,总结一下Python开发步骤

一、前言 Liblib AI&#xff08;哩布哩布 AI&#xff09;是一个集成了先进人工智能技术和用户友好设计的 AI 图像创作绘画平台和模型分享社区。 强大的图像生成能力 &#xff1a;以 Stable Diffusion 技术为核心&#xff0c;提供文生图、图生图、图像后期处理等功能&#xff…...

CCF第七届AIOps国际挑战赛季军分享(RAG)

分享CCF 第七届AIOps国际挑战赛的季军方案&#xff0c;从我们的比赛经历来看&#xff0c;并不会&#xff0c;相反&#xff0c;私域领域问答的优秀效果说明RAG真的很重要 历经4个月的时间&#xff0c;从初赛赛道第1&#xff0c;复赛赛道第2&#xff0c;到最后决赛获得季军&…...

【Cesium入门教程】第七课:Primitive图元

Cesium丰富的空间数据可视化API分为两部分&#xff1a;primitive API面向三维图形开发者&#xff0c;更底层一些。 Entity API是数据驱动更高级一些。 // entity // 调用方便&#xff0c;封装完美 // 是基于primitive的封装// primitive // 更接近底层 // 可以绘制高级图形 /…...

【5分钟学Docker】Docker快速使用

目录 1. 概述 2. 基本操作 2.1. 镜像操作 2.2. 容器操作 2.3. 运行操作 2.4. 镜像保存 2.5. 镜像分享 3. 高级操作 4. 挂载 4.1. 目录挂载 4.2. 卷映射 1. 概述 Docker 镜像有镜像名称和TAG 2. 基本操作 2.1. 镜像操作 查看镜像 docker images docker image ls …...

opencv 一些简单的设置

输出当前程序启动的路径 可能会出现 &#x1f527; 设置 C17 标准&#xff08;解决 std::filesystem 报错&#xff09; 在 VS 中&#xff0c;右键项目 → 属性。 选择左边的 “C/C” → “语言” 找到 C语言标准&#xff08;C Language Standard&#xff09;选项。 设置为&…...

快速地解决Spring循环依赖问题

循环依赖的大体结构如下&#xff1a; AServiceImpl Slf4j Service AllArgsConstructor public class AServiceImpl extends ServiceImpl<AMapper, A> implements AService {private final BService bService; }BServiceImpl Slf4j Service AllArgsConstructor public …...

反向操作:如何用AI检测工具优化自己的论文“人味”?

大家好&#xff0c;这里是论文写手的一线自救指南&#x1f624; 在AIGC横行的今天&#xff0c;谁还没偷偷用过AI写几段论文内容&#xff1f;但问题来了&#xff1a;学校越来越会“识AI”了&#xff01; 有的学校甚至不看重复率&#xff0c;只盯AIGC率报告&#xff0c;一句“AI…...

CPS联盟+小程序聚合平台分销返利系统开发|小红书番茄网盘CPA拉新推广全解析

导语&#xff1a; 在私域流量与社交电商爆发的时代&#xff0c;CPS联盟分销返利系统与小红书CPA拉新推广成为企业增长的核心引擎。本文深度解析如何通过小程序聚合平台开发、多层级返利机制搭建及精准CPA推广策略&#xff0c;快速占领市场&#xff0c;实现用户裂变与收益倍增。…...

苹果处理器“仿生“命名背后的营销策略与技术创新

苹果处理器"仿生"命名背后的营销策略与技术创新 苹果自2017年推出A11 Bionic芯片以来&#xff0c;其处理器系列便开始采用"仿生"&#xff08;Bionic&#xff09;这一名称。这一命名并非源于芯片模仿生物神经系统的技术突破&#xff0c;而是苹果为提升芯片…...

监控易运维管理软件:架构稳健,组件强大

在当今的信息化时代&#xff0c;运维管理对于企业的稳定运营至关重要。一款好的运维管理软件&#xff0c;不仅能够帮助企业高效管理IT基础设施&#xff0c;还能提升运维效率&#xff0c;降低运维成本。今天&#xff0c;我要给大家介绍的&#xff0c;就是我们公司自主研发的监控…...

【Python】抽象基类ABC

抽象基类(Abstract Base Classes)的核心作用 抽象基类(ABC)是Python中一种特殊的类&#xff0c;它通过abc模块实现&#xff0c;主要服务于面向对象编程中的接口规范和设计约束。以下是它的核心作用&#xff1a; 1. 强制接口实现&#xff08;核心作用&#xff09; 确保子类必…...

数字IC后端零基础入门基础理论(Day2)

数字IC后端零基础入门基础理论&#xff08;Day1&#xff09; Placement Blockage: cell摆放阻挡层。它是用来引导工具做placement的一种物理约束或手段&#xff0c;目的是希望工具按照我们的要求来做标准单元的摆放。 它主要有三种类型&#xff0c;分别是hard placement bloc…...

零成本打造专属AI图像处理平台:IOPaint本地部署与远程访问指南

文章目录 前言1.什么是IOPaint&#xff1f;2.本地部署IOPaint3.IOPaint简单实用4.公网远程访问本地IOPaint5.内网穿透工具安装6.配置公网地址7.使用固定公网地址远程访问总结 前言 移动摄影的普及使得记录生活变得轻而易举&#xff0c;然而获得一张高质量的照片往往需要付出不…...

操作系统-物理结构

操作系统使用read系统调用&#xff0c;将逻辑地址转&#xff08;对于用户来说逻辑地址容易计算&#xff0c;因为各个逻辑块都相邻&#xff09;成了逻辑块号和块内偏移量&#xff0c;并根据分配存储方式&#xff0c;将逻辑块号转成物理块号和块内偏移量 对于用户来说的文件的一…...

CGO中引入 <cstddef> <vector> fatal error: cstddef: No such file or directory 失败的原因

原因 可以在CPP里面引入C的头文件&#xff0c;但不能在h文件引入 错误 fatal error: cstddef: No such file or directory 测试case&#xff0c;下面的可以&#xff0c;如果把他放到头文件就会报错 // main.go package main// #cgo CXXFLAGS: -stdc11 // #cgo LDFLAGS: -l…...

亚马逊电商广告革命:当AI推荐沦为红海陷阱,中国卖家如何破局?

一、算法同质化&#xff1a;跨境电商的广告效率危机 亚马逊广告系统正陷入一场自我迭代的悖论。其力推的AI推荐广告模板&#xff08;2023年覆盖率达78%&#xff09;&#xff0c;本意为降低运营门槛&#xff0c;却意外催生出"广告红海效应"——据Jungle Scout监测数据…...

《AI大模型应知应会100篇》第64篇:构建你的第一个大模型 Chatbot

第64篇&#xff1a;构建你的第一个大模型 Chatbot 手把手教你从零开始搭建一个基于大模型的聊天机器人 摘要 你是否想过&#xff0c;自己也能构建一个像 ChatGPT 一样能对话、能思考的聊天机器人&#xff08;Chatbot&#xff09;&#xff1f;别担心&#xff0c;这并不需要你是…...

STM32 片上资源之串口

STM32 片上资源之串口 1 串口介绍1.1 初步介绍1.2 主要特性1.2.1 USART特性1.2.2 UART特性 1.3 主要寄存器1.4 波特率计算1.5 常用工作模式1.5.1 轮询模式&#xff1a;1.5.2 中断模式&#xff1a;1.5.3 DMA模式&#xff1a; 1.6 常见应用1.7 注意事项 2 软件层面协议2.1 基本概…...

职坐标IT培训:互联网行业核心技能精讲

在互联网行业高速迭代的今天&#xff0c;掌握全链路核心技能已成为职业发展的关键突破口。职坐标IT培训聚焦行业需求&#xff0c;系统拆解从需求分析到系统部署的完整能力模型&#xff0c;助力从业者构建多维竞争力。无论是产品岗的用户调研与原型设计&#xff0c;还是技术岗的…...

FlashInfer - 介绍 LLM服务加速库 地基的一块石头

FlashInfer - 介绍 LLM服务加速库 地基的一块石头 flyfish 大型语言模型服务中的注意力机制 大型语言模型服务&#xff08;LLM Serving&#xff09;迅速成为重要的工作负载。Transformer中的算子效率——尤其是矩阵乘法&#xff08;GEMM&#xff09;、自注意力&#xff08;S…...

MySQL 学习(七)undo log、redo log、bin log 的作用以及持久化机制

目录 一、前言二、三大日志的概念、作用、存储位置2.1 bin log 二进制执行日志2.2 undo log 事务回滚日志2.3 redo log 快速恢复日志 三、补充说明3.1 补充&#xff1a;为什么使用 buffer pool 而不直接修改磁盘中的数据&#xff1f;3.2 补充&#xff1a;同为操作数据变更的日志…...