条款1:仔细区别pointers和references 首先你必须认知一点,没有所谓的null reference。一个reference必须总是代表某个对象。所以如果你有一个变量,其目的是用来指向(代表)另一个对象,但是也有可能它不指向(代表)任何对象,那么你应该使用pointer,因为你可以将pointer设为null。换个角度看,如果这个变量总是必须代表一个对象,也就是说如果你的设计并不允许这个变量为null,那么你应该使用reference。
what about this?
char *pc = 0;
char &rc = *pc;
这是有害的行为,其结果无可预期,编译器可以产生任何可能的输出。
由于reference一定得代表某个对象,C++因此要求references必须有初值:
string &rs; //错误!references必须初始化
string s("xyzzy");
string &rs = s; //没问题,rs指向s
但是pointers就没有这样的限制:
string *ps; //未初始化的指针,有效,但风险高
(没有所谓的null reference)这个事实意味使用references可能会比使用pointers更富效率。这是因为使用reference之前不需测试其有效性:
void printDouble(const double &rd)
{
cout << rd; //不需测试rd; 它一定代表某个double
}
如果使用pointers,通常就得测试它是否为null:
void printDouble(const double *pd)
{
if (pd) //检查是否为null pointer
{
cout << *pd;
}
}
pointers和references之间的另一个重要差异就是,pointers可以被重新赋值,指向另一个对象,reference却总是指向(代表)它最初获得的那个对象:
string s1("Nancy");
string s2("Clancy");
string &rs = s1; //rs代表s1
string *ps = &s1; //ps指向s1
rs = s2; //rs仍然代表s1,但是s1的值现在变成了"Clancy".
ps = &s2; //ps现在指向s2;s1没有变化
还有其他情况也需要使用reference,例如当你实现某些操作符的时候。最常见的例子就是operator[]。这个操作符很特别地必须返回某种(能够被当做assignment赋值对象)的东西:
vector<int> v(10);
v[5] = 10;
如果operator[]返回pointer,上述最后一个语句就必须写成这样子:
*v[5] = 10;
但这使v看起来好像是个以指针形成的vector,事实上它不是。为了这个因素,你应该总是令operator[]返回一个referenc。
结论:当你知道你需要指向某个东西,而且绝不会改指向其他东西,或是当你实现一个操作符而其语法需求无法由pointers达成,你就应该选择references。任何其他时候,请采用pointers。
条款2:最好使用C++转型操作符
C++四个新的转型操作符:
static_cast:转型操作。
const_cast:移除某个对象的常量性。
dynamic_cast:将指向base class objects之pointers或references转型为指向derived(或sibling base)class objects之pointers或references,并得知转型是否成功。如果转型失败,会以一个null指针(当转型对象是指针)或一个exception(当转型对象时reference)表现出来。第二个的用途是找出被某对象占用的内存的起始点。
reinterpret_cast:最常用途是转换函数指针性别。
typedef void (*FuncPtr)();
FuncPtr funcPtrArray[10];
假设由于某种原因,你希望将以下函数的一个指针放进funcPtrArray中:
int doSomething();
使用reinterpret_cast,可以强迫编译器了解你的意图:
funcPtrArray[0] = reinterpret_cast<FuncPtr>(&doSomethind); 条款3:绝对不要以多态方式处理数组 class BST{};
class BalancedBST:public BST{};
void printBSTArray(ostream& s, const BST array[], int numElements)
{
for (int i = 0; i < numElements; ++i)
{
s << array[i];
}
}
当你将一个由BST对象组成的数组传给此函数,没问题:
BST BSTArray[10];
printBSTArray(cout, BSTArray, 10);
然而如果你将一个BalancedBST对象所组成的数组交给printBSTArray函数,会发生什么事:
BalancedBST bBSTArray[10];
printBSTArray(cout, bBSTArray, 10);
你的编译器会被误导。这种情况下它仍假设数组中每一元素的大小是BST的大小,但其实每一元素的大小事BalanceBST的大小。由于derived classes通常比其base classes有更多的data members,所以derived class objects通常都比其base class objects来得大。这样的话,结果无可预期。
如果你尝试通过一个base class指针,删除一个derived class objects组成的数组,那么上述问题还会以另一种不同面貌出现。下面是你可能做出的错误尝试:
void deleteArray(ostream& logStream, BST array[])
{
logStream << "Deleting array at adress "
<< static_cast<void*>(array) << '\n';
delete [] array;
}
BalancedBST *balTreeArray = new BalancedBST[50];
deleteArray(cout, balTreeArray);
C++语言规格中说,通过base class指针删除一个由derived classes objects构成的数组,其结果未有定义。
posted on 2012-11-04 21:53
zhuxin 阅读(98)
评论(0) 编辑 收藏 引用 所属分类:
More Effective C++