【more effective c++读书笔记】【第2章】操作符(2)

条款7:千万不要重载&&,|| 和, 操作符

1、C++对于“真假值表达式”采用骤死式”评估方式:一旦表达式的真假值确定,即使表达式中还有部分尚未检验,整个评估工作仍告结束。

例子:

char* p;
...
if ((p != 0) && (strlen(p) > 10))
...

不用担心调用strlen时p是否为null指针,因为如果p != 0是否定的,strlen就绝不会调用。

2、例子:

if (expression1 && expression2)

会被编译器视为以下两者之一

a、if (expression1.operator&&(expression2) //假设operator&&是个member function 

bif (operator&&(expression1, expression2)//假设operator&&是个全局函数

如果重载&&或||,“函数调用”语义会取代“骤死式”语义。“函数调用”语义和“骤死式”语义有两个重大区别:a、当函数调用动作被执行,所有参数值都必须评估完成,所以,当我们调用operator&&和operator||时,两个参数都已评估完成,没有什么“骤死式”语义。b、C++语言规范并未明确定义函数调用动作中各参数的评估顺序,所以没办法知道expression1和expression2哪个会被先评估。

所以如果你将&&或||重载,就没有办法提供程序员预期的某种行为模式。所以不要重载&&或||。

3、表达式如果内含逗号,那么逗号左侧会先被评估,然后逗号右侧再被评估,最后整个逗号表达式的结果是以逗号右侧的值为代表。

4、重载逗号操作符,如果是写成non-member function,无法保证左侧表达式一定比右侧表达式更早评估,因为两个表达式都当做函数调用时的自变量传递给重载函数,而你无法控制一个函数的自变量评估顺序;如果写成member function,仍不能保证逗号操作符的左操作数会先被评估,因为编译器并不强迫做这样的事情。因此,重载逗号操作符不能保证其行为像它应该有的那样。所以不要重载逗号操作符。

5、C++语言中不能重载的操作符有:

.            .*             ::           ?:
new          delete         sizeof       typeid
static_cast  dynamic_cast   const_cast   reinterpret_cast
可以重载的操作符有:
operator new      operator delete
operator new[]    operator delete[]
+   -   *   /   %   ^   &   |   ~
!   =   <   >   +=  -=  *=  /=  %=
^=  &=  |=  <<  >>  >>= <<= ==  !=
<=  >=  &&  ||  ++  --  ,   ->*  ->
() [] 

条款8:了解不各种不同意义的new和delete
1new operator由语言内建,不能改变意义,总是做相同的事情。它做两件事:a、分配足够的内存,用来放置某类型对象;b、调用一个constructor,为刚才分配的内存中的对象设定初值。
2new operator的第一步是调用某个函数,执行必要的内存分配动作,可以重载或重写那个函数,改变其行为,这个函数叫operator new。函数operator new通常声明如下:
void * operator new (size_t size);
其返回值为void *,此函数返回一个指针,指向一块原始的、未初始化的内存。参数size表示需要分配对少内存。该函数可以被重载,但第一参数的类型必须总是size_toperator new的唯一任务就是分配内存。
3placement new是一个特殊版本的operator new,允许你在分配好的原始内存上面构建对象。
void * operator new (size_t size, void * location){
	return loaction;
}
完成在一个已经分配好的原始内存上构建一个新的对象。因为针对一个已存在的对象调用constructor并无意义,此时就用到placement new。要使用placement new,必须加上头文件#include <new>
4、如果希望将对象产生于heap,使用new operator。它不但分配内存而且为该对象调用一个constructor。如果只打算分配内存,请调用operator new,那就没有任何constructor会被调用。如果打算在heap objects产生时自己决定内存分配方式,请写一个自己的operator new,并使用new operator,它将会自动调用你所写的operator new。如果打算在已分配的内存中构造对象,请使用placement new
5delete operatornew operator对应,它做两件事:a、析构指针所指的对象,b、释放被该对象占用的内存。
例子:
string *ps;
...
delete ps;        //使用delete operator
delete ps;会造成编译器产生近似以下的代码:
ps->~string();          //调用对象的dtor operator
operator delete(ps);    //释放对象所占用的内存
6operator deleteoperator new对应,完成内存的释放。如果只打算处理原始的、未初始化的内存,应该完全回避new operator  delete operator,改调用operator new 取得内存并以operator delete归还给系统:
void *buffer = operator new (50 * sizeof(char));  //分配足够的内存,放置50个chars,没有任何ctors
...
operator delete (buffer);                         //释放分配的内存,没有任何dtors
7placement deleteplacement new对应。如果使用placement new, 在某内存块中产生对象,应该避免对那块内存使用delete operator。因为delete operator会调用operator delete来释放内存,但该内存内含的对象最初并非是由operator new分配而来的。毕竟placement new只是返回它所接受的指针而已,并不知道那个指针从哪里而来。所以为了抵消该对象的constructor的影响,应该直接调用该对象的destructor
8、面对数组时,还有几点需要注意,如:
string *ps = new string[10];  //分配一个对象数组
上述例子内存分配不再是operator new,而是operator new[]的函数负责,它也可以被重载。operator new[]必须针对数组中的每个对象调用一个constructor。同样,当delete operator被用于数组,它会针对数组中的每个元素调用其destructor,然后再调用operator delete[]释放内存。


版权声明:本文为博主原创文章,未经博主允许不得转载。

原文地址:https://www.cnblogs.com/ruan875417/p/4785426.html