分享

More Effective C++ (M1 -to- M8)

 My Room 2012 2012-03-17
M1:指针与引用的区别
   1.指针可以指向空值,但是引用不行,它必须指向某个对象,因此引用必须初始化。
   2.引用比指针效率高。
   3.指针可以被重新赋值以指向另一个不同的对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变。
summary:当你知道你必须指向一个对象并且不想改变其指向时,或者在重载操作符并为防止不必要的语义误解时,你不应该使用指针。而在除此之外的其他情况下,则应使用指针。
M2:尽量使用C++风格的类型转换
(static_cast, const_cast, dynamic_cast, 和reinterpret_cast).
const_cast用于类型转换掉表达式的const或volatileness属性
dynamic_cast,它被用于安全地沿着类的继承关系向下进行类型转换。它不能被用于缺乏虚函数的类型上
(参见条款M24),也不能用它来转换掉constness
reinterpret_casts的最普通的用途就是在函数指针类型之间进行转换,转换结果几乎都是执行期定义
(implementation-defined)。因此,使用reinterpret_casts的代码很难移植。转换函数指针的代码是
不可移植的(C++不保证所有的函数指针都被用一样的方法表示),在一些情况下这样的转换会产生不正
确的结果(参见条款M31),所以你应该避免转换函数指针类型,除非你处于着背水一战和尖刀架喉的危
急时刻。
static_cast在功能上基本上与C风格的类型转换一样强大,含义也一样
M3:不要对数组使用多态
语言规范中说通过一个基类指针来删除一个含有派生类对象的数组,结果将是不确定的。多态和指针算法不能混合在一起来用,所以数组与多态也不能用在一起。(array[I]只是一个指针算法的缩写:它所代表的是*(array)。)
M4:避免无用的缺省构造函数
无用的缺省构造函数不能确保对象进行了有意义的初始化,成员函数就会因为必须检测成员的有效性而变得复杂,
而且会影响工作效率
M5:谨慎定义类型转换函数
隐式类型转换运算符只是一个样子奇怪的成员函数:operator 关键字,其后跟一个类型符号。你不用定义函数的返回类型,因为返回类型就是这个函数的名字。
根本问题是当你在不需要使用转换函数时,这些的函数缺却会被调用运行。结果,这些不正确的程序会做出一些令人恼火的事情,而你又很难判断出原因。
克服隐式类型转换运算符的缺点解决方法是用不使用语法关键字的等同的函数来替代转换运算符。例如为了把Rational对象转换为double,用asDouble函数代替operator double函数
通过不声明运算符(operator)的方法,可以克服隐式类型转换运算符的缺点,但是单参数构造函数没有那么简单。
容易的方法是利用一个最新编译器的特性,explicit关键字。
第二就是用 proxy classes
template<class T>
class Array
{
public:
  class ArraySize
 {                    // 这个类是新的
  public:
    ArraySize(int numElements): theSize(numElements) {}
    int size() const { return theSize; }
  private:
    int theSize;
  };
Array(int lowBound, int highBound);
  Array(ArraySize size);                  // 注意新的声明
 ...
};
M6:自增(increment)、自减(decrement)操作符前缀形式与后缀形式的区别
  UPInt& operator++();                   // ++ 前缀
  const UPInt operator++(int);              // ++ 后缀
  UPInt& operator--();                    // -- 前缀
  const UPInt operator--(int);              // -- 后缀

他们返回值类型不同以及后缀操作符应该以前缀操作符为基础来实现
我们明确一下,当处理用户定义的类型时,尽可能地使用前缀increment,因为它的效率较高。

M7:不要重载“&&”,“||”, 或“,”
在C/C++里面,“&&”,“||” 使用布尔表达式短路求值法,如果你重载&&或||,就没有办法提供给程序员他们所期望和使用的行为特性,所以不要重载&&和||。
一个包含逗号的表达式首先计算逗号左边的表达式,然后计算逗号右边的表达式;整个表达式的结果是逗号右边表达式的值。重载后你不能保证这个特性,所以不要重载“,”表达式。
你不能重载下面的操作符:
.              .*              ::             ?:
new          delete        sizeof      typeid
static_cast  dynamic_cast  const_cast  reinterpret_cast
你能重载:
operator new        operator delete
operator   new[]    operator delete[]
+    -   *   /   %   ^     &   |     ~
!    =   <   >  +=   -=   *=   /=   %=
^=  &=  |=  <<  >>   >>=  <<=  ==   !=
<=  >=  &&  ||  ++   --    ,   ->*  ->
()  []
当然能重载这些操作符不是去重载的理由,如果你没有一个好理由重载操作符,就不要重载。

M8:理解各种不同含义的new和delete
你想在堆上建立一个对象,应该用new操作符。它既分配内存又为对象调用构造函数。如果你仅仅想分配内存,就应该调用operator new函数;它不会调用构造函数。如果你想定制自己的在堆对象被建立时的内存分配过程,你应该写你自己的operator new函数,然后使用new操作符,new操作符会调用你定制的operator new。如果你想在一块已经获得指针的内存里建立一个对象,应该用placement new。
operator new / operator delete ==(malloc/free) new/delete  而placement new只是返回转递给它的指针,所以只需要显示调用对象的析构函数
分配数组空间时,new操作符调用的是数组分配函数operator new[],它与operator new一样能被重载。
同样delete操作符调用operator delete[],operator delete[]也可以被重载。
new和delete操作符是内置的,其行为不受你的控制,然而它们调用的内存分配和释放函数则可以控制。当你想定制new和delete操作符的行为时,请记住你不能真的做到这一点。你只能改变它们为完成它们的功能所采取的方法,而它们所完成的功能则被语言固定下来,不能改变。(You can modify how they do what they do, but what they do is fixed by the language)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多