More Effective C++ (操作符)
条款5:对定制的“类型转换函数”保持警觉
C++允许编译器在不同类型之间执行隐式转换,这正是你可以将char转换为int,short转换为都变了的原因。
对于自定义类型则有单自变量构造函数和隐式类型转换操作符两种方法。单自变量构造函数是指能够以单一自变量成功调用的构造函数,隐式类型转换操作符是关键词operator后加上类型名词的成员函数。
很多时候隐式转换是未预期的,因为当你不小心写错了某些代码,而实际上却能通过编译,所以写隐式类型转换操作符时要额外注意。
对于第一种情况因此造成的问题,可以采用C++的关键词explicit,编译器便不能因隐式转化而调用他们。
旧式的消除自单变量构造函数带来的隐式转换的方法是,通过内嵌自定义类型作为参数来避免隐式转换,因为已经隐式转换的类型不能再隐式转换。
条款6:区别increment/decrement操作符的前置和后置形式
C++中重载++操作的前置和后置通过在后置式加入int参数来区分,--同理
class UPInt {
public:
UPInt& operator++();//前置式 ++UPInt
const UPInt operator++(int);//后置式 UPInt++
};
UPInt& UPInt::opeator++()
{
*this += 1;
return *this;
}
const UPInt UPInt::operator++(int)
{
UPInt oldValue = *this;
++(*this);
return oldValue;
}
前置式返回引用,后置式返回const对象,这样是为了避免i++++的情况出现,如果不加const,i++++对于i仍然是加了一次,因为第二个++是对i++返回的临时对象的操作,而++++i是合法的。
为了维护和体现设计原则,后置式自增和自减操作符应当以前置式的为基础,如此只要维护前置式即可。
条款7:千万不要重载&&,||和,操作符
和C一样,C++对于“真假表达式”采用所谓的“骤死式”评估方式,即一旦该表达式的真假值确定,即使表达式中还有部分尚未检验,整个评估工作仍告结束。
int rangeCheck(int index)
{
if(index < lowerBound || index > upperBound)
...
}
//如果index的值小于lowerBound,它就绝不会拿来和upperBound比较
C++允许你为“用户定制类型”量身定做&&和||操作符,即使用operator && 和 operator || 操作符进行重载,它于上面的骤死式评估方式的区别在于:第一,当函数调用动作被执行,所有的参数都必须评估完成。第二,C++语言规范并未明确定义函数调用动作中各参数的评估顺序,而骤死式指明了从左到右的顺序。
对于逗号表达式,C++规定,逗号表达式左侧会先被评估,然后逗号的右侧被评估,最后,整个逗号表达式的结果以逗号右侧的值为代表。而无论是自定义的non-member function还是member function都无法控制表达式的评估顺序,因此不要重载逗号操作符。
C++不能重载的操作符:
. .* :: ?:
new delete sizeof typeid
static_cast dynamic_cast const_cast reinterpret_cast
可以重载的操作符:
operator new operator delete
operator new[] operator delete[]
+ - * / % ^ & | ~
! = < > += -= *= /= %=
^= &= |= << >> >>= <<= == !=
<= >= && || ++ -- , ->* ->
() []
条款8:了解各种不同意义的new和delete
new operator和operator new之间的差异: string *ps = new string("null");
中使用的new是所谓的new operator操作,这个操作符是语言内建的,就像sizeof一样,不能够被改变意义,总是做相同的事情:第一,它分配做个的内存,用来放置某类型的对象。第二,它调用一个构造器,为刚才分配的内存中的那个对象设定初值。你可以改变的是用来容纳对象的那块内存的分配行为,new operator调用某个函数来执行必要的内存分配,你可以重载那个函数,改变其行为,这就是operator new。通常声明为 void * operator new (size_t size);
你可以直接使用它来分配内存,就像malloc函数一样 void *rawMemory = operator new (sizeof(string));
针对已有的内存,你想要使用operator new来操作,可以使用placement new来操作,具体就是额外接受一个void*参数
class Widget {
public:
Widget(int widgetSize);
void* operator new(size_t, void* location)
{
return location;
}
};
Widget* constructWidgetInBuffer(void* buffer, int widgetSize)
{
return new (buffer) Widget(WidgetSize);
}
为避免资源泄露,每个动态分配的行为都必须匹配一个相应但反向的释放动作。函数operator delete之于delete operator就像operator new之于new operator。delete operator负责调用对象析构动作,然后调用对象operator delete动作。
数组的new和delete有其对应的operator new[]和operator delete[]动作。
Member discussion