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. python进阶_浅谈面向对象进阶

    python进阶_浅谈面向对象进阶 学了面向对象三大特性继承,多态,封装.今天我们看看面向对象的一些进阶内容,反射和一些类的内置函数. 一.isinstance和issubclass  class F ...

  2. Redis 分布式锁进化史

    按:系统架构经过多年演进,现在越来越多的系统采用微服务架构,而说到微服务架构必然牵涉到分布式,以前单体应用加锁是很简单的,但现在分布式系统下加锁就比较难了,我之前曾简单写过一篇文章,关于分布式锁的实现 ...

  3. JS的闭包问题

    1.什么是“闭包” 是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 2.闭包的应用场景 (1)保护变量的安全实现JS私有属性和私有方法 (2)在 ...

  4. RandomRowFilter(3)

    比较容易理解 用来随机抽取 RandomRowFilter:从名字上就可以看出其大概的用法,本过滤器的作用就是按照一定的几率(<=0会过滤掉所有的行,>=1会包含所有的行)来返回随机的结果 ...

  5. 更改电脑用户名(可更改C:\Users\用户名)

    参考:http://blog.csdn.net/zhang_jinhe/article/details/40624847 假设我们需要将帐户A改名为B. 首先我们需要用另一个管理员帐户C登陆系统. 1 ...

  6. 移动端适配(rem单位定义方法)

    注:移动端必须写: <meta name="viewport" content="width=device-width, user-scalable=no, ini ...

  7. 20190921-雅礼Day1

    #error 此人太蒻无法编译 #include<iostream> main(){} Before 哦…… -O2 T1 序列问题:分块(莫队),树状数组,线段树,分治 离线 or 在线 ...

  8. java opencv 4.0.1安装配置

    如果没有把dll扔到jdk会报错 Exception in thread "AWT-EventQueue-0" java.lang.UnsatisfiedLinkError: no ...

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

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

  10. Django高级实战 开发企业级问答网站

    Django高级实战 开发企业级问答网站 整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课程本身没问题,大家看的 ...