《More Effective C++》学习

条款1:仔细区别 pointers 和 references

  1. 引用应该被初始化,指针可以不被初始化。
  2. 不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针的要高。因为在使用引用之前不需要测试它的合法性。
  3. 指针与引用的另一个重要的不同是指针可以被重新赋值以指向另一个不同的对象。但是
    引用则总是指向在初始化时被指定的对象,以后不能改变。
    std::string s1("Nancy");std::string s2("Clancy");std::string& rs = s1; // rs引用s1std::string* ps = &s1; // ps指向s1rs = s2; // rs仍旧引用s1,但是s1的值现在是"Clancy"

条款2:最好使用C++转型操作符

这四个操作符是:static_cast、const_cast、dynamic_cast、reinterpret_cast。

  • const_cast 最普通的用途就是转换掉对象的 const 属性
  • dynamic_cast,它被用于安全地沿着类的继承关系向下进行类型转换。这就是说,你能用 dynamic_cast 把指向基类的指针或引用转换成指向其派生类或其兄弟类的指针或引用,而且你能知道转换是否成功。失败的转换将返回空指针(当对指针进行类型转换时)或者抛出异常(当对引用进行类型转换时)。它不能被用于缺乏虚函数的类型上。
  • 如你想在没有继承关系的类型中进行转换,你可能想到 static_cast。
  • reinterpret_cast,使用这个操作符的类型转换,其 的 转 换 结 果 几 乎 都 是 执 行 期 定 义 ( implementation-defined )。 因此,使用reinterpret_casts 的代码很难移植。reinterpret_casts 的最普通的用途就是在函数指针类型之间进行转换。

double result = static_cast<double>(firstNumber)/secondNumber;

条款3:绝对不要以多态(polymorphically)方式处理数组

在对数组进行传参使用多态时,程序会crash; 因为数组在移位至下一数据时,步长是形参(基类)的size,而不是指针实际指向数据类型(派生类)的size,所以会数组会移位至一个非法的地址 。

#include <iostream>
using namespace std;class Base
{
public:virtual void test(){cout<<"Base::test()"<<endl;}int a;
};class Derived: public Base
{
public:void test(){cout<<"Derived::test()"<<endl;}int b, c;
};void testArray(Base bArray[], int n)
{for(int i =0; i<n; i++)bArray[i].test();  //i = 1时,程序crash; 编译器原先已经假设数组中元素
//与Base对象的大小一致,但是现在数组中每一个对象大小却与Derived一致,
//派生类的长度比基类要长,数组将移动到一个非法位置。
}int main()
{Base *p = new Derived[2];  testArray(p, 2);    
}

条款4:非必要不提供 default construcor

提供无意义的缺省构造函数也会影响类的工作效率。如果成员函数必须测试所有的部分是否都被正确地初始化,那么这些函数的调用者就得为此付出更多的时间。而且还得付出更多的代码,因为这使得可执行文件或库变得更大。它们也得在测试失败的地方放置代码来处理错误。如果一个类的构造函数能够确保所有的部分被正确初始化,所有这些弊病都能够避免。缺省构造函数一般不会提供这种保证,所以在它们可能使类变得没有意义时,尽量去避免使用它们。

class EquipmentPiece {
public:EquipmentPiece(int IDNumber) {}virtual ~EquipmentPiece() {}int a = 1;float b = 2.0;
};//避免无用的缺省构造函数int ID1 = 1, ID2 = 2;EquipmentPiece bestPieces3[] = { EquipmentPiece(ID1), EquipmentPiece(ID2) }; 
// 正确,提供了构造函数的参数// 利用指针数组来代替一个对象数组typedef EquipmentPiece* PEP; // PEP指针指向一个EquipmentPiece对象PEP* bestPieces5 = new PEP[10]; // 也正确// 在指针数组里的每一个指针被重新赋值,以指向一个不同的EquipmentPiece对象for (int i = 0; i < 10; ++i)bestPieces5[i] = new EquipmentPiece(ID1);for (int i = 0; i < 10; ++i)delete bestPieces5[i];delete bestPieces5;

