More Effective C++ 《运算符》

为什么要进行运算符重载?

运算符重载是为了解决类对象之间的运算的,通常的运算符只用于算术运算,如常量int之间,因为编译器已经定义了;而一个类两个对象之间成员进行运算必须重新定义,让编译器在遇到对象运算时能按我们要求的进行运算,这就是运算符重载的意义,即重定义运算符,因此你可以看到,运算符重载就是为类对象服务的,那么两个对象的成员进行运算那必须先获得对象本身啦,所以运算符重载参数必须含有类指针或引用。

Item M5:慎定义类型转换函数

C++编译器能够在两种数据类型之间进行隐式转换(implicit conversions),它继承
了 C 语言的转换方法,例如允许把 char 隐式转换为 int 和从 short 隐式转换为 double。
※有两种函数允许编译器进行转换:单参数构造函数( single-argument
constructors)和隐式类型转换运算符。
·单参数构造函数是指只用一个参数即可以调用的构造函数。
·隐式类型转换运算符只是一个样子奇怪的成员函数:operator 关键字,其后跟一个类
型符号。你不用定义函数的返回类型,因为返回类型就是这个函数的名字。例如为了允许
Rational(有理数)类隐式地转换为 double 类型(在用有理数进行混合类型运算时,可能有
用),你可以如此声明 Rational 类:
class Rational {
public:

operator double() const; // 转换 Rational 类成
}; // double 类型
在下面这种情况下,这个函数会被自动调用:
Rational r(1, 2); // r 的值是 1/2
double d = 0.5 * r; // 转换 r 到 double,
cout << r; // 错误! Rationa 对象没有
·当我们要打印一个对象时,就是出现灾难性。编译器会发现它们能调用Rational::operator double 函数来把 r 转换为 double 类型。
·解决方法是用不使用语法关键字的等同的函数来替代转换运算符。例如为了把
Rational 对象转换为 double,用 asDouble 函数代替 operator double 函数
cout << r.asDouble(); // 正确, 用 double 类型
总结:在多数情况下,这种显式转换函数的使用虽然不方便,但是函数被悄悄调用的情况不再会发生,这点损失是值得的。慎定义类型转换函数!!!

Item M6:自增(increment)、自减(decrement)操作符前缀形式与后缀形式的区别

class UPInt { // “unlimited precision int”
public:
UPInt& operator++(); // ++ 前缀
const UPInt operator++(int); // ++ 后缀
UPInt& operator–(); // – 前缀
const UPInt operator–(int); // – 后缀
UPInt& operator+=(int); // += 操作符,UPInts
// 与 ints 相运算

};
这些操作符前缀与后缀形式返回值类型是不同的。前缀形式返回一个引用,后缀形式返回一个 const 类型。
// 前缀形式:增加然后取回值
UPInt& UPInt::operator++()
{
*this += 1; // 增加
return *this; // 取回值
}
// 后缀形式:取回值然后增加
const UPInt UPInt::operator++(int)
{
UPInt oldValue = *this; // 取回值
++(*this); // 增加
return oldValue; // 返回被取回的值
}
如果你很关心效率问题,当你第一次看到后缀函数时,你可能觉得有些问题。
这个函数必须建立一个临时对象以做为它的返回值,(参见条款 M19),上述实现代码建立了
一个显示的临时对象(oldValue),这个临时对象必须被构造并在最后被析构。前缀
increment 函数没有这样的临时对象。由此得出一个令人惊讶的结论,如果仅为了提高代码
效率,UPInt 的调用者应该尽量使用前缀 increment,少用后缀 increment,除非确实需要
使用后缀 increment。

Item M7:不要重载“&&”,“||”, 或“,”

C/C++使用布尔表达式短路求值法(short-circuit evaluation)。这表示一旦
确定了布尔表达式的真假值,即使还有部分表达式没有被测试,布尔表达式也停止运算。
重载函数 operator&& 和operator||,以函数调用法替代了短路求值法,破坏了从左像右中断法则。
可重载的运算符如下:
+ - * / % ^ & | ~
! = < > += -= = /= %=
^= &= |= << >> >>= <<= == !=
<= >= && || ++ – , ->
->
() []

Item M8:理解各种不同含义的new和delete

string *ps = new string(“Memory Management”);
·new操作符第一部分是分配足够的内存以便容纳所需类型的对象。第二部分是它调用构造函数初始化内存中的对象。

placement new:

new operator就是new操作符,不能被重载,假如A是一个类,那么A * a=new A;实际上执行如下3个过程。
(1)调用operator new分配内存,operator new (sizeof(A))
(2)调用构造函数生成类对象,A::A()
(3)返回相应指针
·事实上,分配内存这一操作就是由operator new(size_t)来完成的,如果类A重载了operator new,那么将调用A::operator new(size_t ),否则调用全局::operator new(size_t ),后者由C++默认提供。
※一般来说,使用new申请空间时,是从系统的“堆”(heap)中分配空间。申请所得的空间的位置是根据当时的内存的实际使用情况决定的。但是,在某些特殊情况下,可能需要在已分配的特定内存创建对象,这就是所谓的“定位放置new”(placement new)操作。
定位放置new操作的语法形式不同于普通的new操作。例如,一般都用如下语句A* p=new A;申请空间,而定位放置new操作则使用如下语句A* p=new (ptr)A;申请空间,其中ptr就是程序员指定的内存首地址。
如果你用 placement new 在内存中建立对象,你应该避免在该内存中用 delete 操作符。
因为 delete 操作符调用 operator delete 来释放内存,但是包含对象的内存最初不是被
operator new 分配的,placement new 只是返回转递给它的指针。你应该显式调用对象的析构函数。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值