zoukankan      html  css  js  c++  java
  • [More Effective C++ 学习笔记]基础议题和操作符

    <基础议题>

           pointer和reference在继承机制下存在两种型别:

    1. 静态型别是指其声明时的型别;
    2. 动态型别是指它们实际所指的对象来决定。

      条款1:仔细区分pointer和references

      没有null references,一个reference必须总是代表某个对象。

      使用reference之前不需测试其有效性。若使用pointer,通常就是测试它是否为null。

      pointers和references的差异

    1. pointers可以被重新赋值,指向另一个对象;
    2. reference却总是指向(代表)它最初获得的那个对象,即使被重新赋值改变的也是原对象的值。

      operator[]返回某种能够被当做assignment赋值对象,则令operator[]返回一个reference。

      条款2:最好使用C++转型操作符

      static_cast:用于强制类型专函,不涉及继承机制的型别执行类型转换;

      const_cast:用来改变表达式中的常量性(constant)或易变性(volatileness);

      dynamic_cast:用来执行继承机制中安全的向下型别或跨系型别转换动作,即base objects->derived objects。若转换失败,返回null指针(转型对象是指针)或exception(转型对象是reference);

      reinterpret_cast:用户函数指针型别转换。

      条款3:绝对不要以多台(polymorphically)方式来处理数组

      继承性质之一:通过指向其基类对象的指针或引用,来操作继承基类的对象。

      数组声明时,向系统申请 numElemt * sizeof(数组中对象类型)大小的内存。而声名类类型对象时,系统给予对象类类型中数据数据大小的内存。类成员函数最终将转换成全局函数。数组中元素移动一位的内存距离是sizeof(数组对象的类型)。

      条款4:非必要不提供default constructor

      classes类缺乏default constructor在两种情况下出现问题:

    1. 产生数组时,系统调用默认构造函数;
    2. 不适用于许多template-based constainer classes。

      <操作符>

      条款5:对定制的型别转换函数保持警觉

      两种函数允许编译器进行隐式转换:单变量constructor和隐式型别转换操作符。其中,单变量constructors是指能以单一自变量成功调用的constructors。隐式型别转换操作符是指关键词operator之后加上一个型别名称。不需指定返回值类型,因为其返回值型别基本上已经表现于函数名称上。

      根本问题:在你从未打算也未预期的情况下,此类函数可能被调用,而其结果可能是不正确、不直观的程序行为,很难调试。

           关于隐式型别转换操作符的解决方法:以功能对等的另一个函数取代型别转换操作符。而单自变量constructors的解决方法之一:利用关键字explicit。若将constructors声明为explicit,编译器便不能因隐式型别转换的需要而调用它们。

           条款6:区别increment/decrement操作符的前置(prefix)和后置(postfix)形式

           在重载操作符increment/decrement时,为了区分前置式和后置式,让后置式有一个int自变量类型,但没有变量名称,如operator++(int);。在调用后置式时,编译器默默地为该int指定一个0值。

           自增和自减操作符的前置式和后置式返回不同的型别:前置式返回一个reference,后置式返回一个const对象。

      为什么后置式返回一个const对象?(设计classes的一条无上宝典就是:一旦有疑惑,试看ints行为如何并遵守之。)是为了防止如下行为的出现:

      int i = 4;

      i++++;  // 错误,但++++i合法

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

      C++对于真假表达式采用所谓骤死式(即短路求值)评估方式,其含义是一旦该表达式的真假值确定,纵使表达式中还有部分尚未检验,整个评估工作仍然结束。

      条款8:了解各种不同意义的new和delete

      string *ps = new string(“Memory Management”);

      这里使用的new operator是由语言内建的,它总是做下面的两件事,无论如何你不能改变其行为。它的动作分为两方面:

    1. 它分配足够的内存,用于放置某型别的对象;
    2. 它调用constructor,为刚刚分配的内存中的那个对象设置初值。

      operator new表达式

      new operator调用函数operator new执行必要的内存分配动作,你可以在类中重载该函数。函数operator new通常声明为:

      void* operator new(size_t size);

      operator new只负责内存分配,而new operator的责任就是将operator new返回的内存转换为一个对象。

      表达式:string *ps = new string(“Memory Management”);编译器为该语句产生一些代码:

      void *memory = operator new (sizeof (string));   // get raw memory

      call string::string(“Memory Management”) on *memory // initialize objects

      string *ps = static_cast<string *>(memory);              // ps point the new object

      placement new表达式

      placement new表达式是指对一个已存在的内存缓冲区上构筑对象。

           例如:

           

    1 Widget *constructWidgetInBuffer(void *buffer, in widgetSize)
    2 {
    3 
    4     return new(buffer) Widget(widgetSize);
    5 
    6 }

           该函数返回指针,指向一个Widget object,它被构造于传递给此函数的一块内存缓冲区上。因此,placement new的operator new函数如下所示:

           

    1 void * operator new(size_t, void *location)
    2 {
    3     return location;
    4 }

           在函数中没有用到size_t参数,之所以不赋予名称,为的是避免编译器发出错误。

           删除(delete)与内存归还(Deallocation)

           函数operator delete之于内奸的delete operator,就好像operator new之于new operator一样。内存释放动作是函数operator delete执行,通常声明如下:

           void operator delete(void *memoryToBeDeallocation);

           因此,

           delete ps;

           在编译器转换之后:

           ps->~string();               // 调用对象的析构函数

           operator delete(ps);        // 释放对象所占用的内存

           但是需要注意的是,placement new是从已存在的内存区域中构筑对象。因此不能直接调用operator delete来释放该函数申请的内存。因此,调用析构函数函数来析构对象,然后将内存返还回去。

           数组的new和delete

           在给数组分配内存时,new operator 调用operator new[],而delete operator调用operator delete[]。

  • 相关阅读:
    241. Different Ways to Add Parentheses java solutions
    89. Gray Code java solutions
    367. Valid Perfect Square java solutions
    46. Permutations java solutions
    116. Populating Next Right Pointers in Each Node java solutions
    153. Find Minimum in Rotated Sorted Array java solutions
    判断两颗树是否相同
    求二叉树叶子节点的个数
    求二叉树第k层的结点个数
    将二叉排序树转换成排序的双向链表
  • 原文地址:https://www.cnblogs.com/life91/p/3002504.html
Copyright © 2011-2022 走看看