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

effective c++ 笔记 条款18-25

条款18:让接口容易被正确使用,不易误使用

  1. 使用外覆类型(wrapper)提醒调用者传参错误检查,将参数的附加条件限制在类型本身
Data::Data(int month, int day, int year) { ... }

三个参数类型相同的函数容易造成误用

 Date d1(29, 5, 2014);  //调用顺序错乱,应该是 5, 29, 2014Date d2(2, 30, 2014);  //传入参数有误,2月没有30号

导入新的类型

struct  Month {explicit Month(int mValue):Value(mValue){}int Value;
};struct  Day {explicit Day(int mValue):Value(mValue){}int Value;
};struct  Year {explicit Year(int mValue):Value(mValue){}int Value;
};
Date d2(2, 30, 2014);  //error,类型错误
Date d3(Day(30), Month(2), Year(2014)); //error,类型错误
Date d4(Month(2), Day(30), Year(2014)); //	正确

限制取值

struct Month
{enum E_MON{JAN = 1, FEC, MAR, APR, MAY, JUN, JUL, AGU, SEP, OCT, NOV, DEC};explicit Month(const E_MON month) : m_month(month) {}
private:int m_month;
};
  1. 从语法层面限制调用者不能做的事
    operate*的返回类型上加上const修饰,防止无意的错误赋值if (a * b = c)
  2. 接口应表现出与内置类型的一致性
    如自定义容器的接口在命名上和STL应具备一致性,或者两个对象相乘,最好重载operator*而不是设计名为”multiply”的成员函数。
  3. 从语法层面限制调用者必须做的事
    如条款13在获取资源时返回的是原始指针,如果客户忘记使用智能指针进行管理呢?最好的做法是令获取资源的接口直接返回智能指针。
    对象在动态连接程序库(DLL)被 new 创建,却在另一个 DLL 内被另一个delete销毁。shared_ptr 保证对象会使用原来所在单元的 delete,因此
std::shared_ptr<Investment> createInvestment(){std::shared_ptr<Investment> retVal(static_cast<Invertment*>(0), getRidofInvestment)//因为0不是指针,需要强转//getRidofInvestment函数作为删除器retVal = ...; //令retVal指向正确对象return retVal;
}

如果被pInv管理的原始指针可以在建立pInv之前确定下来,将其传给pInv的构造函数会比,先将pInv初始化为null再赋值为佳,原因条款26

条款19:设计class犹如设计type

  1. 新 type 对象应该如何被创建和销毁? 类中构造函数、析构函数、内存分配和释放函数(operator new,operator new[],operator delete,operator delete[])操作符的重构需求。
  2. 对象的初始化和赋值该有什么样的差别? 构造函数和赋值操作符的区别,重点在资源管理上
  3. 新 type 的对象如果被按值传递,意味着什么? 拷贝构造函数的实现
  4. 什么是新 type 的合法值? 类中的成员函数必须对类中成员变量的值进行检查,如果不合法就要尽快解决或明确地抛出异常。决定了你的 class 必须维护的约束条件,在语法层面、至少在编译前应对用户做出监督
  5. 你的新 type 需要配合某个继承图系吗? 类是否受到基类设计地束缚,是否拥有该覆写的虚函数,是否允许被继承(若不想要被继承,应该声明为final)
  6. 你的新 type 需要什么样的转换? 新类型和已有类型之间的隐式转换问题,类型转换函数和非explicit函数之间的取舍,编写类型转换函数 operator xxx(),或者显式编写转换函数。条款15
  7. 什么样的操作符和函数对此 type 而言是合理的? 影响到你将为你的类声明哪些函数和重载哪些运算符
  8. 什么样的标准函数应该被驳回? 什么样的标准函数应该被驳回?条款6
  9. 谁该取用新 type 的成员? 类中哪些成员设为 public,private 或 protected,以及友元类和友元函数的设置
  10. 什么是新 type 的“未声明接口”? 为未声明接口提供效率、异常安全性以及资源运用上的保证,并在实现代码中加上相应的约束条件。
  11. 你的新 type 有多么一般化? 如果需要定义一类 types,应该定义一个新的 class template。
  12. 你真的需要一个新 type 吗? 如果只是定义新的 derived class 以便为既有的 base class 添加功能,说不定单纯定义一个或多个 non-member 函数或者 templates,更能达到目标

