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. CentOS安装Eclipse luna

    1  解压 Eclipse luna到/opt tar -zxvf eclipse-java-luna-SR1-linux-gtk-x86_64.tar.gz -C /opt 2  创建软链接 ln ...

  2. 在scrapy中利用Selector来提取数据

    1.创建对象 Selector类的实现位于scrapy.selector模块,创建Selector对象的时候,可以将页面的Html文档字符串传递给Selector构造器方法 2.选中数据 调用Sele ...

  3. Java程序员面试题收集(6)

    <!————————————————————————————基础题122道,代码题19道————————————————————————————> JAVA相关基础知识1.面向对象的特征有 ...

  4. HDFS 块

  5. 跟我一起了解koa(一)

    安装koa2 1.初始化package.json npm init 2.新建app.js,并且安装koa cnpm install --save koa //app.js const koa = re ...

  6. ubuntn16.04指令

    基础知识: ubuntn中的/表示根目录,包括bin,mnt等文件夹 /home表示家目录,/home/user表示用户下的家目录,/root表示root目录 常用指令: 进入root : sudo ...

  7. 【python之路41】web框架

    一.web框架介绍 一共有两种web框架 1.既包含socket又能逻辑处理 tornado框架 2.只能处理逻辑 Django bottle flask  二.web框架的本质 众所周知,对于所有的 ...

  8. CF 578B "Or" Game

    传送门 解题思路 题意大概是给你一个数列,可以进行k次操作,每次操作可以选择一个数乘x,问操作后的或的最大值.根据位运算,位数越高答案越优,所以贪心的使这k次操作全都放到一个数上,这样的结果肯定较优. ...

  9. DynamicDataDisplay 双击获取坐标

    近日由于项目需要,学习了DynamicDataDisplay实现动态曲线图,网上的资料基本上够用了,就是双击获得数据点没能找到资料,只好下载了DynamicDataDisplay的源码来学习.总结共享 ...

  10. jQuery 源码解析(二十九) 样式操作模块 尺寸详解

    样式操作模块可用于管理DOM元素的样式.坐标和尺寸,本节讲解一下尺寸这一块 jQuery通过样式操作模块里的尺寸相关的API可以很方便的获取一个元素的宽度.高度,而且可以很方便的区分padding.b ...