当前位置: 首页 > 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…...

AlexNet的出现推动深度学习的巨大发展

尽管AlexNet&#xff08;2012&#xff09;的代码只比LeNet&#xff08;1998&#xff09;多出几行&#xff0c;但学术界花了很多年才接受深度学习这一概念&#xff0c;并应用其出色的实验结果。 AlexNet&#xff08;由Alex Krizhevsky、Ilya Sutskever和Geoffrey Hinton共同设计…...

2024面试offer收割宝典字节篇

1.IO 模型有哪些,讲讲你理解的 nio ,他和 bio,aio 的区别是啥, 谈谈 reactor 模型。 IO 模型主要包括以下几种:1. 阻塞 I/O (BIO): 当一个线程调用 read() 或 write() 系统调用时,如果数据没有准备好或者缓冲区已满,则该线程会被操作系统阻塞,直到有数据可读或写入完…...

冒泡排序及其优化

冒泡排序 int[] arr {1,3,2,9,4,7,2,8};//比较多少轮(n个数字比较n-1次)for(int i0,n arr.length;i<n-1;i) {//每轮比较多少次(n-1-i次)for(int j 0;j<n-1-i;j) {//两两比较if(arr[j] > arr[j1]) { //比较结果为升序排列&#xff0c;如果想要降序排列结果将 >…...

【医学大模型 补全主诉】BioGPT + LSTM 自动补全医院紧急部门主诉

BioGPT LSTM 自动补全医院紧急部门主诉 问题&#xff1a;针对在紧急部门中自动补全主诉的问题子问题1: 提高主诉记录的准确性子问题2: 加快主诉记录的速度子问题3: 统一医疗术语的使用子问题4: 减少打字错误和误解子问题5: 提高非特定主诉的处理能力 解法数据预处理神经网络方…...

HCIE-Datacom证书有效期多久?HCIE考试有哪些内容?

如今越来越多的人开始关注并参与到华为认证的学习中来。 其中&#xff0c;华为认证数据通信专家(HCIE-Datacom)作为华为认证体系中的高级认证&#xff0c;备受瞩目。 那么&#xff0c;关于HCIE-Datacom证书的有效期以及HCIE考试的内容&#xff0c;你知道多少呢&#xff1f;下…...

OpenCV中的边缘检测技术及实现

边缘检测是在电脑如何理解图片这一问题中的一环&#xff0c;它帮助电脑找出照片里的轮廓和分界线。想象一下你在看一幅黑白漫画&#xff0c;轮廓线定义了每一个角色和物体&#xff0c;而电脑要做的&#xff0c;就是通过边缘检测来找出这些线条。这在很多像是图像分析这样的领域…...

机器学习基础(一)理解机器学习的本质

导读&#xff1a;在本文中&#xff0c;将深入探索机器学习的根本原理&#xff0c;包括基本概念、分类及如何通过构建预测模型来应用这些理论。 目录 机器学习 机器学习概念 相关概念 机器学习根本&#xff1a;模型 数据的语言&#xff1a;特征与标签 训练与测试&#xf…...

Eclipse - Makefile generation

Eclipse - Makefile generation References right mouse click on the project -> Properties -> C/C Build -> Generate Makefiles automatically 默认会在 Debug 目录下创建 Makefile 文件。 References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/...

Sora:新一代实时音视频通信框架

一、Sora简介 Sora是一个开源的实时音视频通信框架&#xff0c;旨在提供高效、稳定、可扩展的音视频通信解决方案。它基于WebRTC技术&#xff0c;支持跨平台、跨浏览器的实时音视频通信&#xff0c;并且具备低延迟、高并发、易集成等特点。 --点击进入Sora(一定要科学哦&#x…...

龟兔赛跑算法

一、题目 给定一个长度为 n1 的数组nums&#xff0c;数组中所有的数均在 1∼n1 的范围内&#xff0c;其中 n≥1。 请找出数组中任意一个重复的数。 样例 给定 nums [2, 3, 5, 4, 3, 2, 6, 7]。返回 2 或 3。 二、解析 解决这个问题的一种有效方法是使用快慢指针&#xf…...