Effective C++ 改善程序与设计的55个具体做法笔记与心得 4
四. 设计与声明
18. 让接口容易被正确使用,不易被误用
请记住:
- 好的接口很容易被正确使用,不容易被误用。你应该在你的所有接口中努力达成这些性质
- “促进正确使用”的办法包括接口的一致性,以及与内置类型的行为兼容。
- “阻止误用”的办法包括建立新类型、限制类型上的操作、束缚对象值,以及消除客户的资源管理责任。
- trl::shared_ptr支持定制型删除器。这可防范DLL问题,可被用来自动解除互斥锁等等。
解释:
设计优秀的接口确实要注重以下几个方面:
-
易于正确使用:一个良好设计的接口应该使用户能够更容易地使用它。这需要避免在接口设计中的歧义和不一致,并尽量与已有的、用户熟悉的模式保持一致。例如,对一致性和对内置类型行为的兼容都属于这种情况。
-
不容易被误用:我们也应确保接口能够防止用户误用。创建新的类型(以区分不同的概念或值),限制类型上的操作,约束对象的值,或者管理客户端的资源,都是有效的防止误用手段。
std::shared_ptr
的删除器就是一个很好的例子。这个特性让我们能够自定义对象被删除时的行为。例如,当 std::shared_ptr
管理的资源是一个在动态链接库(DLL)中分配的对象,或者是一个需要在释放之前执行特定操作(例如解锁)的资源时,删除器就非常有用了。
以下是一个 std::shared_ptr
如何使用自定义删除器的例子:
// 假设 lock 是一个互斥锁对象
std::shared_ptr<std::mutex> lock(new std::mutex, [](std::mutex* m){m->unlock(); // 解锁delete m;
});
这里,我们创建了一个 std::shared_ptr
来管理一个 std::mutex
对象,并提供了一个自定义的删除器。当 std::shared_ptr
需要释放它管理的对象时,它就会先调用 unlock
方法,然后再删除对象。这样,我们就不需要关心何时解锁或删除互斥锁对象, std::shared_ptr
会帮我们自动完成。
19. 设计class犹如设计type
请记住:
- 新type的对象应该如何被创建和销毁?
- 对象的初始化和对象的赋值该有什么样的差别?
- 新type的对象如果被passed by value(以值传递),意味着什么?
- 什么是新type的“合法值”?
- 你的新type需要配合某个继承图系吗?
- 你的新type需要什么样的转换?
- 什么样的操作符和函数对此新type而言是合理的?
- 什么样的标准函数应该驳回?
- 该取用新type的成员?
- 什么是新type的“未声明接口”?
- 你的新type有多么一般化?你真的需要一个新type么?
解释:
-
新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。前者通常比较高级,并可避免切割问题。
- 以上规则并不适用于内置类型,以及STL的迭代器和函数对象。对他们而言,pass-by-value往往比较适当。
解释:
-
尽量以pass-by-reference-to-const替换pass-by-value: 对象在传递过程中,pass-by-value需要对对象进行复制操作,产生新的对象。这通常会发生在函数参数传递和返回值中。但复制大型对象可能会非常耗时,也可能引发性能问题。为了避免这些问题,一种常见的解决方法是使用"传递常量引用",即pass-by-reference-to-const,这样就可以避免复制操作。同时使用const可以避免在函数内部修改原对象。
-
以上规则并不适用于内置类型,以及STL的迭代器和函数对象: 对于内置类型(如int、char等),以及STL的迭代器和函数对象,他们通常在内存占用和复制成本上非常小,因此使用pass-by-value通常会更高效。此外,这些类型通常设计为值语义,使用pass-by-value可以更符合其设计原则。
21. 必须返回对象时,别妄想返回其reference
请记住:
绝不要返回pointer或reference指向一个local stack对象,或返回reference指向一个heap-allocated对象,或返回pointer或reference指向一个local static对象而有可能同时需要多个这样的对象。
解释:
-
绝不要返回pointer或reference指向一个local stack对象:这是因为当函数执行完毕后,它的栈内存会被销毁,那些局部变量也就不复存在了。如果你返回一个指向局部变量的指针或引用,那么该指针或引用就会变成悬挂指针或者悬挂引用,这种无效的引用可能会导致程序错误。
-
或返回reference指向一个heap-allocated对象:这主要是因为在返回引用到堆上分配的对象时,对象的生命周期控制可能变得复杂。如果在函数中分配了堆内存,但是没有正确返回该内存的指针,那么调用者可能根本不知道应该释放这个内存,这就导致了内存泄漏。
-
或返回pointer或reference指向一个local static对象而有可能同时需要多个这样的对象:如果你返回一个指向局部静态变量的指针或引用,而在不同的上下文中需要多个这样的对象,那么这些上下文会共享该对象,这可能会导致出现意料之外的副作用。
总的来说,编程时很重要的一点就是管理好对象的生命周期,不正确的内存管理,如上述的几种情况,可能会引发很多问题。因此在编程时,我们要尽量避免这些错误的用法。
22. 将成员变量声明为private
请记住:
- 切记将成员变量声明为private。这可赋予客户访问数据的一致性、可细微划分访问控制、允诺约束条件获得保证,并提供class作者以充分的实现弹性。
- protected并不比public更具封装性。
解释:
在面向对象编程中,封装是非常重要的特性之一。
-
将成员变量声明为private:这样做能保护类的内部状态,防止客户端代码直接修改它。我们只能通过定义的public方法来访问和修改,这样可以确保数据的安全性和一致性。
-
可细微划分访问控制:private限定符可以使类的成员只能被该类的方法访问,这样我们可以更精细地控制谁可以访问和修改类的状态。
-
约束条件获得保证:通过private属性和公开的setter方法,我们可以在修改数据前执行检查,保证数据满足一定的约束条件。
-
提供class作者以充分的实现弹性:因为客户端代码不能直接访问私有成员,所以我们在未来需要修改类的内部实现时会更加灵活,不需要担心会影响到已有的客户端代码。
-
protected并不比public更具封装性:protected成员可以被自身和任何子类访问,相比private,其访问权限更宽松,所以有时可能不如private符合封装性的理念。
所以,将数据成员设置为private并通过public方法进行访问和修改,是实现良好封装的常用手段。
23. 宁以non-member、non-friend替换member函数
请记住:
宁可拿non-member non-friend函数替换member函数。这样做可以增加封装性、包裹弹性和机能扩充性。
解释:
-
增加封装性:在一些情况下,使用non-member non-friend函数(非成员非友元函数)可以增加类的封装性。这是因为non-member non-friend函数无法访问类的私有和受保护成员,所以对类的内部结构知之甚少。这就使得类的实现可以在不破坏这些函数正确性的情况下自由改变。
-
提高包裹弹性:如果我们知道函数不需要访问对象的私有或受保护成员,那么就没有必要将它作为类的成员函数,这就提供了更多的弹性。我们可以在不改变类定义的情况下添加更多的函数,或者将这些函数放入不同的命名空间中。
-
提升机能扩充性:non-member non-friend函数可以对多个对象执行操作,即使这些对象来自不同的类。相比之下,成员函数只能对它所属的对象执行操作。所以使用非成员非友元函数更加灵活,能更好地扩展功能。
24. 若所有参数皆需类型转换,请为此采用non-member函数
请记住:
如果你需要为某个函数的所有参数(包括被this指针所指的那个隐喻参数)进行类型转换,那么这个函数必须是个non-member。
解释:
一个成员函数的隐式this
参数只能用来转换它所属的对象,而不能用来转换传给该函数的其他参数。
但是,非成员函数没有这样的限制,它们可以自由地转换传递给它们的所有参数。因此,如果一个操作需要对所有参数进行类型转换(包括那个由this
指针隐含的参数),那么这个操作通常应该由非成员函数来完成。
请注意,根据C++的运算符重载规则,有两个参数的运算符(例如+或-)应该作为非成员函数来实现,以便能处理左操作数进行的类型转换。然而,有些运算符(例如=或+=)则常常作为成员函数,因为它们通常需要改变它们的左操作数,即this
对象。这是由于它们通常需要直接访问对象的内部状态,而这正是成员函数所提供的。
25. 考虑写一个不抛异常的swap函数
请记住:
- 当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常。
- 如果你提供一个member swap,也该提供一个non-member swap用来调用前者。对于class(而非templates),也请特化std::swap。
- 调用swap时应针对std::swap使用using声明式,然后调用swap并且不带任何“命名空间资格修饰”。
- 为“用户定义类型”进行std templates全特化是好的,但千万不要尝试在std内加入某些对std而言全新的东西。
解释:
-
提供一个swap成员函数:这样可以确保swap操作针对你的类型最为高效。确保这个函数不会抛出异常,这样可以使之在异常敏感的上下文环境中更安全。
-
提供一个non-member swap:非成员swap函数往往更易用,因为它们可以被引入到不需要访问类内部数据的函数或者范畴中。这个非成员函数应该简单地调用上面定义的成员swap函数。
-
特化std::swap:如果你的类型不适用于标准库提供的
std::swap
,你可以为你的类型提供一个std::swap
的全特化版本,这样可以使标准算法和容器能够利用你的高效swap实现。 -
不带任何“命名空间资格修饰”调用swap:这样可以确保在swap操作符重载的上下文中你总是调用了正确的swap版本。
-
不要在std内添加新东西:这是一个关于C++编程习惯的通常建议。尽管为
std::swap
提供全特化版本是可以接受的,但在std
命名空间内添加全新的内容是不被允许的,因为这可能引发未定义的行为。
相关文章:
Effective C++ 改善程序与设计的55个具体做法笔记与心得 4
四. 设计与声明 18. 让接口容易被正确使用,不易被误用 请记住: 好的接口很容易被正确使用,不容易被误用。你应该在你的所有接口中努力达成这些性质“促进正确使用”的办法包括接口的一致性,以及与内置类型的行为兼容。“阻止误…...
WordPress管理员后台登录地址修改教程,WordPress admin登录地址文件修改方法
我们使用WordPress时,管理员后台登录默认地址为“域名/wp-login.php”或“域名/wp-admin”,为了安全,一般会把此地址改掉,防止有人恶意来攻击咱的WordPress,今天出个WordPress后台登录地址修改教程,修改之后…...
Python基础教程(二十四):日期和时间
💝💝💝首先,欢迎各位来到我的博客,很高兴能够在这里和您见面!希望您在这里不仅可以有所收获,同时也能感受到一份轻松欢乐的氛围,祝你生活愉快! 💝Ὁ…...
java面向对象(上)
一.面向对象与面向过程 1.面向过程 面向过程(procedure Oriented Programming),简称POP,主要思想就是将问题分解成一个个步骤去解决,把这个步骤称为函数. 典型语言:C语言 优点:可以大大简化代码 缺点:当代码量过大时,不方便维护 2.面向对象 面向对象(Object Oriented Pr…...
揭示SOCKS5代理服务器列表的重要性
在复杂的网络安全领域中,SOCKS5代理在保护在线活动方面发挥着关键作用。本文深入探讨了SOCKS5代理服务器列表的细节,探讨了它们的应用、优势以及在增强在线安全和隐私方面不可或缺的功能。 一、理解SOCKS5代理服务器列表 作为在客户端和服务器之间进行通…...
机器学习python实践——关于ward聚类分层算法的一些个人心得
最近在利用python跟着参考书进行机器学习相关实践,相关案例用到了ward算法,但是我理论部分用的是周志华老师的《西瓜书》,书上没有写关于ward的相关介绍,所以自己网上查了一堆资料,都很难说清楚ward算法,幸…...
从零制作一个docker的镜像
近期docker的镜像仓库不好用了,很多国内的源也无法使用了,所有今天给大家分享一下怎么从零制作一个CentOS镜像。 准备CentOS7最小环境 mkdir /centos7.9-root# 在该目录准备centos的最小环境 sudo yum --installroot/centos7.9-root --releasever7 ins…...
eclipse 老的s2sh(Struts2+Spring+Hibernate) 项目 用import导入直接导致死机(CPU100%)的解决
1、下载Apache Tomcat - Apache Tomcat 8 Software Downloads 图中是8.5.100的版本,下面的设置用的是另一个版本的,其实是一样。 2、先将Server配好,然后再进行导入操作。 2、选择jdk 当然,这里也可以直接“Download and instal…...
《米小圈动画汉字》汉字教育动画化:传统与创新的完美融合!
汉字,作为中华文化的瑰宝,承载着千百年来中华民族的智慧和思想。每一个汉字不仅仅是一个符号,更是一段历史的见证,一种文化的传承。在当今全球化的背景下,汉字教育面临着新的挑战与机遇。在这种背景下,如何…...
【LeetCode最详尽解答】11-盛最多水的容器 Container-With-Most-Water
欢迎收藏Star我的Machine Learning Blog:https://github.com/purepisces/Wenqing-Machine_Learning_Blog。如果收藏star, 有问题可以随时与我交流, 谢谢大家! 链接: 11-盛最多水的容器 直觉 这个问题可以通过可视化图表来理解和解决。 通过图形化这个…...
redis 缓存jwt令牌设置更新时间 BUG修复
大家好,今天我又又又来了,hhhhh。 上文中 我们永redis缓存了token 但是我们发现了 一个bug ,redis中缓存的token 是单用户才能实现的。 就是 我 redis中存储的键 名 为token 值 是jwt令牌 ,但是如果 用户a 登录 之后 创建一个…...
nginx精准禁止特定国家或者地区IP访问
1、安装依赖 dnf -y install gcc-c libtool gd-devel pcre pcre-devel openssl openssl-devel zlib zlib-devel libmaxminddb-devel pcre-devel zlib-devel gcc gcc-c make git2、获取NGINX安装包并安装 wget https://nginx.org/download/nginx-1.26.1.tar.gz git clone http…...
单片机课设-基于单片机的电子时钟设计(仿真+代码+报告)
基于单片机的电子时钟设计 前言一、课设任务是什么?二、系统总体方案硬件设计2.1 系统硬件总体设计2.2 键盘电路设计2.3 DS1302实时时钟芯片电路设计2.4 复位电路2.5 LCD电路设计 三、软件设计3.1 主程序流程图3.2 主要程序设计代码3.3 修改时间函数3.4 扫描键盘函数 四、仿真…...
.net 6 api 修改URL为小写
我们创建的api项目,url是[Route(“[controller]”)],类似这样子定义的。我们的controller命名是大写字母开头的,显示在url很明显不是很好看(url不区分大小写)。转换方式: var builder WebApplication.Crea…...
Windows电脑部署Jellyfin服务端并进行远程访问配置详细教程
文章目录 前言1. Jellyfin服务网站搭建1.1 Jellyfin下载和安装1.2 Jellyfin网页测试 2.本地网页发布2.1 cpolar的安装和注册2.2 Cpolar云端设置2.3 Cpolar本地设置 3.公网访问测试4. 结语 前言 本文主要分享如何使用Windows电脑本地部署Jellyfin影音服务并结合cpolar内网穿透工…...
rsync同步目录脚本
假设有两台服务器的示例 IP 地址为: Server A: 192.168.1.100Server B: 192.168.1.200 现在来解释如何使用这个脚本进行服务器之间文件夹内容的同步,保留路径和服务器信息的抽象化。 1. 脚本文件位置和权限 假设脚本文件位于 /root/script.sh&#x…...
LeetCode 6. Z 字形变换
LeetCode 6. Z 字形变换 将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。 比如输入字符串为 “PAYPALISHIRING” 行数为 3 时,排列如下: 之后,你的输出需要从左往右逐行读取,产生…...
RTC实时时钟
一、Unix时间戳 1、Unix 时间戳 (1)Unix 时间戳(Unix Timestamp)定义为从UTC/GMT的1970年1月1日0时0分0秒开始所经过的秒数,不考虑闰秒 (2)时间戳存储在一个秒计数器中,秒计数器为…...
WHAT - React 学习系列(一)
官方文档 If you have a lot of HTML to port to JSX, you can use an online converter. You’ll get two things from useState: the current state (count), and the function that lets you update it (setCount). To “remember” things, components use state.To mak…...
代理模式(静态代理/动态代理)
代理模式(Proxy Pattern) 一 定义 为其他对象提供一种代理,以控制对这个对象的访问。 代理对象在客户端和目标对象之间起到了中介作用,起到保护或增强目标对象的作用。 属于结构型设计模式。 代理模式分为静态代理和动态代理。…...
Word2Vec基本实践
系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目…...
IIS配置網站登錄驗證,禁止匿名登陸
需要維護一個以前的舊系統,這個系統在內網運行,需要抓取電腦的登陸賬號,作為權限管理的一部分因此需要在IIS配置一下...
抖音矩阵系统搭建,AI剪辑短视频,一键管理矩阵账号
目录 前言: 一、抖音矩阵系统有哪些功能? 1.AI智能文案 2.多平台账号授权 3.多种剪辑模式 4. 矩阵一键发布,智能发布 5.抖音爆店码功能 6.私信实时互动 7.去水印及外链 二、抖音矩阵系统可以解决哪些问题? 总结ÿ…...
山东大学软件学院创新项目实训开发日志——收尾篇
山东大学软件学院创新项目实训开发日志——收尾篇 项目名称:ModuFusion Visionary:实现跨模态文本与视觉的相关推荐 -------项目目标: 本项目旨在开发一款跨模态交互式应用,用户可以上传图片或视频,并使用文本、点、…...
vue2.7支持组合式API,但是对应的vue-router3并不支持useRoute、useRouter。
最近在做一个项目,因为目标用户浏览器版本并不确定,可能会有较旧版本,于是采用vue2.7而不是vue3,最近一年多使用vue3开发的项目都碰到了很多chrome 63-73版本,而对应UI 库 element plus又问题很多。 为了不碰到这些问…...
摊位纠纷演变肢体冲突,倒赔了500:残疾夫妇与摊主谁之过?
在一个小商贩密集的街区,一起由摊位纠纷引发的肢体冲突事件在当地社区和网络上引起了热议。涉事双方为一名摊主和一对残疾夫妇,他们的争执源自对一个摊位的使用权。本是口头上的争吵,却由于双方情绪激动,迅速升级为肢体冲突&#…...
深入理解和实现Windows进程间通信(消息队列)
常见的进程间通信方法 常见的进程间通信方法有: 管道(Pipe)消息队列共享内存信号量套接字 下面,我们将详细介绍消息队列的原理以及具体实现。 什么是消息队列? Windows操作系统使用消息机制来促进应用程序与操作系…...
Web网页前端教程免费:引领您踏入编程的奇幻世界
Web网页前端教程免费:引领您踏入编程的奇幻世界 在当今数字化时代,Web前端技术已成为互联网发展的重要驱动力。想要踏入这一领域,掌握相关技能,却苦于找不到合适的教程?别担心,本文将为您带来一份免费的We…...
北斗短报文终端在应急消防通信场景中的应用
在应对自然灾害和紧急情况时,北斗三号短报文终端以其全球覆盖、实时通信和精准定位的能力,成为应急消防通信的得力助手。它不仅能够在地面通信中断的极端条件下保障信息传递的畅通,还能提供精准的位置信息,为救援行动提供有力支持…...
Java跳动爱心代码
1.计算爱心曲线上的点的公式 计算爱心曲线上的点的公式通常基于参数方程。以下是两种常见的参数方程表示方法,用于绘制爱心曲线: 1.1基于 (x, y) 坐标的参数方程 x a * (2 * cos(θ) - sin(θ))^3 y a * (2 * sin(θ) - cos(θ))^3 其中ÿ…...
wordpress多国语言版本/亚马逊关键词排名提升
整合谷粒在线教育登录注册前后端 一、在nuxt环境中安装插件 1、安装element-ui 和 vue-qriously (1)执行命令安装npm install element-uinpm install vue-qriously2、修改配置文件 nuxt-swiper-plugin.js,使用插件 nuxt-swiper-plugin.jsimp…...
怎么制造网站/重庆公司seo
void ExitProcess( UINT uExitCode ); ExitProcess是一个API函数,它会结束当前应用程序的执行,并设置它的退出代码。uExitCode是此程序的退出代码。虽然可以在程序的任何地方去调用ExitProcess,强制当前程序的执行立即结束。对于操作系统来说…...
凡科网站能在百度做推广吗/建网站找哪个公司
Day 1(冯哲) 今天的内容很杂但却都是基础知识 主要分为下面几个点 枚举 枚举也称作穷举,指的是从问题所有可能的解的集合中一一枚举各元素。用题目中给定的检验条件判定哪些是无用的,哪些是有用的。能使命题成立的即为其解。 有几…...
做算命网站挣钱吗/seo网站建设公司
2019独角兽企业重金招聘Python工程师标准>>> 在少数需求下,需要能够自动打包,将app发布到不同的平台,那么下面给出本人使用的自动打包脚本: # 以下内容到分割线是,需要针对每个项目进行配置的部分 buildDay$(date %Y%m…...
wordpress外贸商店/长春seo技术
一。周六三月进去。星期天 Calendar calendarCalendar.getInstance();//当前日期Calendar calendar2Calendar.getInstance();int curYear calendar2.get(Calendar.YEAR); // 得到系统年份int curMonth calendar2.get(Calendar.MONTH); // 得到系统月份int curDaycalendar2.ge…...
seo整站优化公司/seo网络优化师招聘
2019独角兽企业重金招聘Python工程师标准>>> 参考资料 1、CentOS/Linux 开放80、8080端口或者开放某个端口 注: 修改/etc/sysconfig/iptables配置,可以参考22端口开放特例 -A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT…...