《Effective C++》第三版——设计与声明(1)
参考资料:
- 《Effective C++》第三版
注意:《Effective C++》不涉及任何 C++11 的内容,因此其中的部分准则可能在 C++11 出现后有更好的实现方式。
条款 18:让接口容易被正确使用,不易被误用
好的接口很容易被正确使用,不容易被误用。你应该在你的接口里努力达成这一性质
理想状态下,应该在编译期发现客户对接口的误用。
“促进正确使用”的办法包括接口的一致性、以及与内置类型的行为兼容
如果没有特殊理由,尽量令你的 types 和内置类型保持一致。
“阻止误用的办法”的办法包括建立新类型、限制类型上的操作,束缚对象值,以及消除客户的资源管理责任
假设我们有设计一个用来表示日期的类:
class Date {
public:Date(int month, int day, int year);
private:int month, day, year;
};
这样的接口是很容易被误用的:
Date(30, 3, 2024); // 错误的参数传递顺序
Date(2, 30, 2024); // 错误数据范围
一种常见的预防方法是导入新类型:
class Month {
public:explicit Month(int val);
private:int val;
};class Date {
public:Date(const Month &m, const Day &d, const Year &y);...
};Date(Day(30), Month(3), Year(2024)); // 编译错误
设计了类型,就可以限制每个类型的合法值,比较安全的做法是预先用函数定义有效值:
class Month {
public:static Month Jan() { return Month(1) }...
private:explicit Month(int val); // 私有构造函数,避免用户调用
};Date(Month::Jan(), day(30), Year(2024));
如果我们的接口要求客户必须“记得某些事情”,就是有着“不正确”使用的倾向,例如下面的工厂函数, 返回一个指针指向动态分配对象:
A* createA(...);
为了避免资源泄露,客户可以将 createA
返回的指针保存在智能指针中,但客户可能会忘记这一点,所以我们最好令工厂函数直接返回智能指针:
shared_ptr<A*> createA(...);
shared_ptr
支持定制删除器, 可以防范 DLL 问题
shared_ptr
有一个特别好的性质:它会自动使用它所管理指针的专属删除器(自定义的删除器或默认的 delete
),这可以解决“cross-DLL problem”。“cross-DLL problem”指:对象在一个动态链接库(DLL)被 new
创建,在另一个 DLL 被 delete
销毁。
条款 19:设计 class 犹如设计 type
Class 的设计就是 type 的设计,在定义一个新 type 之前,请确定你仔细思考过本条款覆盖的所有讨论主题
每次设计 class 时,需要考虑如下问题:
- 新 type 的对象应该被如何创建和销毁?
- 对象的初始化和对象的赋值该有什么样的差别?
- 新 type 的对象如果被 passed by value,意味着什么?
- 什么是新 type 的“合法值”?
- 你的新 type 需要配合某个继承体系吗?
- 你的新 type 需要什么样的转换?
- 什么样的操作符和函数对此新 type 而言是合理的?
- 什么样的标准函数应该被驳回?
- 谁该取用新 type 的成员?
- 什么是新 type 的未声明接口?
- 你的新 type 有多么一般化?
- 你真的需要一个新 type 吗?
条款 20:宁以 pass-by-reference-to-const 替换 pass-by-value
尽量以 pass-by-reference-to-const 替换 pass-by-value。前者通常比较高效,并可避免切割问题
考虑下面的例子:
class A{
public:...
private:string a, b ,c;
}void func(A a);
执行 func
在参数构造时需要调用 A
的 copy 构造函数,进而需要调用 3 个 string
的 copy 构造函数,执行结束后,这些对象还要析构。更高效的方法是,使用 pass-by-conference-to-const,避免了对象的构造和析构,同时 const
也保证 func
不会对传入的对象进行修改。
pass-by-reference-to-const 还可以避免切割问题:
void func(base b){b.f(); // 调用base::f()
}void func(cosnt base &b){b.f(); // 根据实际传入的类型执行不同版本的f()
}
以上规则并不适用于内置类型、STL 的迭代器和函数对象。对它们而言,pass-by-value 比较合适
引用的底层实现往往是指针,所以对于内置类型,pass-by-value 往往比 pass-by-reference-to-const 更高效。此外,STL 的迭代器和函数对象习惯上实现为 pass-by-value,STL 的实现者保证了高效性和避免出现切割问题。
需要注意的是,不能因为内置类型适合 pass-by-value,就认为和内置类型一样小的对象适合 pass-by-value,原因是:
- 对象小不一定意味着构造函数不昂贵。
- 编译器对待内置类型和自定义类型的方式截然不同,例如某些编译器会把
double
对象放入缓存中,却拒绝把只含有一个double
成员的自定义对象加入缓存。 - 自定义对象的大小容易有所变化。
条款 21:必须返回对象时,别妄想返回其 reference
绝不要返回 pointer 或 reference 指向一个 local stack,或返回 reference 指向一个 heap-allocated 对象,或返回 pointer 或 reference 指向一个 local static 对象而有可能同时需要多个这样的对象
考虑我们有一个表示有理数的类,重载了 *
:
class Rational {
public:Rational(int n, int d);// 试图by reference返回值friend const Rational &operator*(const Rational &lhs, const Rational &rhs);
private:int n, d;
};
返回指向 local stack 的 pointer 或 reference,由于 local stack 对象在函数返回的时候就已经被销毁了,所以任何使用返回值的行为都将导致未定义行为。
返回指向 heap-allocated 的 pointer 或 reference,调用者很容易忘记 delete
,很容易造成内存泄露。
如果考虑定义一个静态 Rational
对象专门保存结果,不仅会在多线程产生不安全的问题,有时还会造成逻辑错误:
bool operator==(const Rational &lhs, const Rational &rhs);
Rational a, b, c, d;
if ((a * b) == (c * d)) {...
}
由于 a*b
和 c*d
返回的是同一个静态变量,所以条件永远成立。
条款 22:将成员变量声明为 private
切记将成员变量声明为 private
,这可赋予客户访问数据的一致性,可细微划分访问控制、允诺约束条件获得保证,并提供 class 作者以充分实现弹性
所有成员变量都不该是 public
:
- 从语法一致性的角度来看,所有变量不是
public
,意味着所有接口都是函数,此时用户就不需要纠结是否应该使用小括号。 - 使用函数可以让你对成员变量有更精确的控制:通过函数可以实现“不可访问”、“只读访问”、“只写访问”、“读写访问”等。
- 从封装性的角度来看,将成员变量隐藏在函数接口的背后,可以使实现更加灵活,如:在成员变量被读写时进行记录、验证成员变量是否满足约束条件等。此外,不封装通常也意味着不可改变,将成员变量隐藏,就保留了优化的空间。
protected
并不比 public
更具封装性
使用 protected
虽然相比 public
可以实现语法一致和精确控制,但其封装性却并不比 public
强。
成员变量的封装性,与改变(例如:从 class
中移除)这个成员变量所破坏的代码量成反比。对于 public
变量,一旦其被移除,所有使用它的用户代码都会被破坏;对于 protected
变量,一旦被移除,所有使用它的 derived class 代码都会被破坏,二者都会造成不可预知的大量代码受到破坏。
所以,从封装的角度来看,只有 private
(封装)和其他(不封装)。
相关文章:
《Effective C++》第三版——设计与声明(1)
参考资料: 《Effective C》第三版 注意:《Effective C》不涉及任何 C11 的内容,因此其中的部分准则可能在 C11 出现后有更好的实现方式。 条款 18:让接口容易被正确使用,不易被误用 好的接口很容易被正确使用&…...
数值计算的程序设计问题举例
### 数值计算的程序设计问题 #### 1. 结构静力分析计算 **涉及领域**:工程力学、建筑工程 **主要问题**:线性代数方程组(Linear Algebraic Equations) **解释说明**: 在结构静力分析中,我们需要解决复杂的…...
Java之方法的使用
修饰符 返回值 方法名称(形式参数){ } 当无参数的时候形式参数中什么都不写。 列如求两个数相加 修饰符可有可无。 方法重载: 1.方法名相同 2.参数列表不同 3。返回值不影响重载...
sudo 命令:掌握系统权限控制,实现安全高效管理
一、命令简介 sudo 命令允许系统管理员授权普通用户执行特定命令,并以管理员身份运行这些命令,通常需要输入用户自己的密码。 sudo 全称是"substitute user do",意为“替用户做”,也就是“以另一个用户的身…...
AndroidStudio导入so文件
点击app 右键依次选择New-Floder-JNI Floder 创建jni目录 将需要的so文件拷贝到jni目录 在app目录下,build.gradle文件的android{}中添加: sourceSets {main{jniLibs.srcDirs [src/main/jni]}}点击一下Sync Project with Gradle Files 然后编译生成AP…...
Kuebernetes 群集基于 Docker 部署
Kuebernetes 群集基于 Docker 部署 实验报告资源列表基础环境一、准备 Docker1、安装 Docker 二、安装 Kubeadm 工具1、配置 yum 源2、安装 Kubeadm 工具 三、初始化 Master 节点1、配置 Master 节点2、常见故障 四、Node 节点加入集群五、部署网络插件(CNI…...
追随 HarmonyOS NEXT,Solon v3.0 将在10月8日发布
Solon (开放原子开源基金会,孵化项目)原计划10月1日发布 v3.0 正式版。看到 HarmonyOS NEXT 将在 10月8日启用公测,现改为10月8日发布以示庆贺。另外,Solon 将在2025年启动“仓颉”版开发(届时,…...
服装时尚与动漫游戏的跨界联动:创新运营与策划策略研究
摘要:本论文聚焦于服装时尚与动漫游戏的跨界联动现象,深入探讨其在运营和策划方向的策略与实践。通过对相关理论的梳理和实际案例的分析,阐述了跨界联动的背景、意义、模式以及面临的挑战。研究发现,成功的跨界联动能够实现品牌价…...
Redis中String类型的常用命令(append,getrenge,setrange等命令)
Redis----String命令 前言.常见的String存储类型. 常见命令1. set 命令2. get 命令3. mget命令与mset命令4. setnx命令5. setex与psetex命令6. incr与incrby与incrbyfloat命令7. decr与decrby命令8. append命令9. getrange和setrange命令10. strlen命令. 前言. 常见的String存…...
深度拆解:如何在Facebook上做跨境电商?
国内社交媒体正在逐渐兴盛,海外也不例外。在数字营销的新时代,Facebook已成为跨境电商不可或缺的平台之一。通过Facebook的巨大流量,卖家可以更好的触及潜在消费者,以实现销售增长。本文就深度拆解一下,卖家如何利用Fb…...
为啥数据需转换成tensor才能参与后续建模训练
将数据转换为Tensor(张量)格式用于深度学习和机器学习模型训练,主要是出于以下几个关键原因: 数值计算的效率:Tensor(由PyTorch、TensorFlow等库提供)是在GPU上执行高效的数值运算的数据结构。相…...
leetcode:380. O(1) 时间插入、删除和获取随机元素
实现RandomizedSet 类: RandomizedSet() 初始化 RandomizedSet 对象bool insert(int val) 当元素 val 不存在时,向集合中插入该项,并返回 true ;否则,返回 false 。bool remove(int val) 当元素 val 存在时࿰…...
Linux集群部署RabbitMQ
目录 一、准备三台虚拟机,配置相同 1、所有主机都需要hosts文件解析 2、所有主机安装erLang和rabbitmq 3、修改配置文件 4、导入rabbitmq 的管理界面 5、查看节点状态 6、设置erlang运行节点 7、rabitmq2和rabbitmq3重启服务 8、查看各个节点状态 二、添加…...
01DSP学习-了解DSP外设-以逆变器控制为例
(由于是回忆自己简单的DSP学习过程,所以博客看起来有些没有章法,请见谅~) 上一篇博客介绍了学习DSP需要的软件和硬件准备,以及一个DSP的工程包含了哪些东西。我的学习方法是目的导向,即我需要用什么我就学什么,并没有…...
【ArcGIS Pro实操第三期】多模式道路网构建(Multi-model road network construction)原理及实操案例
ArcGIS Pro实操第三期:多模式道路网构建原理及实操案例 1 概述1.1 原理 2 GIS实操2.1 新建文件并导入数据2.2 创建网络数据集2.3 设置连接策略(Setting up connectivity policies)2.4 添加成本(Adding cost attributes)…...
深度学习基础及技巧
机器学习中的监督学习 监督学习是通过对数据进行分析,找到数据的表达模型,对新输入的数据套用该模型做决策 主要分为训练和预测两个阶段 训练阶段:根据原始数据进行特征提取,然后使用决策树、随机森林等模型算法分析数据之间的特…...
Unity 外描边简单实现(Shader Graph)
1:原理 将物体的模型空间的位置(也就是顶点数据)放大,作为一个单独的渲染通道单独渲染,这时候模型是已经发大过的,要想看到外描边的效果,需要将正面显示的东西给去掉,显示背面渲染的…...
text2sql方法:NatSQL和DIN-SQL
NatSQL NatSQL出自2021年9月的论文《Natural SQL: Making SQL Easier to Infer from Natural Language Specifications》(github),它是一种SQL 中间表征(SQL intermediate representation(IR))方法。 NatSQL作者认为Text2SQL的关键挑战是自然语言描述和其对应的SQ…...
【新闻转载】Storm-0501:勒索软件攻击扩展到混合云环境
icrosoft发出警告,勒索软件团伙Storm-0501近期调整了攻击策略,目前正将目标瞄准混合云环境,旨在全面破坏受害者的资产。 该威胁行为者自2021年首次露面,起初作为Sabbath勒索软件行动的分支。随后,他们开始分发来自Hive…...
RabbitMQ 队列之战:Classic 和 Quorum 的性能洞察
RabbitMQ 是一个功能强大且广泛使用的消息代理,它通过处理消息的传输、存储和交付来促进分布式应用程序之间的通信。作为消息代理,RabbitMQ 充当生产者(发送消息的应用程序)和使用者(接收消息的应用程序)之…...
Spring Boot 集成 MySQL 的详细指南
在现代软件开发中,Spring Boot 因其简单易用而成为构建 Java 应用程序的热门选择。结合 MySQL这一常用关系型数据库,开发者可以快速构建出功能完善的后端服务。本文将详细介绍如何将 Spring Boot 与 MySQL 集成,提供从环境搭建到代码实现的全…...
python格式化输入输出
以下是使用 format()、f-string 和百分号 % 运算符进行 Python 数据格式化输入输出的示例代码。 1. 使用 format() 方法进行格式化 # 使用 format() 方法格式化数据并输出到文件 name "Alice" age 25 score 92.5# 格式化字符串 formatted_string "Name: {…...
音视频入门基础:FLV专题(10)——Script Tag实例分析
一、引言 在《音视频入门基础:FLV专题(9)——Script Tag简介》中对FLV文件的Script Tag进行了简介。下面用一个具体的例子来对Script Tag进行分析。 二、Script Tag的Tag header实例分析 用notepad打开《音视频入门基础:FLV专题…...
国外问卷调查匠哥已经不带人了,但是还可以交流
国外问卷调查匠哥已经不带人了,但是还可以来和匠哥交流, 为啥不带人了呢? 从今年年初开始,匠哥在带学员的过程中发现: 跟往年同样的收费,同样的教学,甚至我付出的时间精力比以前还多ÿ…...
Linux 进程的基本概念及描述
目录 0.前言 1. 什么是进程 1.1 进程的定义与特性 1.2 进程与线程的区别 2.描述进程 2.1 PCB (进程控制块) 2.2 task_struct 3.查看进程 3.1 查看进程信息 3.1.1 /proc 文件系统 3.1.2 ps 命令 3.1.2 top 和 htop 命令 3.2 获取进程标识符 3.2.1使用命令获取PID 3.2.2 使用C语言…...
【C++】透过STL源代码深度剖析vector的底层
✨ Blog’s 主页: 白乐天_ξ( ✿>◡❛) 🌈 个人Motto:他强任他强,清风拂山冈! 🔥 所属专栏:C深入学习笔记 💫 欢迎来到我的学习笔记! 参考博客:【C】透过STL源…...
ubuntu 开启root
sudo passwd root#输入以下命令来给root账户设置密码 sudo passwd -u root#启用root账户 su - root#要登录root账户 root 开启远程访问: 小心不要改到这里了:sudo nano /etc/ssh/ssh_config 而是:/etc/ssh/sshd_config sudo nano /etc/ssh…...
使用 Llama 3.1 和 Qdrant 构建多语言医疗保健聊天机器人的步骤
长话短说: 准备好深入研究: 矢量存储的复杂性以及如何利用 Qdrant 进行高效数据摄取。掌握 Qdrant 中的集合管理以获得最佳性能。释放上下文感知响应的相似性搜索的潜力。精心设计复杂的 LangChain 工作流程以增强聊天机器人的功能。将革命性的 Llama …...
【Linux-基础IO】如何理解Linux下一切皆文件磁盘的介绍
目录 如何理解Linux系统上一切皆文件 1.物理角度认识磁盘 2.对磁盘的存储进行逻辑抽象 磁盘寻址 3.磁盘中的寄存器 如何理解Linux系统上一切皆文件 计算机中包含大量外设,操作系统想要管理好这些外设,就必须对这些外设进行先描述再组织,…...
Golang | Leetcode Golang题解之第436题寻找右区间
题目: 题解: func findRightInterval(intervals [][]int) []int {n : len(intervals)type pair struct{ x, i int }starts : make([]pair, n)ends : make([]pair, n)for i, p : range intervals {starts[i] pair{p[0], i}ends[i] pair{p[1], i}}sort.…...
免费x网站域名/软文范文大全1000字
背景 在垃圾短信过滤应用 SMSFilters 中,需要使用 Jieba 分词库来対短信进行分词,然后使用 TF-IDF 来进行处理 分词库是 C 写的,这就意味着需要在Swift中集成 C 库。在官方文档 "Using Swift with Cocoa and Objective-C" 中&#…...
wordpress图片主题演示/网页模板免费html
本人性别男,年龄47岁,一位建筑工程师,性格开朗,喜欢学习,2013年在网上搜索记忆关键词,从此开始了学习超级记忆和思维导图之路!也因此,接触了魔方!初学魔方,我…...
目前做美术的网站以及app/网络营销这个专业怎么样
(下面内容均是来源于网友经验,我只是大自然的搬运工,多谢广大网友) 原因应该是 :上次关机时未正常关机; 表现症状:QQ初始化失败,很多网页打不开,一些应用打不开ÿ…...
那个网站可以做网站测速对比/百度推广后台登录页面
文章目录1.下载安装包2.上传安装包3.修改配置文件3.1修改zeppelin-site.xml文件3.2修改zeppelin-env.sh文件4.启动zeppelin5.配置hive解释器5.1环境和变量配置5.2 在web界面配置集成hive6.使用Zepplin的hive解释器1.下载安装包 官网直达 选择zeppelin-0.8.1-bin-all.tgz 2.上…...
网站建设合同中英文/自己有货源怎么找客户
在CSS中background: -moz-linear-gradient 让网站背景渐变的属性,目前火狐3.6以上版本和google浏览器支持这个属性。 background: -moz-linear-gradient(top, #bccfe3 0%, #d2dded 100%); 适合 FF3.6 background: -webkit- gradient(linear, left top, left bott…...
百度网站免费电话/网站排名软件推荐
Fun 0 直接获取日期再次计算时间戳 function fun00(){let today new Date();return new Date(today.toDateString()).getTime();} 复制代码function fun01(){let today new Date();return new Date(today.getFullYear()/today.getMonth()1/today.getDate()).getTime();} 复制…...