利用指针数组代替一个对象数组这种方法有两个缺点:第一你必须删除数组里每个指针所指向的对象。如果忘了,就会发生内存泄漏。第二增加了内存分配量,因为正如你需要空间来容纳EquipmentPiece对象一样,你也需要空间来容纳指针.

解决办法:

	//分配足够的 raw memory,给一个预备容纳 10 个EquipmentPiece objects 的//数组使用void* rawMemory = operator new[](10 * sizeof(EquipmentPiece));//让 basePiece 指向此块内存,使这块内存被视为一个 EquipmentPiece 数组EquipmentPiece* bestPieces6 = static_cast<EquipmentPiece*>(rawMemory);//利用 “placement new”构造这块内存中的 EquipmentPiece objectsfor (int i = 0; i < 10; ++i)new(&bestPieces6[i]) EquipmentPiece(i);//将 basePieces 中的各个对象,以其构造顺序的相反顺序析构掉for (int i = 9; i >= 0; --i)bestPieces6[i].~EquipmentPiece(); // 如果使用普通的数组删除方法,程序的运行将是不可预测的//因为 basePieces 并非来自 new operator//释放 raw memoryoperator delete[](rawMemory);

条款5:对定制的“类型转换函数”保持警觉

单自变量 constructors 是指能够以单一自变量成功调用的 constructors。如此的 constructor 可能声明拥有单一参数,也可能声明拥有多个参数,并且除了第一参数之外都有默认值。

class Name{
public:Name(const string& s);  //可以把string转换成Name...
};class Rational{
public:Rational(int numerator = 0,int denominator = 1);//可以把 int 转换成 Rational...
};

有两种函数允许编译器进行这些的转换:单参数构造函数(single-argument constructors)和隐式类型转换运算符。

隐式类型转换运算符只是一个样子奇怪的成员函数:operator关键字,其后跟一个类型符号。你不用定义函数的返回类型,因为返回类型就是这个函数的名字。

class Rational {
public:Rational(int numerator = 0, int denominator = 1) // 转换int到有理数类{n = numerator;d = denominator;}operator double() const // 转换Rational类成double类型{return static_cast<double>(n) / d;}double asDouble() const{return static_cast<double>(n) / d;}private:int n, d;
};//谨慎定义类型转换函数Rational r(1, 2); // r的值是1/2double d = 0.5 * r; // 转换r到double,然后做乘法fprintf(stdout, "value: %f\n", d);std::cout << r << std::endl; // 应该打印出"1/2",但事与愿违,是一个浮点数,而不是一个有理数,隐式类型转换的缺点

一般来说,越有经验的 C++程序员就越喜欢避开类型转换运算符。例子,在打印Rational类实例时,你忘了为 Rational 对象定义 operator<<。你可能想打印操作将失败,因为没有合适的的 operator<<被调用。但是你错了。当编译器调用 operator<<时,会发现没有这样的函数存在,但是它会试图找到一个合适的隐式类型转换顺序以使得函数调用正常运行。类型转换顺序的规则定义是复杂的,但是在现在这种情况下,编译器会发现它们能调用Rational::operator double 函数来把 r 转换为 double 类型。所以上述代码打印的结果是一个浮点数,而不是一个有理数。这样的函数有时候会引起预料之外的调用。可以用显示的转换函数替代

构造函数用 explicit 声明,如果这样做,编译器会拒绝为了隐式类型转换而调用构造函数。显式类型转换依然合法。

条款6:区别 increament/decrement 操作符的前置(prefix)和后置(postfix)形式

参考文章:《More Effective C++》笔记_more effective c++ pdf github-CSDN博客

More Effective C++-CSDN博客

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

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

相关文章

mac环境桌面版docker错误修改daemon.json配置后,启动失败,一直卡在Docker Engine starting界面的解决方法

