• 【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[]释放内存。


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

  • 相关阅读:
    Hadoop配置文件-mapred-site.xml
    Hadoop配置文件-core-site.xml
    Hadoop配置文件-hdfs-site.xml
    Hadoop学习-HDFS篇
    Android应用程序签名详解(转载)
    [Android]启用Service时Context.bindService()和Context.startService()的区别与联系(转载)
    Android模拟器代理设置(转载)
    如何在Android中利用Instrumentation来进行测试(转载)
    防止SQL注入的方法(转载)
    JavaScript常用语句 转载
  • 原文地址:https://www.cnblogs.com/ruan875417/p/4785426.html
Copyright © 2020-2023  润新知