Item 5 Be wary of user-defined conversion functions.对用户定制的“类型转换函数”保持警觉。
(1)含有单自变量的构造函数,包括两种情况,构造函数只有一个参数,或者有多个参数,但是除第一个参数外,都有默认值。
class Name
{
public:
Name(const string& s):name(s) {}
private:
string name;
};
以上的构造函数可以把string隐式转换为Name类型。
class Rational
{
public:
Rational(int numerator=0,int denominator=1):n(numerator),d(denominator) {}
operator double() const;
private:
int n;
int d;
};
以上构造函数把int转为Rational。
(2)隐式类型转换操作符,关键词operator之后加上一个类型名称,作为一个类的成员函数。
上例中的operator double() const;就是把一个Rational类型转为double。
这类函数的问题:会在你未打算或预期的情况下被调用。
隐式类型转换操作符的解决办法:提供一个功能对等的另一个函数来取代类型转换操作符。
比如:double asDouble() const;
单自变量的构造函数的解决办法:将构造函数声明为explicit,这样编译器便不再因隐式类型转换的需要而调用单自变量的构造函数。
Item 6 Distinguish between prefix and postfix forms of increment and decrement operators.区分自增和自减的前置和后置形式。
class UInt
{
public:
UInt():i(0) {}
UInt& operator ++();
const UInt operator ++(int);
UInt& operator --();
const UInt operator --(int);
private:
unsigned int i;
};
UInt& UInt::operator++()
{
this->i+=1;
return *this;
}
const UInt UInt::operator++(int)
{
UInt oldValue=*this;
++(*this);
return oldValue;
}
在以上的例子中,注意几点:
(1)前置形式的++和--返回的是对象的引用。
(2)后置形式的++和--返回的是const对象。
(3)后置形式的++和--有一个int型的参数。
(4)后置形式的++和--以前置形式的实现为基础。
得出这个结论:i++++;是错误的;++++i;则没有问题。
Item 7 Never overload &&, || or ,. 千万不要重载&&,||,,。
C++对于真假值表达式采用“骤死式”方式,即一旦确定表达式的真假,即使表达式中还有部分尚未计算,整个计算工作结束。
如果重载&&和||,则会采用下述函数调用的方式调用重载过的&&:
operator&&(expression1,expression2);
这里注意两点:
(1)函数调用要求所有参数计算完成。
(2)C++语言规范并未明确定义函数调用动作中各个参数的计算顺序。
因此我们重载这两个运算符的话很难达到内置类型的效果,固不要去重载。
如果一个表达式内涵逗号,则逗号左侧表达式先计算,然后再计算右侧的表达式,最后以右侧表达式的值作为整个表达式的结果。
同样的道理,我们自己的实现的成员函数的逗号运算符,无法保证左侧的表达式先计算。
Item 8 Understand the different meanings of new and delete.了解各种不同意义的new和delete。
new operator和delete operator:
当我们写出这样的语句:
Rational *pr=new Rational(1,2);
这里的new是new操作符,是语言内建的,其动作分为两方面:
(1)分配足够的内存,来放置某对象。
(2)调用一个构造函数,刚刚才分配的内存设置初值。
删除上述对象通过delete pr;这里的delete也是语言内建的。
我们能改变的就是第一步中的分配内存的方式。这一步是通过operator new完成的。
operator new和operator delete:
operator new的声明为:void* operator new(size_t size); 它的唯一任务就是分配内存。通过此种形式分配的内存要通过operator delete释放。
使用的时候可同调用其他的函数一样使用:
void* rawMemory=operator new(sizeof(Rational));
operator delete(rawMemory);
placement new:声明为:void * operator new(size_t,void *location);
调用者已经知道指向内存的指针了,placement new要做的就是将它获得的指针再返回。
调用方式如下:
Rational* contructRational(void* buffer,int numernator,int denominator)
{
return new(buffer) Rational(numernator,denominator);
}
这样的方法适用于使用共享内存或者内存映射的I/O的情况。
通过这样的方式调用的placement new无需释放内存。
operator new[]和operator delete[]:
UInt *pu=new UInt[10]; //调用operator new[]以分配足够容纳10个UInt对象的内存,然后针对每个元素调用默认构造函数。
delete[] pu; //为数组中的每个元素调用析构函数,然后调用operator delete[]以释放内存。