如下图&#xff1a;当桌面版docker的配置被错误的修改后&#xff0c;配置修改重启应用时&#xff0c;会一直卡在启动界面 此时需要找到mac下该桌面版docker的配置文件位置&#xff0c;手动修改恢复&#xff0c;然后重启应用。 daemon.json文件一般默认在隐藏文件夹下&#xff0…

SSL证书安装在哪?

安装SSL证书的具体步骤取决于你使用的服务器软件和操作系统。一般来说&#xff0c;SSL证书通常用于加密网站上的数据传输&#xff0c;因此安装过程主要涉及到Web服务器的配置。以下是一般步骤&#xff0c;但请注意这可能因你的具体环境而异。 永久免费SSL证书_永久免费https证…

一天一个设计模式---单例模式

概念 单例模式是一种创建型设计模式&#xff0c;其主要目的是确保一个类只有一个实例&#xff0c;并提供一个全局访问点。这意味着在应用程序中的任何地方&#xff0c;只能有一个实例存在&#xff0c;而不会创建多个相同类型的实例。 具体内容 单例模式通常包括以下几个要素…

Pytorch种torch.cat与torch.stack的区别

torch.cat 和 torch.stack 是 PyTorch 中用于拼接张量的两个不同的函数&#xff0c;它们的主要区别在于拼接的方式和创建的维度。 torch.cat&#xff1a; 拼接方式&#xff1a; torch.cat 是按照给定的维度&#xff08;dim 参数&#xff09;将多个张量沿着该维度拼接。在拼接的…

IntelliJ IDEA编译Maven工程:一步步详解

IntelliJ IDEA编译Maven工程:一步步详解 Apache Maven是一个流行的自动化构建工具,被广泛应用于Java项目的构建管理中。IntelliJ IDEA作为一个功能强大的集成开发环境(IDE),它提供了对Maven工程的天然支持,并能简化构建过程。本文将详细介绍如何在IntelliJ IDEA中编译Ma…

盘点2023 | 工业互联网:聚焦五大功能体系,加速推进新型工业化进程

党的二十大作出了推进新型工业化&#xff0c;加快建设制造强国、网络强国、数字中国的战略部署。“把高质量发展的要求贯穿新型工业化全过程&#xff0c;把建设制造强国同发展数字经济、产业信息化等有机结合&#xff0c;为中国式现代化构筑强大物质技术基础”深刻阐述新型工业…

Unity 利用UGUI之Slider制作进度条

在Unity中使用Slider和Text组件可以制作简单的进度条。 首先在场景中右键->UI->Slider&#xff0c;新建一个Slider组件&#xff1a; 同样方法新建一个Text组件&#xff0c;最终如图&#xff1a; 创建一个进度模拟脚本&#xff0c;Slider_Progressbar.cs using System.C…

PDF 文件操作指南

PDF 文件操作指南 PDF 文件介绍 PDF 是一种便携式文档格式&#xff08;Portable Document Format&#xff09;的缩写&#xff0c;是由 Adobe 公司创建的一种用于文档交换的文件格式。PDF 格式的文件可以跨平台、跨操作系统和跨设备进行共享和查看&#xff0c;其最大特点是在不…

【NodeJS学习 day1——引入】

前端核心技术体系 HTMLCSS→JavaScript→Node.js→Webpack→React→Vue→小程序 Node.js是什么&#xff1f; Node.js是一款应用程序&#xff0c;是一款软件&#xff0c;它可以运行JavaScript。 Node.js的作用&#xff1f; 1. 开发服务器应用 用户通过url向服务器发送请求&#…

矿泉水除硝酸盐的方法分析

摘要&#xff1a;饮用水中的硝酸盐污染已成为全球性问题&#xff0c;对人类健康构成威胁。本文将介绍离子交换树脂技术在饮用水除硝酸盐方面的应用与优势&#xff0c;帮助您了解如何有效去除饮用水中的硝酸盐&#xff0c;保障水质安全。 正文&#xff1a; 一、饮用水中硝酸盐…

使用kubesphere的devops部署SpringCloud项目

