effective c++ 笔记 条款18-25

news/发布时间2024/5/14 14:52:05

条款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而言全新的东西。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.bcls.cn/Vgpt/824.shtml

如若内容造成侵权/违法违规/事实不符,请联系编程老四网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

【Android】使用Android Studio运行Hello World项目

文章目录 1. JDK的安装与配置2. Android Studio的安装3. 运行Hello World项目3.1 新建项目3.2 修改项目配置3.2.1 修改UI界面3.2.2 配置 Android SDK 3.3 添加并运行虚拟设备3.4 运行项目 1. JDK的安装与配置 想要使用Android Studio&#xff0c;必须先配置Java环境&#xff0…

关于内存相关的梳理

1 关键字 总结 &#xff08;lowmemory&#xff0c;anr in&#xff09; 2 知识储备 虚拟机原理 垃圾回收算法 又包含标记 和清除两种算法 标记&#xff1a;程序计数器-已过时&#xff0c;可达性分析 具体可见 http://help.eclipse.org/luna/index.jsp?topic%2Forg.ec…

【AI视野·今日NLP 自然语言处理论文速览 第七十八期】Wed, 17 Jan 2024

AI视野今日CS.NLP 自然语言处理论文速览 Wed, 17 Jan 2024 (showing first 100 of 163 entries) Totally 100 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Deductive Closure Training of Language Models for Coherence, Accur…

服务器硬件、部署LNMP动态网站、部署wordpress、配置web与数据库服务分离、配置额外的web服务器

目录 day01 项目实战目标 单机安装基于LNMP结构的WordPress网站 基本环境准备 配置nginx 配置数据库服务 部署wordpress web与数据库服务分离 准备数据库服务器 迁移数据库 配置额外的web服务器 day01 项目实战目标 主机名IP地址client01192.168.88.10/24web1192.1…

GPT-4带来的思想火花

GPT-4能够以其强大的生成能力和广泛的知识储备激发出众多思想火花。它能够在不同的情境下生成新颖的观点、独特的见解和富有创意的解决方案&#xff0c;这不仅有助于用户突破思维定势&#xff0c;还能促进知识与信息在不同领域的交叉融合。 1.GPT-4出色的创新思考和知识整合能…

网络协议与攻击模拟_16HTTP协议

1、HTTP协议结构 2、在Windows server去搭建web扫描器 3、分析HTTP协议流量 一、HTTP协议 1、概念 HTTP&#xff08;超文本传输协议&#xff09;用于在万维网服务器上传输超文本&#xff08;HTML&#xff09;到本地浏览器的传输协议 基于TCP/IP(HTML文件、图片、查询结构等&…

多线程案例

&#x1f3a5; 个人主页&#xff1a;Dikz12&#x1f4d5;格言&#xff1a;那些在暗处执拗生长的花&#xff0c;终有一日会馥郁传香欢迎大家&#x1f44d;点赞✍评论⭐收藏 目录 单例模式 饿汉模式 懒汉模式 阻塞队列 生产者-消费者模型意义 定时器 单例模式 单例模式就…

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

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

飞书上传图片

飞书上传图片 1. 概述1.1 访问凭证2. 上传图片获取image_key1. 概述 飞书开发文档上传图片: https://open.feishu.cn/document/server-docs/im-v1/image/create 上传图片接口,支持上传 JPEG、PNG、WEBP、GIF、TIFF、BMP、ICO格式图片。 在请求头上需要获取token(访问凭证) …

代码随想录刷题笔记-Day17

1. 路径总和 112. 路径总和https://leetcode.cn/problems/path-sum/ 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径&#xff0c;这条路径上所有节点值相加等于目标和 targetSum 。如果存在&#xff0c;返回 true …

C语言希尔排序详解!!!速过

