03.实现
实现
条款26:尽可能延后变量定义式的出现时间
条款27:尽量少做转型动作
条款28:避免返回handles指向对象内部成分
在C++中,handles是指一个指向对象的指针或引用,用于访问该对象的成员函数或成员变量。
而条款28所说的“返回handles指向对象内部成分”,是指返回指向该对象的私有成员或受保护成员的指针或引用。
这样做的问题在于,如果对该成员进行修改或者删除操作,可能会破坏该对象的不变量(invariant)或者类的封装性,导致程序出现不可预测的结果。
另外,如果返回的指针或引用是指向临时对象或已经被释放的对象,则会导致指针悬挂(dangling pointer)或引用无效的问题。
为了避免这种问题,建议使用对象的公有成员函数访问该对象的内部成分,而不是返回指向内部成分的指针或引用。
如果必须返回指针或引用,可以考虑使用智能指针(如std::shared_ptr
或std::unique_ptr
)来确保对象的生命周期,或者返回const指针或引用,防止被修改。
假设有一个类Person
,其中有一个私有变量age
表示年龄,如下所示:
class Person {
public:void SetAge(int age) { this->age = age; } // 设置年龄int GetAge() const { return age; } // 获取年龄
private:int age;
};
如果在该类中返回对age
的引用或指针,就会存在返回handles指向对象内部成分的问题。例如:
const int& GetAgeRef() const { return age; } // 返回对age的引用
const int* GetAgePtr() const { return &age; } // 返回指向age的指针
在这种情况下,如果返回的引用或指针被用于修改或者删除age
,将会破坏Person
类的封装性,导致程序出现不可预测的结果。 为了解决这个问题,可以使用对象的公有成员函数访问该对象的内部成分,而不是返回指向内部成分的指针或引用。例如:
class Person {
public:void SetAge(int age) { this->age = age; } // 设置年龄int GetAge() const { return age; } // 获取年龄void SetAgeViaFunction(int age) { SetAge(age); } // 通过公有成员函数设置年龄
private:int age;
};
在这个例子中,我们新增了一个公有成员函数SetAgeViaFunction
,用于通过公有成员函数SetAge
来设置年龄。这样,外部代码就不能直接访问age
,而是必须通过SetAgeViaFunction
来访问age
,从而保证了Person
类的封装性。
条款29:为“异常安全”而努力是值得的
“异常安全”是指程序在面对异常时,能够保持正确性、资源不泄漏、不破坏数据结构等性质。
在C++中,异常安全是非常重要的,因为C++支持异常机制,如果程序无法正确处理异常,可能会导致资源泄漏、内存泄漏、数据结构损坏等问题,从而影响程序的正确性和稳定性。
请记住:
- 异常安全函数(Exception-safe functions)即使发生异常也不会泄露资源或允许任何数据结构败坏。这样的函数区分为三种可能的保证:基本型、强烈型、不抛异常型。
- “强烈保证”往往能够以 copy-and-swap 实现出来,但“强烈保证“并非对所有函数都可实现或具备现实意义。
- 函数提供的”异常安全保证“通常最高只等于其所调用的各个函数的”异常安全保证“中的最弱者。
因此,为了确保程序的异常安全性,我们需要在编写代码时,考虑并努力实现异常安全。具体来说,我们需要注意以下几点:
-
确保不会让异常逃逸:在函数中抛出异常后,应该确保异常被正确地处理掉,而不是让它逃逸到函数外部,导致资源泄漏或数据结构损坏。为了避免异常逃逸,可以使用RAII技术,通过在对象构造函数中获取资源,在对象析构函数中释放资源,来保证资源的正确获取和释放。
-
考虑异常安全的级别:异常安全性有三个级别,分别是基本保证、弱异常安全和强异常安全。在编写代码时,应该根据情况选择最佳的异常安全级别。
基本保证是指:如果函数出现异常,程序状态不会受到破坏,但是可能会发生资源泄漏。
弱异常安全是指:如果函数出现异常,程序状态和资源都不会受到破坏,但是可能会出现不一致的状态。
强异常安全是指:如果函数出现异常,程序状态和资源都不会受到破坏,而且程序状态和资源都会回到调用函数前的状态。
-
使用异常处理机制:在编写代码时,应该使用异常处理机制来处理异常。通过抛出异常并捕获异常,可以避免程序崩溃,并将异常抛出给调用者进行处理。在抛出异常时,应该提供一个清晰的异常信息,方便调用者进行处理。
条款30:透彻了解inlining的里里外外
条款30中的"inlining"是指C++中的内联函数,它是一种编译器的优化技术,可以将函数调用处直接替换为函数体,从而减少函数调用的开销,提高程序的性能。但是,内联函数也有一些限制和缺点,因此,在使用内联函数时,需要透彻了解其里里外外,以便能够正确地使用和优化内联函数。
具体来说,理解"inlining"的里里外外需要注意以下几点:
- 内联函数的适用范围:内联函数适用于函数体比较小、被频繁调用的函数。如果函数体比较大,内联函数可能会导致代码膨胀,从而影响程序的性能。因此,在使用内联函数时,需要根据实际情况进行选择。
- 内联函数的定义:内联函数通常需要在头文件中进行定义,以便编译器能够在编译时进行函数替换。在定义内联函数时,需要注意函数体不能包含循环、递归等语句,否则会导致编译器无法进行内联。
- 内联函数的声明:内联函数的声明通常也需要在头文件中进行,以便其他文件能够调用内联函数。**在声明内联函数时,需要使用inline关键字,**以便告诉编译器该函数需要进行内联。
- 内联函数的优化:在使用内联函数时,需要注意优化内联函数的代码,以提高程序的性能。可以使用一些技巧,如避免多余的函数调用、避免局部变量过多、使用常量表达式等。
- 内联函数的限制:内联函数也有一些限制,例如不能取地址、不能有静态变量等。在使用内联函数时,需要注意这些限制,以便正确地使用内联函数。
请记住:
- 将大多数inlining限制在小型、被频繁调用的函数身上。这可使日后的调试过程和二进制升级更容易,也可使潜在的代码膨胀问题最小化,使程序的速度提升机会最大化。
- 不要只因为function templates 出现在头文件,就将它们声明为inline
如何理解“不要只因为function templates 出现在头文件,就将它们声明为inline”这句话?
在C++中,函数模板(function templates)是一种通用的函数定义方式,它可以用来定义多个具有相同功能但类型不同的函数。**由于函数模板通常需要在头文件中进行定义,因此很容易将它们声明为内联函数(inline)以提高程序的性能。**但是,在使用函数模板时,不要只因为它们出现在头文件中,就将它们声明为内联函数,而应该根据实际情况进行选择。 具体来说,不要将所有函数模板都声明为内联函数的原因有以下几点:
- 函数模板的大小不确定:由于函数模板可以用于定义多个函数,因此其大小在编译时是不确定的。如果将所有函数模板都声明为内联函数,可能会导致代码膨胀,从而影响程序的性能。
- 内联函数的适用范围有限:内联函数适用于函数体比较小、被频繁调用的函数。对于函数模板来说,其具体调用情况是不确定的,因此并不是所有函数模板都适合声明为内联函数。
- 其他优化方式:除了将函数模板声明为内联函数之外,还有其他一些优化方式,如使用编译器的优化选项、避免多余的函数调用等。在使用函数模板时,应该根据实际情况选择最合适的优化方式。
因此,不要只因为函数模板出现在头文件中,就将它们声明为内联函数。应该根据实际情况进行选择,综合考虑函数模板的大小、调用情况、优化方式等因素,以便选取最合适的优化方案。
如何理解“将大多数inlining限制在小型、被频繁调用的函数身上。这可使日后的调试过程和二进制升级更容易,也可使潜在的代码膨胀问题最小化,使程序的速度提升机会最大化。”这句话?
这句话指出了在使用内联函数时应该将大多数内联函数限制在小型、被频繁调用的函数中,以便最大化程序的速度提升机会,并最小化潜在的代码膨胀问题,同时也方便了日后的调试过程和二进制升级。 具体来说,这句话的含义可以从以下几个方面进行解释:
- 内联函数的适用范围:内联函数适用于函数体比较小、被频繁调用的函数。将大多数内联函数限制在这样的函数中,可以最大化程序的速度提升机会,因为内联函数可以减少函数调用的开销。
- 调试和升级的问题:将内联函数限制在小型、被频繁调用的函数中,可以使调试过程更加容易,因为这些函数的代码比较简单,容易定位和排查问题。同时,也方便了二进制升级,因为这些函数的代码不会对其他部分的代码产生过多的影响。
- 代码膨胀问题:内联函数会将函数体直接嵌入到调用处,从而增加代码的长度。如果将所有函数都声明为内联函数,可能会导致代码膨胀,从而影响程序的性能。将大多数内联函数限制在小型、被频繁调用的函数中,可以最小化潜在的代码膨胀问题,并提高程序的性能。
综上所述,将大多数内联函数限制在小型、被频繁调用的函数中,可以最大化程序的速度提升机会,最小化潜在的代码膨胀问题,并方便了日后的调试过程和二进制升级。
条款31:将文件间的编译依存关系降至最低
条款31指出了在软件开发中,应该尽可能地将文件间的编译依存关系降至最低,以便实现更好的模块化和可重用性。具体来说,可以从以下几个方面来理解这个条款:
-
编译依存关系:文件间的编译依存关系指的是一个源文件(或头文件)在编译时所依赖的其他源文件(或头文件)。如果一个源文件依赖于另外的源文件,那么在编译时就需要将这些源文件一起编译,从而增加了编译时间和编译的复杂性。
-
最小化编译依存关系的好处:将文件间的编译依存关系降至最低,可以实现更好的模块化和可重用性。
模块化指的是将软件系统分解为多个独立的模块,每个模块都具有清晰的接口和功能;
可重用性指的是可以将一个模块在不同的系统中重复使用。
最小化编译依存关系可以使得每个模块都具有清晰的接口和功能,从而提高了模块化和可重用性。
-
实现最小化编译依存关系的方法:实现最小化编译依存关系的方法有很多,比如使用前置声明(forward declaration)、将接口和实现分离、使用接口类(interface class)等。
前置声明可以减少头文件之间的相互依赖;将接口和实现分离可以使得模块之间的依赖更加清晰;
使用接口类可以将模块之间的依赖限制在接口上,从而提高了模块化和可重用性。
以下是对该条款的理解和一些实践建议:
-
减少头文件的依赖:尽量减少头文件之间的相互包含,只包含必要的头文件。这样可以降低编译依存关系,避免引入不必要的依赖关系。
-
使用前向声明(Forward Declaration):如果在头文件中只需要使用某个类的指针或引用而不需要知道其具体实现细节,可以使用前向声明来代替包含整个头文件。这样可以减少头文件的依赖关系。
-
使用接口类(Interface Class):将公共接口定义在一个独立的接口类中,然后在其他文件中使用该接口类而不是具体的实现类。这样可以降低对具体实现类的依赖,提高代码的可扩展性和可替换性。
-
使用编译器前置声明(Compiler Firewall):在某个实现文件中使用前向声明和指针/引用,然后在该文件的头文件中包含所需的具体实现。这样可以将依赖关系局限在该实现文件内部,减少对其他文件的影响。
-
使用纯虚函数(Pure Virtual Functions):如果某个类在其他文件中只需要通过指针或引用进行操作,可以将该类设计为纯虚函数的基类,然后使用基类的指针或引用来操作具体的子类。这样可以降低对具体子类的依赖,提高代码的灵活性和可扩展性。
总的来说,条款31的目标是通过降低文件间的编译依存关系,减少代码间的耦合性,从而提高代码的可维护性和可扩展性。通过合理的头文件设计、前向声明、接口类和编译器前置声明等技术手段,可以将依赖关系限制在合适的范围内,使代码更加模块化、可重用和易于维护。
相关文章:
03.实现
实现 条款26:尽可能延后变量定义式的出现时间 条款27:尽量少做转型动作 条款28:避免返回handles指向对象内部成分 在C中,handles是指一个指向对象的指针或引用,用于访问该对象的成员函数或成员变量。 而条款28所说…...

