01:仔细区别 pointers 和 references

1:没有所谓的null reference,但是可以将 pointer 设为null。由于 reference 一定得代表某个对象,C++ 因此要求 references 必须有初值,但是pointers 就没有这样的限制。

没有所谓的 null reference 这个事实意味使用 references 可能会比使用 pointers更富效率。这是因为使用 reference 之前不需测试其有效性。

2:Pointers 和 references 之间的另一个重要差异就是,pointers 可以被重新设值,指向另一个物件,reference 却总是指向它最初获得的那个物件。

02:最好使用C++转型操作符

1:低阶转型动作,像goto一样地被视为程序设计上的贱民。尽管如此,某些情况下,转型可能是必要的。

2:旧式的C转型方式,几乎允许你将任何型别转换为任何其他型别,这是十分拙劣的。更好的方式是每次转型都能够更精确地指明意图。旧式转型的第二个问题是它们难以辨识,旧式转型的语法形式是 (type) expression 这样的,不只是人眼难以辨识,诸如 grep 之类的工具也无法区分语法上极类似的一些非转型写法。

3:为解决C旧式转型的缺点,C++ 导入四个新的转型运算符:static_cast, const_cast, dynamic_cast 和 reinterpret_cast。

static_cast基本上拥有与C旧式转型相同的威力与意义,以及相同的限制。例如你不能够利用 static_cast 将一个 struct 转型为 int 或将一个double转型为pointer;

const_cast最常见的用途就是将某个对象的常数性去除掉;

dynamic_cast用来执行继承体系中“安全的向下转型或跨系转型动作”。也就是说你可以利用 dynamic_cast,将“指向  base class objects之pointers或references”转型为“指向 derived(或 sibling base)class objects之 pointers 或 references”,并得知转型是否成功。如果转型失败,会以一个null指针(当转型对象是指针)或一个exception(当转型对象是  reference)表现出来。

最后一个转型运算符是reinterpret_cast。这个运算符的转换结果几乎总是与编译平台息息相关。所以 reinterpret_casts 不具移植性。最常见的用途是转换“函数指针”。假设有一个函数指针数组:

typedef void (*FuncPtr)();
FuncPtr funcPtrArray[];

现在希望将以下函数指针放进 funcPtrArray 中:

int doSomething();

使用 reinterpret_cast,可以强迫编译器编译通过:

funcPtrArray[] = reinterpret_cast<FuncPtr>(&doSomething);

函数指针的转型动作,并不具移植性,某些情况下这样的转型可能会导至不正确的结果,所以应该尽量避免将函数指针转型。

03:绝对不要以多态方式处理数组

假设有下面的代码:

class BST { ... };
class BalancedBST: public BST { ... }; void printBSTArray(ostream& s, const BST array[], int numElements)
{
for (int i = ; i < numElements; ++i) {
s << array[i];
}
} BalancedBST bBSTArray[];
printBSTArray(cout, bBSTArray, );

上面的代码编译器不会报错,但是针对array[i],它的本质是*(array+i),编译器在编译期间必须知道数组中的对象大小。函数原型中,参数array声明为类型为BST的数组,所以编译器认为数组中的每个元素必然都是BST对象,然而实际上数组元素都是BalancedBST对象,这就有问题了。

上述问题有时候会以一种更隐秘的方式出现:

void deleteArray(ostream& logStream, BST array[])
{
delete [] array;
}
BalancedBST *balTreeArray = new BalancedBST[];
deleteArray(cout, balTreeArray);

delete数组时,数组中每一个元素的析构函数就会被调用,所以,delete []array这样的语句,会产生下面的代码:

for (int i = the number of elements in the array - ; i >= ; --i)
{
array[i].BST::~BST();
}

错误的原因与上面是一样的。

04:非必要不提供default constructor

1:有许多对象,如果没有外来信息,就没有办法执行一个完全的初始化动作。例如一个用来表现联络簿字段的 class,如果没有获得外界指定的人名,产生出来的对象将毫无意义。

凡可以“合理地从无到有产出对象”的 classes,都应该内含 default constructors,而“必须有某些外来信息才能产出对象”的 classes,则不必拥有default constructors。

2:假定有一个NoDefault类,它没有定义自己的默认构造函数,却有一个接受一个string实参的构造函数。NoDefault没有默认构造函数,意味着它具有以下限制:

a:具有NoDefault成员的每个类的每个构造函数,必须通过传递一个初始的string值给NoDefault构造函数来显式地初始化 NoDefault成员。

b:编译器将不会为具有NoDefault类型成员的类合成默认构造函数。如果这样的类希望提供默认构造函数,就必须显式地定义,并且默认构造函数必须显式地初始化其NoDefault成员。

c:NoDefault类型不能用作动态分配数组的元素类型。

d:NoDefault类型的静态分配数组必须为每个元素提供一个显式的初始化式。

e:如果有一个保存NoDefault对象的容器,例如vector,就不能使用接受容器大小而没有同时提供一个元素初始化式的构造函数。

f:如果NoDefault作为virtual base classe,则要求其最底层的派生类--不管距离多么遥远--都必须知道其意义,从而提供NoDefault的构建自变量。

3:对于有些类而言,盲目定义default constructors返回会带来不好的影响。假设在某些公司,所有仪器设备都必须贴上一个识别号码;为这种用途而设计的类EquipmentPiece,如果其中没有供应适当的ID号码,将毫无意义,因此不应该定义默认构造函数。然而如果为其定义了:

class EquipmentPiece {
public:
EquipmentPiece(int IDNumber = UNSPECIFIED);
...
private:
static const int UNSPECIFIED;
// 一个魔术数字,
// 意味没有被指定 ID 值
};

这种代码,肯定会使得该类内其他的成员函数变得复杂:因为允许一个无ID的EquipmentPiece对象能够存在,其他成员函数必须检查ID是否存在,这就造成了时间和空间上的代价。如果构造函数保证所有字段能正确的初始化,就不会出现这种问题。因此,这样的类就不应该有默认构造函数。

More Effective C++: 01基础议题的更多相关文章

  1. ###《More Effective C++》- 基础议题

    More Effective C++ #@author: gr #@date: 2015-05-11 #@email: forgerui@gmail.com 一.仔细区别pointers和refere ...

  2. More Effective C++ 基础议题(条款1-4)总结

    More Effective C++ 基础议题(条款1-4)总结 条款1:仔细区别pointers和references 如果有一个变量,其目的是用来指向(代表)另一个对象,但是也有可能它不指向(代表 ...

  3. Java 之 I/O 系列 01 ——基础

    Java 之 I/O 系列 目录 Java 之 I/O 系列 01 ——基础 Java 之 I/O 系列 02 ——序列化(一) Java 之 I/O 系列 02 ——序列化(二) 整理<疯狂j ...

  4. linux 01 基础命令

    linux 01 基础命令 对于Linux要记住一个概念,一切皆文件,哪怕是目录,也是一个文件 1.修改用户密码 sudo passwd pyvip@Vip:~$ #pyvip表示用户名, Vip表示 ...

  5. 01.基础架构:一条SQL查询语句是如何执行的?学习记录

    01.基础架构:一条SQL查询语句是如何执行的?学习记录http://naotu.baidu.com/file/1c8fb5a0f2497c3a2655fed89099cb96?token=ff25d ...

  6. 01 基础版web框架

    01 基础版web框架 服务器server端python程序(基础版): import socket server=socket.socket() server.bind(("127.0.0 ...

  7. More Effective C++ - 章节一 : 基础议题

    1. 仔细区分 pointers 和 references references和pointers的差别描述如下: pointer:当需要考虑"不指向任何对象"时,或者是考虑&qu ...

  8. PHP学习笔记01——基础语法

    <!DOCTYPE html> <html> <?php // 1.使用$加变量名来表示变量,php是弱类型语言,不要求在使用变量前声明,第一次赋值时变量才被创建 $a ...

  9. 一步步Cobol 400 上手自学入门教程01 - 基础概念

    先学习基础概念 1.COBOL字符:包含: User-defined words 用户定义字符 ŸSystem-names ŸReserved words 关键字 2.用户定义字符User-defin ...

随机推荐

  1. windows下docker 启动jenkins成功,浏览器无法访问,拒绝了我们的连接

    [问题现象] 在Windows下使用docker启动了一个jenkins,翻越了无数的坑,最后的启动命令为 docker run --name jenkins -u root -p 8000:8000 ...

  2. Cesium官方教程5--地形图层

    原文地址:https://cesiumjs.org/tutorials/Terrain-Tutorial/ Cesium支持渐进流式加载和渲染全球高精度地形,并且包含海.湖.河等水面效果.相对2D地图 ...

  3. 单例模式(Singleton)(单一实例)

    单例模式基本要点: 用于确保一个类只有一个实例,并且这个实例易于被访问. 让类自身负责保存他的唯一实例.这个类可以保证没有其他实例创建,并且他可以提供一个访问实例的方法,来实现单例模式. (1)把构造 ...

  4. Django项目:CRM(客户关系管理系统)--22--14PerfectCRM实现King_admin分页的省略显示

    {#table_data_list.html#} {## ————————08PerfectCRM实现King_admin显示注册表的字段表头————————#} {% extends 'king_m ...

  5. js实现五子棋人机对战源码

    indexhtml <!DOCTYPE html> <html lang="en"> <head> <meta charset=" ...

  6. Oil Deposits HDU - 1241 (dfs)

    Oil Deposits HDU - 1241 The GeoSurvComp geologic survey company is responsible for detecting undergr ...

  7. mysql8.0 的坑 hibernate连接配置坑

    https://blog.csdn.net/qq_36448800/article/details/81180881 这篇文章对于连接配置说的是对的,也比较全面

  8. 洛谷 P3742 umi的函数【构造】

    题目背景(https://www.luogu.org/problemnew/show/P3742) umi 找到了一个神秘的函数 f. 题目描述 这个函数接受两个字符串 s1,s2.这些字符串只能由小 ...

  9. Socket与TcpClient的区别

    原文:Socket与TcpClient的区别 Socket和TcpClient有什么区别 原文:http://wxwinter.spaces.live.com/blog/cns!C36588978AF ...

  10. 简单的layui二级联动

    用layui实现省市二级联动, 需要注意的是使用layui之后, 你看到的下拉选框就不是option了,而是一些div 1.select表单 2.JS, ajax返回的是普通的数组