目录 希尔排序是什么&#xff1f; 关于时间复杂度 希尔排序的源代码 希尔排序源代码的详解 希尔排序是什么&#xff1f; 之前我们说了三个排序&#xff08;插入排序&#xff0c;选择排序&#xff0c;冒泡排序&#xff09;有需要的铁铁可以去看看之前的讲解。 但因为之前的…

算法沉淀——分治算法(leetcode真题剖析)

算法沉淀——分治算法 快排思想01.颜色分类02.排序数组03.数组中的第K个最大元素04.库存管理 III 归并思想01.排序数组02.交易逆序对的总数03.计算右侧小于当前元素的个数04.翻转对 分治算法是一种解决问题的算法范式&#xff0c;其核心思想是将一个大问题分解成若干个小问题&a…

docker安装一系列镜像

启动docker systemctl start docker docker 启动已经停止的容器 docker start idOrName PS&#xff1a;idOrName为容器的id或者名称 1、安装mysql镜像 拉取mysql5.7的镜像 docker pull mysql:5.7 查看镜像 docker images 启动mysql #启动mysql docker run --name mysql…

Golang中的fmt包:格式化输入输出的利器

Golang中的fmt包&#xff1a;格式化输入输出的利器 在软件开发的世界里&#xff0c;fmt包就像是一位忠实的伙伴&#xff0c;始终陪伴着开发人员。它简化了格式化输入输出的过程&#xff0c;让打印和扫描数据变得轻松自如。无论是向控制台输出简单的消息&#xff0c;还是处理复杂…

城市智慧驿站是什么?城市智慧驿站有哪些功能

城市智慧驿站作为一种创新性的社会配套设施&#xff0c;开始在多个城市落地使用&#xff0c;引起了社会的关注。 城市智慧驿站是什么&#xff1f;城市智慧驿站是在智慧城市的背景下&#xff0c;城市智慧驿站智慧公厕成为了一种创新性的社会配套建筑。作为景观式模块化建筑&…

BOSS直聘招聘经验

招聘低端兼职岗位。流量很大&#xff0c;来的人通常实力也不足。 招聘高端兼职岗位。流量不多。来的人通常具备一定实力。 招聘高薪职位&#xff0c;流量一般&#xff0c;会有有实力的勾搭。 招聘低薪职位&#xff0c;流量一般。通常没什么实力。

Android widget基础指南

widget的概念最早是由一名叫Rose的苹果工程师提出&#xff0c;后来经过多方面机缘巧合的发展下&#xff0c;便有了今天Android平台上的小组件widget&#xff0c;一般APP开发可能应用场景较少&#xff0c;最常见的莫过于天气APP的widget。但对于从事IOT或车载方向的同学&#xf…

网络安全最典型基础靶场-DVWA-本地搭建与初始化

写在前面&#xff1a; 之前也打过这个 DVWA 靶场&#xff0c;但是是在虚拟机环境下的一个小块分区靶场&#xff1b; 本篇博客主要介绍在本地搭建 DVWA 靶场以及靶场的初始化&#xff0c;后续会陆续更新通关教程。 由于我们是在本地搭建&#xff0c;则需要基于你已经装好 phpstu…

基于Springboot+Vue实现的宿舍管理系统

基于SpringbootVue的宿舍管理系统 1.系统相关性介绍1.1 系统架构1.2 设计思路 2.功能模块介绍2.1 用户信息模块2.2 宿舍管理模块2.3 信息管理模块 3. 源码获取以及远程部署 前言&#xff1a; 在现代教育环境中&#xff0c;学生宿舍的管理显得尤为重要&#xff0c;需要一套能…

【C语言相关问题】C语言中关于大小写字母转换的问题

大家好&#xff0c;这里是争做图书馆扫地僧的小白。非常感谢各位的支持&#xff0c;也期待着您的关注。 目前博主有着C语言、C、linux以及数据结构的专栏&#xff0c;内容正在逐步的更新。 希望对各位朋友有所帮助同时也期望可以得到各位的支持&#xff0c;有任何问题欢迎私信与…
推荐文章