devops部署SpringCloud项目 环境说明部署流程创建DevOps工程填写流水线信息创建流水线jenkinsfileDockerfiledeploy.yaml 环境说明 已经安装kubesphere的devops组件安装教程可参考官方文档:https://v3-1.docs.kubesphere.io/zh/docs/pluggable-components/devops/ 部署流程 创…

redis 从0到1完整学习 (十六):内存回收之 key 过期处理策略

文章目录 1. 引言2. redis 源码下载3. redisDb 结构体4. Redis 过期 key 的处理策略4.1 惰性删除 (Lazy Expiration)4.2 定期删除 (Active Expire / Periodic Expiration)* 5. 参考 1. 引言 前情提要&#xff1a; 《redis 从0到1完整学习 &#xff08;一&#xff09;&#xff…

Lumerical Monitors------frequency domain power monitor 频率域功率监视器

frequency domain power monitor 频率域功率监视器 引言正文引言 这里给大家介绍一下 frequency domain power monitor。 正文 首先,我们可以通过以下方式添加 frequency domain power monitor 到我们的工程文件中: 在 general tab 中,有一个共同的监视器设置 simulati…

python将文件打包为exe经常性的被360软件杀掉问题

在python将程序打包后&#xff0c;发生360会一直将自己打包出来的文件进行清楚&#xff0c;查询资料发生可能是因为调用了os库。 使用pyinstaller打包遇到的问题&#xff0c; The pathlib package is an obsolete backport of a standard library package and is incompatibl…

苍穹外卖Day01——总结1

总结1 1. 软件开发整体介绍1.1 软件开发流程1.2 角色分工1.3 软件环境 2. 苍穹外卖项目介绍2.1 项目介绍2.2 技术选项 3. Swagger4. 补充内容&#xff08;待解决...&#xff09; 1. 软件开发整体介绍 1.1 软件开发流程 1.2 角色分工 从角色分工里面就可以查看自己以后从事哪一…

跨国制造业组网方案解析,如何实现总部-分支稳定互联?

既要控制成本&#xff0c;又要稳定高效&#xff0c;可能吗&#xff1f; 在制造企业积极向“智造”发展、数字化转型的当下&#xff0c;物联网、人工智能、机器人等新型设备加入到生产、管理环节&#xff0c;为企业内部数据传输提出了更高的要求。而当企业规模扩大&#xff0c;数…

记录一次接近24万条数据导入Mysql的过程

由于开发项目的需求&#xff0c;之前有部分数据要写入阿里云的表格存储&#xff0c;过了一年多时间&#xff0c;表A的数据量接近24万条&#xff0c;现在需要将表A的数据转到Mysql中。 利用官方工具导出数据后&#xff0c;发现文件里面有238999条数据&#xff0c;文件大小是460…

python什么是装饰器

1 python什么是装饰器 python装饰器本身是一个函数&#xff0c;它在不修改原函数或类的情况下&#xff0c;接受一个原函数或类作为参数&#xff0c;为原函数或类添加新逻辑&#xff0c;然后返回函数或类。是管理函数和类的一种方式。 此处原函数或类&#xff0c;为被修饰的函…

Unity中URP下深度图的线性转化

文章目录 前言一、_ZBufferParams参数有两组值二、LinearEyeDepth1、使用2、Unity源码推导&#xff1a;3、使用矩阵推导&#xff1a; 三、Linear01Depth1、使用2、Unity源码推导3、数学推导&#xff1a; 前言 在之前的文章中&#xff0c;我们实现了对深度图的使用。因为&#…

C语言中常用的字符串函数(strlen、sizeof、sscanf、sprintf、strcpy)

C语言中常用的字符串函数 文章目录 C语言中常用的字符串函数1 strlen函数2 sizeof函数2.1 sizeof介绍2.2 sizeof用法 3 sscanf函数3.1 sscanf介绍3.2 sscanf用法3.3 sscanf高级用法 4 sprintf函数4.1 背景4.2 sprintf用法 5 strcpy函数5.1 strcpy介绍5.1 strcpy用法 1 strlen函…