📄 mc1.htm
字号:
...update(dynamic_cast<SpecialWidget*>(pw)); // fine, passes to update a pointer // to the SpecialWidget pw points to // if pw really points to one, // otherwise passes the null pointervoid updateViaRef(SpecialWidget& rsw);updateViaRef(dynamic_cast<SpecialWidget&>(*pw)); // fine, passes to updateViaRef the // SpecialWidget pw points to if pw // really points to one, otherwise // throws an exceptiondynamic_casts are restricted to helping you navigate inheritance hierarchies. They cannot be applied to types lacking virtual functions (see also Item 24), nor can they cast away constness: int firstNumber, secondNumber;...double result = dynamic_cast<double>(firstNumber)/secondNumber; // error! no inheritance is involvedconst SpecialWidget sw;...update(dynamic_cast<SpecialWidget*>(&sw)); // error! dynamic_cast can't cast // away constnessIf you want to perform a cast on a type where inheritance is not involved, you probably want a static_cast. To cast constness away, you always want a const_cast.The last of the four new casting forms is reinterpret_cast. This operator is used to perform type conversions whose result is nearly always implementation-defined. As a result, reinterpret_casts are rarely portable.The most common use of reinterpret_cast is to cast between function pointer types. For example, suppose you have an array of pointers to functions of a particular type: typedef void (*FuncPtr)(); // a FuncPtr is a pointer // to a function taking no // args and returning voidFuncPtr funcPtrArray[10]; // funcPtrArray is an array // of 10 FuncPtrsLet us suppose you wish (for some unfathomable reason) to place a pointer to the following function into funcPtrArray: int doSomething();You can't do what you want without a cast, because doSomething has the wrong type for funcPtrArray. The functions in funcPtrArray return void, but doSomething returns an int: funcPtrArray[0] = &doSomething; // error! type mismatchA reinterpret_cast lets you force compilers to see things your way: funcPtrArray[0] = // this compiles reinterpret_cast<FuncPtr>(&doSomething);Casting function pointers is not portable (C++ offers no guarantee that all function pointers are represented the same way), and in some cases such casts yield incorrect results (see Item 31), so you should avoid casting function pointers unless your back's to the wall and a knife's at your throat. A sharp knife. A very sharp knife.If your compilers lack support for the new casting forms, you can use traditional casts in place of static_cast, const_cast, and reinterpret_cast. Furthermore, you can use macros to approximate the new syntax: #define static_cast(TYPE,EXPR) ((TYPE)(EXPR))#define const_cast(TYPE,EXPR) ((TYPE)(EXPR))#define reinterpret_cast(TYPE,EXPR) ((TYPE)(EXPR))You'd use the approximations like this: double result = static_cast(double, firstNumber)/secondNumber;update(const_cast(SpecialWidget*, &sw));funcPtrArray[0] = reinterpret_cast(FuncPtr, &doSomething);These approximations won't be as safe as the real things, of course, but they will simplify the process of upgrading your code when your compilers support the new casts.There is no easy way to emulate the behavior of a dynamic_cast, but many libraries provide functions to perform safe inheritance-based casts for you. If you lack such functions and you must perform this type of cast, you can fall back on C-style casts for those, too, but then you forego the ability to tell if the casts fail. Needless to say, you can define a macro to look like dynamic_cast, just as you can for the other casts: #define dynamic_cast(TYPE,EXPR) (TYPE)(EXPR)Remember that this approximation is not performing a true dynamic_cast; there is no way to tell if the cast fails.I know, I know, the new casts are ugly and hard to type. If you find them too unpleasant to look at, take solace in the knowledge that C-style casts continue to be valid. However, what the new casts lack in beauty they make up for in precision of meaning and easy recognizability. Programs that use the new casts are easier to parse (both for humans and for tools), and they allow compilers to diagnose casting errors that would otherwise go undetected. These are powerful arguments for abandoning C-style casts, and there may also be a third: perhaps making casts ugly and hard to type is a good thing. Back to Item 2: Prefer C++-style castsContinue to Item 4: Avoid gratuitous default constructorsItem 3: Never treat arrays polymorphically.One of the most important features of inheritance is that you can manipulate derived class objects through pointers and references to base class objects. Such pointers and references are said to behave polymorphically as if they had multiple types. C++ also allows you to manipulate arrays of derived class objects through base class pointers and references. This is no feature at all, because it almost never works the way you want it to.For example, suppose you have a class BST (for binary search tree objects) and a second class, BalancedBST, that inherits from BST: class BST { ... };class BalancedBST: public BST { ... };In a real program such classes would be templates, but that's unimportant here, and adding all the template syntax just makes things harder to read. For this discussion, we'll assume BST and BalancedBST objects contain only ints.Consider a function to print out the contents of each BST in an array of BSTs: void printBSTArray(ostream& s, const BST array[], int numElements){ for (int i = 0; i < numElements; ++i) { s << array[i]; // this assumes an } // operator<< is defined} // for BST objectsThis will work fine when you pass it an array of BST objects: BST BSTArray[10];...printBSTArray(cout, BSTArray, 10); // works fineConsider, however, what happens when you pass printBSTArray an array of BalancedBST objects: BalancedBST bBSTArray[10];...printBSTArray(cout, bBSTArray, 10); // works fine?Your compilers will accept this function call without complaint, but look again at the loop for which they must generate code: for (int i = 0; i < numElements; ++i) { s << array[i];}Now, array[i] is really just shorthand for an expression involving pointer arithmetic: it stands for *(array+i). We know that array is a pointer to the beginning of the array, but how far away from the memory location pointed to by array is the memory location pointed to by array+i? The distance between them is i*sizeof(an object in the array), because there are i objects between array[0] and array[i]. In order for compilers to emit code that walks through the array correctly, they must be able to determine the size of the objects in the array. This is easy for them to do. The parameter array is declared to be of type array-of-BST, so each element of the array must be a BST, and the distance between array and array+i must be i*sizeof(BST).At least that's how your compilers look at it. But if you've passed an array of BalancedBST objects to printBSTArray, your compilers are probably wrong. In that case, they'd assume each object in the array is the size of a BST, but each object would actually be the size of a BalancedBST. Derived classes usually have more data members than their base classes, so derived class objects are usually larger than base class objects. We thus expect a BalancedBST object to be larger than a BST object. If it is, the pointer arithmetic generated for printBSTArray will be wrong for arrays of BalancedBST objects, and there's no telling what will happen when printBSTArray is invoked on a BalancedBST array. Whatever does happen, it's a good bet it won't be pleasant.The problem pops up in a different guise if you try to delete an array of derived class objects through a base class pointer. Here's one way you might innocently attempt to do it: // delete an array, but first log a message about its// deletionvoid deleteArray(ostream& logStream, BST array[]){ logStream << "Deleting array at address " << static_cast<void*>(array) << '\n';delete [] array;}BalancedBST *balTreeArray = // create a BalancedBST new BalancedBST[50]; // array...deleteArray(cout, balTreeArray); // log its deletionYou can't see it, but there's pointer arithmetic going on here, too. When an array is deleted, a destructor for each element of the array must be called (see Item 8). When compilers see the statement delete [] array;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -