More Effective C++读书笔记---运算符

来源:互联网 发布:淘宝店铺客服的链接 编辑:程序博客网 时间:2024/06/09 20:56

运算符重载,允许给予你的自定义类型有着和C++内建类型完全相样的语法,更有甚者,它们允许你将强大的能量注入到运算符背后的函数体中。然而它也是很难驾驭的,单参数的构造函数和隐式类型转换尤其棘手,因为它们会被调用在没有任何的源代码显示了这样的调用的地方
五、条款5--谨慎定义类型转换函数
1.C++编译器能够在两种数据类型之间进行隐式转换(implicit conversions),它继承了C语言的转换方法。这种可怕的转换可能会导致数据的丢失
2.1所提到的是语言本身的特性,你无能为力。不过当你定义自己的类型时,就有更多的控制力,因为你能选择是否提供函数让编译器进行隐式类型转换
3.有两种函数允许编译器进行这些转换:单参数构造函数(single-argument-constructors)和隐式类型转换运算符
4.单参数构造函数:只定义了一个参数的函数或虽定义了多个参数但第一个参数以后的所有参数都有缺省值
5.隐式类型转换操作符只是一个样子奇怪的成员函数:operator关键字,其后跟一个类型符号。你不用定义函数的返回类型,因为返回类型就是这个函数的名字
6.说了上面几条,根本问题是当你在不需要使用转换函数时,这些函数却会被调用运行。它表明了隐式类型转换的缺点:它们的存在将导致错误的发生
7.解决6的办法是,用不使用语法关键字的等同的函数来替代转换运算符(显式调用转换成员函数),虽然这种显式转换函数的使用不方便,但是函数被悄悄调用的情况不再会发生
8.通过单参数构造函数进行隐式类型转换更难消除 解决办法:(1)explicit关键字 (2)使用自定义类型来代替构造函数的单参数
六、条款6--自增(increment)、自减(decrement)操作符前缀形式与后缀形式的区别
1.句法问题:重载函数间的区别决定于它们的参数类型上的差异,但是不论是increment或decrement的前缀还是后缀都只有一个参数。为了解决这个语言问题,C++规定后缀形式有一个int类型的参数,当函数被调用时,编译器传递一个0做为int参数的值给该函数
class UPInt
{
public:
 UPInt& operator++(); // ++前缀
 const UPInt operator++(int); // ++后缀
 UPInt& operator--()  // --前缀
 const UPInt operator--(int); // --后缀
 UPInt& operator +=(int)  // +=操作符,UPInts
};// 注意返回值类型,前缀形式返回一个引用,后缀形式返回一个const类型
2.从你开始做C程序员那天开始,你就记住increment的前缀形式有时叫做“增加然后取回”,后缀形式叫做“取回然后增加”
UPInt& UPInt::operator++()
{
 *this += 1;  // 增加
 return *this;  // 取回
}

const UPInt UPInt::operator++()
{
 UPInt oldValue = *this;  // 取回值
 ++(*this);   // 增加
 return oldValue;
}
3.后缀操作符函数没有使用它的参数。它的参数只是用来区分前缀和后缀函数调用。如果没有在函数里使用参数,许多编译器会显示警告信息,很令人讨厌。为了避免这些警告信息,一种经常使用的方法是省略掉你不想使用的参数名称
七、条款7--不要重载"&&","||",或","
1.与C一样,C++使用布尔表达式短路求值法(short-circuit evaluation)。这表示一旦确定了布尔表达式的真假值,即使还有部分表达式没有被测试,布尔表达式也停止运算
2.如果你重载&&或||,就没有办法提供给程序员他们所期望和使用的行为特性,因为你以函数调用法替代了短路求值法,极大地改变了游戏规则
3.C++语言规范没有定义函数参数的计算顺序
4.C++的逗号表达式规则:一个包含逗号的表达式首先计算逗号左边的表达式,然后计算逗号右边的表达式;整个表达式的结果是逗号右边表达式的值
5.提到逗号表达式的规则,主要是要重载,你需要模仿这个行为特性。不幸的是你无法模仿
6.你不能重载的操作符:. .* :: ?: new delete sizeof typeid
7.你可以重载的操作符:operator new   operator delete   operator new[]    operator delete[]
+ - * / % ^ & | ~ ! = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= && || ++ -- , ->* -> () []
8.能重载操作这些操作符不是去重载的理由。操作符重载的目的是使程序更容易阅读,书写和理角,而不是用你的知识去迷惑其他人。如果没有一个好理由重载操作符,就不要重载
八、条款8--理解各种不同含义的new和delete
1.new操作符(new operator)和new操作(operator new)
2.new操作符完成的功能分两部分,第一部分是分配足够的内存以便容纳所需类型的对象。第二部分是它调用构造函数初始化内存中的对象。new操作符总是做这两件事情,你不能以任何方式改变它的行为。你所能改变的是如何为对象分配内存。
3.new操作符调用一个函数来完成必需的内存分配,你能够重新或重载这个函数来改变它的行为
4.new操作符为分配内存所调用函数的名字是operator new
5.operator new声明:
void* operator new( size_t size );
返回值类型是void*,因为这个函数返回一个未经处理(raw)的指针,未初始化内存。你能增加额外的参数重载函数operator new,但是第一个参数类型必须是size_t。
像malloc一样,operator new的职责是分配内存,它对构造函数一无所知
6.有时你有一些已经被分配但是尚未处理的(raw)内存,你需要在这些内存中构造一个对象,你可以使用一个特殊的operator new,它被称为placement new
7.为了使用placement new,你必须使用语句#include <new>
8.如果你想在堆上建立一个对象,应该用new操作符。它既分配内存又为对象调用构造函数。如果你仅仅想分配内存,就应该调用operator new函数,它不会调用构造函数。如果你想定制自己的堆对象被建立时的内存分配过程,你应该写你自己的operator new函数,然后使用new操作符,new操作符会调用你定制的operator new。如果你想在一块已经获得指针的内存里建立一个对象,应该用placement new
9.为了避免内存泄漏,每个动态内存分配必须与一个等同相反的deallocation对应。函数operator delete与delete操作符的关系与operator new与new操作符的关系一样。

原创粉丝点击