条款20:宁以 pass-by-reference-to-const 替换 pass-by-value

c++函数默认传值
函数接口应该以const引用的形式传参,而不应该是按值传参。
传值涉及大量参数的复制,这些副本大多是没有必要的
按引用传参也可以避免对象切片(Object slicing) 的问题 (对于多态而言,将父类设计成按值传参,如果传入的是子类对象,仅会对子类对象的父类部分进行拷贝,即部分拷贝,而所有属于子类的特性将被丢弃,造成不可预知的错误,同时虚函数也不会被调用)
小的类型并不意味着按值传参的成本就会小。类型大小与编译器的类型和版本有很大关系,某些类型在特定编译器上编译结果会比其他编译器大得多。小的类型也无法保证在日后始终很小。
只有内置类型,以及STL的选代器和函数对象,传值更合适

条款 21:必须返回对象时,别妄想返回其引用

绝不要返回pointer或reference指向一个local stack对象,局部变量在函数结束时就销毁了
或返回reference指向一个heap-allocated对象,额外控制delete可能出错

w = x * y * z; //重载了operator*,如果引用指向的是堆内存,会内存泄漏

或返回 pointer或 reference指向一个local static对象而有可能同时需要多个这样的对象

对于C++11以上的编译器,可以采用给类型编写“转移构造函数”以及使用std::move()函数更加优雅地消除由于拷贝造成的时间和空间的浪费。
条款4已经为“在单线程环境中合理返回reference指向一个local static对象”提供了一份设计实例。(单例模式)

条款22:将成员变量声明为private

请对class内所有成员变量声明为private,private意味着对变量的封装。
从语法一致性看,所有的变量都是private,那么所有的public和protected成员都是函数了,用户在使用的时候也就无需区分
所有类的使用者想利用私有变量实现自己的业务功能时,就必须通过我们留出的接口,这样的接口便充当了一层缓冲,将类型内部的升级和改动尽可能的对客户不可见——不可见就是不会产生影响
protected 并不比public更具封装性
public、protected和private三者反应的是类设计者对类成员封装特性的不同思路——对成员封装还是不封装,如果不封装是对第一类客户不封装还是对第二类客户不封装。

条款23:宁以non-member, non-friend替换member函数

class WebBrowser {          //  一个浏览器类
public:void clearCache();      // 清理缓存,直接接触私有成员void clearHistory();    // 清理历史记录,直接接触私有成员void clearCookies();    // 清理cookies,直接接触私有成员void clearBrowser();           // 在内部调用上边三个函数,不直接接触私有成员,应该放在类外
}

无需直接访问private成员,而只是若干public函数集成而来的member函数。本条款告诉我们:这些函数应该尽可能放到类外。
关于类的封装性:封装的作用是尽可能减小被封装成员的改变对类外代码的影响。某成员封装的好坏:看类内有多少(public或protected)函数直接访问到了这个成员,这样的函数越多,该成员的封装性就越差——该成员的改动对类外代码的影响就可能越大。对于上述clearBrowser函数,设计者本意是不应直接访问任何私有成员,只是公有成员的简单集成(没有必要让其也拥有访问类中private成员的能力),以最大程度维护封装性。但在类的未来维护中,可能忘记设定,在此函数中添加对私有成员的直接访问
成员函数不仅可以访问private成员变量,也可以取用private函数、enums、typedefs等等。而非成员非友元函数能实现更大的封装性,因为它只能访问public函数
关于包括弹性和机能扩展性:提取至类外,通过不同的工具类或者namespace来明确责任,可以从更多维度组织代码结构,并优化编译依赖关系。当我们使用不同功能时就可以include不同的头文件,而不用在面对cache的需求时不可避免的将cookies的工具函数包含进来,降低编译依存性。这也是namespace可以跨文件带来的好处。
命名空间可以跨越多个源码文件而类则不可以。

