《Effective C++》学习笔记

条款01:把 C++ 看成一个语言联邦


C++由几个重要的次语言构成

C语言:区块,语句,预处理器,数组,指针等等。

类:class,封装,继承,多态......(动态绑定等等)

模板:涉及泛型编程,内置数种可供套用的函数或者类。

STL:STL是个模板库,主要涉及容器,算法和迭代器

在不同情况下使用适合的部分,可以使 C++ 实现高效编程
 

条款02:尽量以const,enum,inline替换 #define

因为或许 #define 不被视为语言的一部分,这经常会产生让人捉摸不透的bug

1,#define 修饰的记号,在预处理的时候可能就替换成了对应的数值,当代码出错的时候会提到具体的数值,但是我们不知道这个数值是干什么的

解决之道:以一个常量替换上述的宏(#define)

const double AspectRatio = 1.653   //大写名称通常用于宏,因此这里改变名称写法

2.#define 无法创建一个 class 专属常量,因为 #define 不重视作用域。一旦宏被定义,那么它就在其后的编译过程中有效,它不提供任何封装性

//enum hack 补偿做法:
enum 枚举量{para1 = value1, para2 = value2,......}
//将一个枚举类型的数值当作 int 类型使用
//和 #define 很像,都不能取地址,但它没有 #define 的缺点

3.看看这个神奇的宏

template<typename T>
inline void callWithMax(const T& a,const T& b)  //由于不知道T是什么,所以采用 pass by reference-to-const
{f(a > b? a:b);
}
  • 对于单纯常量,最好以 const 对象或 enums 替换 #defines
  • 对于形似函数的宏(macros),最好改用 inline 函数替换#define

条款03:尽可能使用 const

const 允许你指定一个于一约束,使一个值不被改动

如果关键字 const 出现在星号左边,表示被指物是常量,如果出现在星号右边,表示指针自身是常量。如果出现在两边,表示都是常量

const 修饰迭代器

const 修饰成员函数

        如果两个成员函数只是常量性不同(其他相同)则可以发生重载

                const 类对象调用 const 成员函数

                non-const 类对象调用普通成员函数

bitwise:

const 成员函数不能改变(除 static)成员变量的值,因为常函数里 this 指针指向的值不可改变。同理,const 对象不可以调用 non-const 函数,因为函数有能力更改成员属性的值。

        但是若成员变量是一个指针,仅仅改变指针指向的值却不改变指针地址(地址是 this 指向的值),则不算是 const 函数 ,但能够通过 bitwise 测试。

        使用 mutable 可以消除 non-static 成员变量的 bitwise constness 约束。

3、当 const 和 non-const 成员函数有实质的等价实现时,利用两次转型,令 non-const 调用 const 可以避免代码重复。

const char& operator[](int pos) const
{//...//...return name[pos];
}char& operator[](int pos)
{returnconst_cast<char&>//移除第一次转型添加的 const(static_cast<const classname>(*this)[pos]//把 classname 类型数据转换为 const classname//使得能够调用 const operator[]);
}

条款04:确定对象被使用前已先被初始化

具体规则比较复杂,最佳处理办法就是:永远在使用对象之前先将它初始化。对于无任何成员的内置类型,必须手工完成此事:

内置类型以外的任何其他类型,初始化责任落在构造函数身上

不要混淆了赋值和初始化

这会使对象带有你指定的值,并不是最佳的做法

如果成员变量是 const 或 reference,它们就一定需要初值,不能被赋值。为了避免需要记住成员变量合适必须在成员初值列中初始化,何时不需要,最简单的做法是:总是使用成员初值列。

class 的成员变量总是以其声明次序被初始化,即使更改初始值列表的次序也不影响

使用 local-static 对象替换 non-local-static 对象

函数内 static 对象是 local-static 对象,函数外 static 对象是 non-local-static 对象。

C++ 对 “定义于不同编译单元内的 non-local static 对象”的初始化次序并无明确定义

解决办法:利用一个函数,定义并初始化本 static 对象,并返回它的引用。类似于 singleton 设计模式,调用这个函数获得想要的对象引用,而不是直接获取这个对象引用

条款05:了解C++默默编写并调用哪些函数

惟有当这些函数被需要,它们才会被编译器创建出来。下面代码造成上述每一个函数被编译器产出:

拷贝运算符注意事项:

若成员变量中有引用,或者被 const 修饰等等,拷贝运算符不可被调用。

条款06:若不想使用编译器自动生成的函数,就该明确拒绝

编译器自动生成的函数都是 public 函数,所以我们将 public 改为 private,就可以防止对象调用拷贝构造。

注:private 只有成员函数和友元函数可以调用。

同时也产生了一个问题,如何防止拷贝在成员函数或友元函数中被调用?

答案是建立一个父类,在父类中定义 private 拷贝函数,子类( person 等等)继承父类。因为子类不可以调用父类的 private 函数:

条款07:为多态基类声明 virtual 析构函数

如果一个基类没有声明 virtual 析构函数,那么当销毁一个指向该基类的派生类的指针时就会造成诡异的局部销毁,派生的部分还残留着

给 base class 一个 virtual 析构函数,以后删除 derived class 对象就会如我们希望的那样销毁整个对象

当 class 不企图被当作 base class ,令其析构函数为 virtual 往往是个馊主意

如果类中含有 virtual 函数,其体积会增加

条款08:别让异常逃离析构函数

C++并不禁止析构函数吐出异常,但它不鼓励你这样做

在C++程序中,若是同时存在两个异常,则要么结束程序,要么导致不确定行为。结束程序,剩余的操作就无法完成,这对于程序员来说是一个麻烦。

异常处理方法:

try
{...}
//try 内部写可能产生异常的语句,没有产生异常,则catch语句不执行,产生则一一匹配
//catch 用于捕获并处理异常,和 case 有异曲同工之妙
catch(...)
{1、可以使用 abort(); 函数终止程序2、可以吞下这个异常,在 catch 内部做一些处理
}

条款09:绝不在构造和析构过程中调用 virtual 函数

众所周知,在类的操作中,父类比子类先构造,而子类也比父类先析构(多态也是如此,多态先通过 virtual 找到子类析构,再析构父类),所以在构造父类的时候,子类对象还未进行初始化,在析构父类的时候,子类已经被销毁。

此时,如果父类的构造和析构函数中有 virtual ,则该函数无法找到子类的地址(或者说无视子类,因为子类被销毁/未被初始化),使程序发生不明确的行为。

所以 virtual 函数的调用无法下降至子类,但是子类可以将必要的构造信息向上传递到父类:

条款10:令 operator= 返回一个 reference to *this

为了实现以上效果

条款11:在 operator= 中处理“自我赋值”

a[ i ] = a[ j ]      //如果 i == j 那么也是自我赋值

*px = *py      //潜在的自我赋值

一般而言如果某段代码操作 pointers 或 references 而被它们用来“指向多个相同类型的对象”,就需要考虑这些对象是否为同一个了。实际上只要来自同一个继承体系,就可能造成“别名”

条款12:复制对象时勿忘每一个成分

当你编写一个 copying 函数,请确保(1)复制所有的 local 成员变量 (2)调用所有 base classes 内的适当的 copying 函数

不应该令 copy assignment 操作符调用 copy 构造函数,反过来也一样。构造函数用来初始化新对象,而 assignment 操作符只施行于已初始化对象身上

条款13:以对象管理资源

为确保返回的资源总是被释放,我们需要将资源放进对象内,当控制流离开负责析构该资源的函数时,该对象的析构函数会自动释放那些资源

auto_ptr 是个“类指针(pointer-like)对象”也就是所谓的“智能指针”,其析构函数自动对所指对象调用 delete 

注意不要让多个 auto_ptrs 同时指向同一个对象。如果那样的话,对象会被删除一次以上,会导致未定义的错误行为

为了预防这个问题,auto_ptrs 有一个特别的性质--若通过 copy 构造函数或 copy assignment 操作符复制它们,它们就会变成 null,而复制所得的指针将取得资源的唯一拥有权

auto_ptr 和 tr1::shared_ptr 两者都在其析构函数内做 delete 而不是 delete[] 动作,那意味着在动态分配而得到的 array 身上使用它们是不合适的,但是这种行为还是可以通过编译

条款14:在资源管理类中小心 copying 行为

禁止复制    可以将copy设置为父类的私有函数

对底层资源祭出“引用计数法”

复制底部资源      进行深拷贝

转移底部资源的拥有权  拷贝之后删除被拷贝物

条款15:在资源管理类中提供对原始资源的访问

//使用智能指针如 auto_ptr 或 tr1::shared_ptr 保存 factory 函数如 createInvestment 的调用结果:
std::tr1::shared_ptr<Investment> pInv(createInvestment());
//假如希望以某个函数处理 Investment 对象,像这样:
int daysHeld(const Investment* pi)   //返回投资天数
int days = daysHeld(pInv);   //错误!需要的是一个Investment*指针,传给它的却是个类型为 //tr1::shared_ptr<Investment> 的对象

条款16:成对使用 new 和 delete 时要采取相同形式

delete 最大的问题在于:即将被删除的内存之内有多少对象?这个问题的答案决定了有多少个析构函数必须被调用

如果调用 new 时使用 [ ] ,必须在对应调用 delete 时也使用 [ ].如果调用 new 时没有使用 [ ] ,那么也不该在对应调用 delete 时使用 [ ].

条款17:以独立语句将 newed 对象置入智能指针

processWidegt(std::tr1::shared_ptr<widget>(new Widget),priority());

虽然我们在此使用"对象资源管理式资源",上述调用却可能泄露资源

万一对 priority 的调用导致异常,在此情况下"new Widget" 返回的指针将会遗失,因为它尚未被置入 tr1:shared_ptr 内

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

用户可能传错数字或者传入无效的数字

参考文章:EFFECTIVE C++ (万字详解)(一)-CSDN博客

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

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

相关文章

GitHub入门介绍:从小白到大佬的旅程

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

mybatis动态SQL-choose-when-otherwise

1、建库建表 create database mybatis-example; use mybatis-example; create table emp (empNo varchar(40),empName varchar(100),sal int,deptno varchar(10) ); insert into emp values(e001,张三,8000,d001); insert into emp values(e002,李四,9000,d001); insert into…

一、运行时数据区域

根据 《Java 虚拟机规范》的规定&#xff0c;Java 虚拟机所管理的内存将会包括以下截个运行时数据区域&#xff0c;如图所示。 1、程序计数器 程序计数器是一块较小的内存空间&#xff0c;它可以看做是当前线程所执行的字节码的行号指示器。在 Java 虚拟机的概念模型里&#x…

或许你更胜一筹呢

还记得刚出来时&#xff0c;一位前辈对我说过的一句话&#xff0c;“一定不要妄自菲薄”。说实话&#xff0c;一开始我并不知道这个成语的具体含义。后面百度才知道 妄自菲薄&#xff1a;过分地看轻自己 当时还没毕业&#xff0c;无论是从能力还是学识方面&#xff0c;我都不知…

C#学习笔记 - C#基础知识 - C#从入门到放弃

C# 第1节 C# 简单介绍1.1 C# 是什么1.2 C# 强大的编程功能1.3 C# 发展史1.4 C#与Java区别 第2节 C#程序结构2.1 Hello world2.2 C# 结构解析 第3节 C#基本语法3.1 第1节 C# 简单介绍 1.1 C# 是什么 C# 的发音为“C Sharp”&#xff0c;是一门由微软开发并获得了 ECMA&#xf…

【算法通关村】链表反转经典问题解析

&#x1f6a9;本文已收录至算法学习之旅 一.基础反转 我们通常有两种方法反转链表&#xff0c;一种是直接操作链表实现反转操作&#xff0c;一种是建立虚拟头节点辅助实现反转操作。 力扣习题链接&#xff1a;206. 反转链表 (1) 直接操作实现反转 我们需要一个变量pre来保…

Jmeter接口自动化测试 —— Jmeter变量的使用

​在使用jmeter进行接口测试时&#xff0c;我们难免会遇到需要从上下文中获取测试数据的情况&#xff0c;这个时候就需要引入变量了。 定义变量 添加->配置元件->用户自定义的变量 添加->配置元件->CSV 数据文件设置 变量的调用方式&#xff1a;${变量名} 变量的…

Qt6.5类库实例大全:QWidget

哈喽大家好&#xff0c;我是20YC小二&#xff01;欢迎扫码关注公众号&#xff0c;现在可免费领取《C程序员》在线视频教程哦&#xff01; ~下面开始今天的分享内容~ 1. QWidget介绍 QWidget 是 Qt 框架中的一个核心类&#xff0c;用于创建图形用户界面(GUI)应用程序的基本可视…

iic应用篇

一.iic的优点 1. IIC总线物理链路简单&#xff0c;硬件实现方便&#xff0c;扩展性非常好&#xff08;1个主机控制器可以根据需求增加从机数量&#xff0c;同时删减从机数量也不会影响总线通信&#xff09;&#xff1b;IIC总线只需要SDA和SCL两条信号线&#xff0c;相比于PCI/…

融了超24亿一分钱不花,放银行吃利息,这家存储创企厉害了

​引言&#xff1a;AI与大模型风起云涌&#xff0c;催生了这匹存储“黑马” 【全球存储观察 &#xff5c; 科技热点关注】 这家总部设在美国的存储初创公司&#xff0c;真的赶上AI与大模型时代的风口了。Vast Data公司最新再次获得E轮融资1.18亿美元&#xff0c;但是这个存储…

【MySQL】:表的约束(上)

表的约束 一.非空约束二.default约束三.列描述四.zerofill五.主键1.单个主键2.复合主键 真正约束字段的是数据类型&#xff0c;但是数据类型约束很单一&#xff0c;需要有一些额外的约束&#xff0c;更好的保证数据的合法性&#xff0c;从业务逻辑角度保证数据的正确性。比如有…

TrustGeo代码理解(一)main.py

代码链接&#xff1a;https://github.com/ICDM-UESTC/TrustGeo 一、导入各种模块和数据库 # -*- coding: utf-8 -*- import torch.nnfrom lib.utils import * import argparse, os import numpy as np import random from lib.model import * import copy from thop import p…

sillyGirl(傻妞机器人)安装以及对接go-cqhttp(2023年12月)

目录 编写的原因 下载傻妞 注意&#xff01;&#xff01;注意&#xff01;&#xff01;&#xff01;注意&#xff01;&#xff01;&#xff01;&#xff01; 同样的下载go-cqhttp 安装以及配置 go-cqhttp 下载screen 创建go-cqhttp窗口 创建sillyGirl窗口 常见错误 编写…

Python玩转PDF:几招搞定的高效操作方法

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 当在Python中操作PDF时&#xff0c;有几种常见的方法&#xff0c;每种方法都有其独特的优点和用例。在本文中&#xff0c;我们将深入探讨这些方法&#xff0c;并提供丰富的示例代码&#xff0c;以帮助大家更好地…

「Verilog学习笔记」可置位计数器

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 timescale 1ns/1nsmodule count_module(input clk,input rst_n,input set,input [3:0] set_num,output reg [3:0]number,output reg zero);reg [3:0] cnt ; always (posed…

3-分布式存储之Ceph

任务背景 虽然使用了分布式的glusterfs存储, 但是对于爆炸式的数据增长仍然感觉力不从心。对于大数据与云计算等技术的成熟, 存储也需要跟上步伐. 所以这次我们选用对象存储. 任务要求 1, 搭建ceph集群 2, 实现对象存储的应用 任务拆解 1, 了解ceph 2, 搭建ceph集群 3, 了…

深度学习 | Pytorch深度学习实践 (Chapter 12 Basic RNN)

十二、Basic RNN —— 实际上就是对线性层的复用 使用RNN最重要的两点&#xff1a; 了解序列数据的维度&#xff1b;循环过程所用的权重共享机制&#xff1b; 一般就是自己写个循环&#xff0c;权重层重复用就行了&#xff1b; 回顾&#xff1a;-----------------------------…

破局创新,天翼云HBlock如何以小见大、以柔克刚?

引言&#xff1a;另辟蹊径开拓创新 不走传统存储厂商的“寻常路” 【全球存储观察 &#xff5c; 科技热点关注】 在分布式块存储领域&#xff0c;大部分厂商的安装软件套件大小都在GB级。然而&#xff0c;天翼云破天荒地将存储资源盘活系统HBlock的软件安装包浓缩到了170MB&a…

linux中proc与sys的区别

在Linux系统中&#xff0c;/sys目录和/proc目录都是特殊的虚拟文件系统&#xff0c;用于提供对系统内核和设备信息的访问。 虽然它们的作用有一些重叠&#xff0c;但它们在功能和用途上有一些区别。 功能&#xff1a; /sys目录主要用于提供对设备和驱动程序的信息和配置的访…

Python-乒乓球小游戏【附完整源码】

乒乓球小游戏 乒乓球小游戏是一个简单而有趣的2D页面交互式游戏&#xff0c;玩家可以通过键盘输入来控制球拍上下移动来接球&#xff0c;从而体验乒乓球的乐趣。该游戏有单人和双人两种模式 运行效果&#xff1a; 一&#xff1a;主程序&#xff1a; import sys import cfg …