可视化大屏时代的到来:智慧城市管理的新思路
随着科技的不断发展,智能芯片作为一种新型的电子元件,被广泛应用于各个领域,其中智慧芯片可视化大屏是一种重要的应用形式。 一、智慧芯片可视化大屏的优势 智慧芯片可视化大屏是一种将智能芯片与大屏幕显示技术相结合的产品,山海…...

Hibernate的三种状态
1.瞬时状态(Transient) 通过new创建对象后,对象并没有立刻持久化,他并未对数据库中的数据有任何的关联,此时java对象的状态为瞬时状态,Session对于瞬时状态的java对象是一无所知的,当对象不再被其他对象引用时…...
React 中 useContext 的用法与性能问题详解
文章目录 一、useContext 是什么?二、useContext 使用场景三、使用步骤1.使用 createContext 创建一个 Context2.使用 Provider 提供值3.使用 useContext 访问 Context完整示例 四、Provider 的 value 类型五、如何在子组件中修改 context 的数据?六、使…...

流程图是什么,用什么软件做?
在工作流程中,经常会遇到需要图形化呈现整个流程的情况。流程图就是一种一目了然的图形化表现方式,便于人们理解、沟通和管理整个流程。 1.Visio Visio是一款微软公司的图表软件,可以用于创建各种类型的流程图、组织结构图、网络图、平面图…...