//在C++中,比较自然的做法是让clearBrowser()函数成为一个non-member函数并且位于WebBrowser类所在的同一个命名空间(namespace)中。
namespace WebBrowserStuff {	class WebBrowser {   //核心机能public :void clearCache();void clearHistory();void clearCookies();};// non-member函数,提供几乎所有客户都需要的核心机能void clearBrowser(WebBrowser& wb) {wb.clearCache();wb.clearHistory();wb.clearCookies();}
}

一个像WebBrowser这样的类中可能有大量的便利函数,如书签便利函数、打印便利函数、cookies管理有关的便利函数.为了防止多个便利函数之间发生编译相互依赖性,分离它们的最直接方法是将书签便利函数声明在一个头文件中,将cookies管理有关的便利函数声明在另一个头文件中,再将打印便利函数声明于第三个头文件中

// 头文件webbrowser.h,这个头文件针对WebBrowser类
namespace WebBrowserStuff{class WebBrowser{// ...};// ...   non-member函数
}
// 头文件webbrowserbookmarks.h
namespace WebBrowserStuff{// ...   与书签相关的便利函数
}
// 头文件webbrowsercookies.h
namespace WebBrowserStuff{// ...   与cookies管理相关的便利函数
}

本条款讨论的是那些不直接接触私有成员的函数,如果你的public(或protected)函数必须直接访问私有成员,那请忘掉这个条款

条款24:若所有参数皆需类型转换,请为此采用non-member函数

这个条款告诉了我们操作符重载被重载为成员函数和非成员函数的区别
如果一个操作符是成员函数,那么它的第一个操作数(即调用对象)不会发生隐式类型转换。
如果你需要为某个函数的所有参数(包括被this指针所指的那个隐喻参数,第一个操作数,即调用对象)进行类型转换,那么这个函数必须是个non-member。

class Rational {
public:Rational(int numerator = 0, int denominator = 1);...
};
Rational oneHalf(1, 2);
result = oneHalf * 2;    // 正确
result = 2 * oneHalf;    // 报错

等价于

result = oneHalf.operator*(2);    // 正确
result = 2.operator*(oneHalf);    // 报错

应放在类外

const Rational operator*(const Rational& lhs,  Rational& rhs){return Rational(lhs.numerator() * rhs.numerator(),lhs.denominator() * rhs.denominator());}

条款25:考虑写出一个不抛异常的swap函数

std::swap函数在 C++11 后改为了用std::move实现,因此几乎已经没有性能的缺陷
原文的思想:

  1. 当std::swap对你的类型效率不高时,提供一个swap成员函数,这个成员函数不抛出异常,只对内置类型进行操作
  2. 如果提供一个member swap,也该提供一个non-member swap来调用前者,对于普通类,也请特化std::swap
  3. 调用swap时,区分是调用自身命名空间的swap还是std的swap,不能乱加std::符号
  4. 为“用户自定义类型”进行std template全特化是好的,但千万不要尝试在std内加入某些对std而言全新的东西。

函数匹配优先级:普通函数 > 特化函数 > 模板函数

相关文章:

effective c++ 笔记 条款18-25

条款18&#xff1a;让接口容易被正确使用&#xff0c;不易误使用 使用外覆类型&#xff08;wrapper&#xff09;提醒调用者传参错误检查&#xff0c;将参数的附加条件限制在类型本身 Data::Data(int month, int day, int year) { ... }三个参数类型相同的函数容易造成误用 Da…...

Nginx学习笔记

Bilibili尚硅谷视频 Nginx 简介 Nginx 概述 Nginx (“engine x”) 是一个高性能的 HTTP 和 反向代理服务器&#xff0c;特点是占有内存少&#xff0c;并发能力强&#xff0c;能经受高负载的考验,有报告表明能支持高达 50,000 个并发连接数 。 正向代理 正向代理&#xff1a;如…...

摆(行列式、杜教筛)

有一个 n n n\times n nn 的矩阵 A A A&#xff0c;满足&#xff1a; A i , j { 1 i j 0 i ̸ j ∧ i ∣ j C otherwise A_{i,j}\begin{cases} 1 &ij\\ 0 &i\notj\land i\mid j\\ C &\text{otherwise} \end{cases} Ai,j​⎩ ⎨ ⎧​10C​ijij∧i∣jotherwi…...

尝试以语法对照表格形式学习新语言:c,rust

以语法对照表格形式学习新语言&#xff0c;以rust为例。 关于rust的个人看法&#xff1a; 能否替代c&#xff1f;部分场景可以&#xff0c;长远看并不会。如果c再扩一些关键字&#xff0c;类似cpp的吸星大法式扩充&#xff0c;rust并不具备优势。解决了c的内存管理问题&#x…...

408计算机网络--基础概论

学习计算机网络走以前需要首先明白一个大的概念&#xff0c;计算机网络通常分为通信子网&#xff08;实现数据通信&#xff09;和资源子网&#xff08;实现资源共享/数据处理&#xff09;七层妖塔 计算机网络&#xff1a;是一个将分散的、具有独立功能的计算机系统&#xff0…...

数据库应用:kylin 部署 达梦数据库DM8

目录 一、实验 1.环境 2.部署前规划 3.部署达梦数据库DM8 4.创建数据库及数据库事例管理 5.达梦数据库的基本操作 二、问题 1.xhost命令报错 2.执行安装程序DMInstall.bin 报错 3.解压安装程序报错 4.安装程序找不到文件 5.图像化界面打不开 6.安装内存太小 7.打开…...

GO框架基础 (二)、sqlx库

在 Go 语言中&#xff0c;sqlx 包是一个用于数据库操作的库&#xff0c;它建立在标准库的 database/sql 包之上&#xff0c;并提供了一些额外的功能&#xff0c;以简化和增强与数据库的交互。sqlx 的目标是通过提供更方便的 API 和一些附加功能来改善在 Go 中进行 SQL 数据库查…...

Expected class selector “.menuChildMall“ to be kebab-case报错原因

![在这里插入图片描述](https://img-blog.csdnimg.cn/dire ct/6b72bda760a2497a90558d48bd0a4de3.png) 使用stylelint格式化css文件时候报上述错误&#xff1a; 原因&#xff1a; css类名未使用-分隔符 将类名修改为&#xff1a; .menu-child-mall形式即可...

NC文件不规则裁剪(利用shp文件裁剪)(三)

文章目录 前言实例数据代码部分需要的库加载文件写入地理信息裁剪NC结果 完整代码奉上 前言 Hello大家好呀&#xff0c;最近正好需要用到多个SHP去裁剪NC&#xff0c;按照我以前的两种办法&#xff08;办法1和办法2&#xff09;操作的话&#xff0c;我自己都会破防&#xff0c…...

java 宠物在线商城系统Myeclipse开发mysql数据库web结构jsp编程servlet计算机网页项目

一、源码特点 java 宠物在线商城系统是一套完善的java web信息管理系统 servletdaobean mvc模式&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S 模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&…...

三防平板丨手持工业平板丨ONERugged工业三防平板丨推动数字化转型

随着科技的发展&#xff0c;数字化转型已经成为企业转型升级的必由之路。而在数字化转型中&#xff0c;三防平板作为一种重要的工具&#xff0c;可以极大地推动企业的数字化转型。本文将从以下几个方面探讨三防平板如何推动数字化转型。 一、提高工作效率 ONERugged加固平板的…...

【Linux | C++ 】基于环形队列的多生产者多消费者模型(Linux系统下C++ 代码模拟实现)

阅读导航 引言一、生产者消费者模型二、环形队列简介三、基于环形队列的生产者消费者模型&#xff08;C 代码模拟实现&#xff09;⭕Makefile文件⭕ . h 头文件✅sem.hpp✅ringQueue.hpp ⭕ . cpp 文件✅testMain.cpp 温馨提示 引言 在上一篇文章中&#xff0c;我们深入探讨了…...

【Docker】Docker存储卷

文章目录 一、什么是存储卷二、为什么需要存储卷三、存储卷分类四、管理卷Volume创建卷方式一&#xff1a;Volume 命令操作方式二&#xff1a;-v 或者--mount 指定方式三&#xff1a;Dockerfile 匿名卷 操作案例Docker 命令创建管理卷Docker -v 创建管理卷Docker mount 创建管理…...

基于python的租车管理平台/汽车租赁网站

功能介绍 平台采用B/S结构&#xff0c;后端采用主流的Python语言进行开发&#xff0c;前端采用主流的Vue.js进行开发。 整个平台包括前台和后台两个部分。 前台功能包括&#xff1a;首页、详情页、用户中心、家政入驻模块。后台功能包括&#xff1a;总览、车辆管理、分类管理…...

【JVM】双亲委派机制

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;JVM ⛺️稳中求进&#xff0c;晒太阳 双亲委派机制 在Java中如何使用代码的方式去主动加载一个类呢&#xff1f; 方式1&#xff1a;使用Class.forName方法&#xff0c;使用当前类的类加载…...

分布式id实战

目录 常用方式 特征 潜在问题 信息安全 高性能 UUID 雪花算法 数据库生成 美团Leaf方案 Leaf-segment 数据库方案 Leaf-snowflake 方案 常用方式 uuid雪花算法数据库主键 特征 全局唯一趋势递增信息安全 潜在问题 信息安全 如果id连续递增, 容易被爬虫, 批量下…...

深入了解 SOCKS5 代理、代理 IP 和 HTTP

在网络通信和数据传输中&#xff0c;代理服务器扮演着至关重要的角色。本文将深入探讨 SOCKS5 代理、代理 IP 和 HTTP&#xff0c;揭示它们的工作原理、应用场景以及优缺点。 1. SOCKS5 代理 SOCKS&#xff08;Socket Secure&#xff09;是一种网络协议&#xff0c;允许客户端…...

外包干了3个多月,技术退步明显。。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;19年通过校招进入广州某软件公司&#xff0c;干了接近3年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…...

Unity之闪电侠大战蓝毒兽(简陋的战斗系统)

目录 &#x1f3a8;一、创建地形 &#x1f3ae;二、创建角色 &#x1f3c3;2.1 动画 &#x1f3c3;2.2 拖尾 &#x1f3c3;2.3 角色控制 ​&#x1f3c3;2.4 技能释放 &#x1f3c3;2.5 准星 &#x1f4f1;三、创建敌人 &#x1f432;3.1 选择模型 &#x1f432;3.…...

C# 菜鸟级别有关于redis的使用

public IActionResult Index() { ConnectionMultiplexer _conn ConnectionMultiplexer.Connect("127.0.0.1:6379");//初始化 var database _conn.GetDatabase(7);//指定连接的库 0 RedisHelper redisHelper new Redi…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案&#xff1a;Java 字节码技术实战分享&#xff08;仅供学习&#xff09; 一、Aspose.PDF 简介二、说明&#xff08;⚠️仅供学习与研究使用&#xff09;三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...

SQL慢可能是触发了ring buffer

简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

GitHub 趋势日报 (2025年06月06日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...

如何应对敏捷转型中的团队阻力

应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中&#xff0c;明确沟通敏捷转型目的尤为关键&#xff0c;团队成员只有清晰理解转型背后的原因和利益&#xff0c;才能降低对变化的…...