Linux 家目录和根目录
摘要: 在 Linux 操作系统中,家目录和根目录是两个非常重要的概念。它们是 Linux 文件系统中的两个关键节点,为用户和系统进程提供存储、管理和访问文件和目录的接口。本文旨在深入探讨和理解这两个目录的结构、功能和使用方式,同时…...
js前端跨屏效果
效果: 三个球 源码: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>三个球</title> </h…...

配置华为云镜像加速器
登录华为云官网,点击控制台 在服务列表里面寻找swr服务 点击镜像中心,点击镜像加速器 {"registry-mirrors": [ "https://301dc05233c6419b810bdb22135af9eb.mirror.swr.myhuaweicloud.com" ]}配置镜像加速器 vim /etc/docker…...
Redis的四种模式:单机、主从、哨兵、集群
一、简单理解 单机模式:安装你的redis,启动服务即为单机模式。 主从模式:一个主节点搭配一个或多个从节点,无自动故障转移功能,主节点发生故障后,需要人工将其中一个从节点设置为主节点。 哨兵模式&…...

【开源】基于Vue.js的民宿预定管理系统
项目编号: S 058 ,文末获取源码。 \color{red}{项目编号:S058,文末获取源码。} 项目编号:S058,文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用例设计2.2 功能设计2.2.1 租客角色…...
网络安全深入学习第九课——本机信息收集
文章目录 一、Windows基本信息收集1、查看当前权限2、查看指定用户的详细信息3、查看用户SID4、查看网卡配置5、查看服务器版本\补丁等6、查看系统架构7、查看安装的软件及版本8、查看本机服务信息9、查询进程信息和列表10、查看启动程序信息11、查看计划任务12、查看主机开机时…...
深入理解C语言指针基础概念:定义、内存地址与声明初始化
导言: 在C语言中,指针是一项强大而重要的概念,直接涉及内存的底层操作。理解指针的基础概念对于熟练运用C语言以及理解底层系统工作原理至关重要。本文将深入研究指针的定义、内存地址的概念以及指针的声明和初始化,帮助读者建立对…...
Django DRF版本号的处理
在restful规范中,后端的API中需要体现版本。如果项目比较大,需要些很多的视图类,在每一个类中都写一遍会比较麻烦,所以drf中也支持了全局配置。在每个版本处理的类中还定义了reverse方法,他是用来反向生成URL并携带相关…...

[工业自动化-25]:IDEC和泉RU2S-24D/RU4S-24D继电器的使用说明和接线方式
目录 一、外观 1.1 继电器整体: 1.2 继电器主体: 1.3 底座: 二、RU系列通用继电器介绍 2.1 总体 2.2 性能规格 2.3 锁存杆 2.4 信号定义与连线 - 2S系列 (1)24V输入 (2)第一路输出 …...
如何通过短视频提高转化率?
在当今信息爆炸的时代,如何让自己的品牌在众多的短视频中脱颖而出,吸引更多的潜在客户,是许多企业面临的问题。抖音和快手作为目前最受欢迎的短视频平台,为企业提供了无限的营销机会。在这篇文章中,我们将探讨如何通过…...

微软离Altman越近,离OpenAI就越远!
大数据产业创新服务媒体 ——聚焦数据 改变商业 在OpenAI这场连续剧中(之所以说是连续剧,这个事情肯定没完,后面肯定还会出续集),让我倍感意外的是,Altman刚跟OpenAI分手,“离婚手续”都还没办…...

minio集群部署(k8s内)
一、前言 minio的部署有几种方式,分别是单节点单磁盘,单节点多磁盘,多节点多磁盘三种方式,本次部署使用多节点多磁盘的方式进行部署,minio集群多节点部署最低要求需要4个节点,集群扩容时也是要求扩容的节点…...

【C语言】函数(四):函数递归与迭代,二者有什么区别
目录 前言递归定义递归的两个必要条件接受一个整型值(无符号),按照顺序打印它的每一位使用函数不允许创建临时变量,求字符串“abcd”的长度求n的阶乘求第n个斐波那契数 迭代总结递归与迭代的主要区别用法不同结构不同时间开销不同…...

[原创](免改BIOS)使用Clover升级旧电脑-(高阶玩法)让固态硬盘内置Win11 PE启动系统
[简介] 常用网名: 猪头三 出生日期: 1981.XX.XXQQ: 643439947 个人网站: 80x86汇编小站 https://www.x86asm.org 编程生涯: 2001年~至今[共22年] 职业生涯: 20年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、Delphi…...

React项目中发生空白但不报错的原因分析和解决?
文章目录 前言组件渲染问题状态管理问题异步操作问题代码错误但未抛出异常如果我们使用的是chorme浏览器的话,可以下载一个开发者工具,例如下图:代码审查使用调试工具日志和输出检查外部依赖异步操作终极大法,不到万不得已不可以使…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...

云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...

mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...