Scott Meyers
Modification History and Errata List for More Effective C++
Last Updated October 18, 2012
by Scott Meyers
What follows are my notes on what I've changed in More Effective C++ since its original publication (i.e., since its first printing) and what I believe may need to be changed in future printings. Most of the changes (or prospective changes) are cosmetic and don't affect the technical content of the book. To distinguish those modifications that do affect technical material, I precede those entries with an exclamation mark ("!") and display them in red.
Each entry includes the following information:
- Date Reported: The date the problem was brought to my attention.
- Who: The initials of the person reporting the problem. The initials "sdm" refer to me. The mapping from other initials to full names is at the bottom of this document.
- Pages: The pages of the book (or the Item number) affected.
- What: A description of the problem and the solution.
- Date Fixed: The date on which I fixed the problem. If no date is shown, it means I haven't gotten around to fixing the bug or I'm still mulling over whether I want to fix it. Sometimes I change my mind on bug reports, so prospective bugs often spend quite a while in the "not yet fixed" state. When Addison-Wesley notifies me that a new printing is about to take place, I go through the bugs and fix all those I've decided must be fixed. It's thus not uncommon for all the bugs fixed for a particular printing to have the same fix date.
The easiest way to use this list is to find out which of the book's printings you have (it's listed on the copyright page near the bottom), then go to the appropriate section of the list and read from that point forward. For example, if you have a copy of the second printing, the changes made to the first printing won't apply to you, because the changes will already be in place in your copy of the book.
I am always interested in bug reports and suggestions for improvements to More Effective C++. To submit comments, send email to mec++@aristeia.com or write to the address listed on page 8 of the book's Introduction.
To be assured of always having the most accurate version of More Effective C++ at your fingertips, I encourage you to buy a copy of each printing :-).
The following changes were made for the second printing. You need to worry about these changes only if you have a copy of the first printing of the book:
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 2/ 7/96 msf 14 The comment in the call to updateViaRef should 2/20/96 say the call is to that function, not to update. 2/21/96 ml 31 " ... attempting to perform the assignment." --> 2/21/96 " ... attempting to perform the comparison." 1/28/96 jep 40 The placement new example is in Item 4, not 2/20/96 Item 3. 2/12/96 wg 44 In 2nd paragraph, "reliability" --> "reliably" 2/20/96 2/23/96 lh 70 Declaration of Validation_error::what fails to 2/25/96 put a space after the "*" in the return type. 1/28/96 jep 76 "You can set it up IT like this." 2/20/96 2/ 5/96 jep 111 Replace postfix ++/-- with prefix ++/-- for 2/20/96 189 stylistic consistency (in accord with Item 6). 191 193-4 225 2/ 5/96 tu 113 The text says a vtbl is an array of pointers to 2/20/96 non-member functions, but the diagrams clearly show that the pointers ARE to member functions. Find a way to rephrase this to make it true. 2/ 5/96 jep 117 End of number 2: This cost of this step --> The 2/20/96 cost of this step . ! 2/ 3/96 sdm 120 The figures should show A having a vptr and D 2/20/96 242 having none. See ARM pp. 234-5. 2/ 5/96 tu 127 The formal parameter in the declaration of the 2/20/96 copy constructor should be "rhs". ! 2/11/96 ds 146 UPNumber::destroy should be a const member 2/20/96 function. Otherwise it is not possible to delete a pointer-to-const. 2/21/96 clt 146 The comment pointing out that declaration of a 2/25/96 UPNumber is legal, but its destruction is illegal should be clarified a bit. 1/14/96 sdm 163 Sentence ends with a comma instead of a period 2/20/96 in the second line of text on the page. ! 2/ 5/96 jep 188 value->++refCount --> ++value->refCount. 2/20/96 2/ 3/96 sdm 191-2 Various problems with figures: arrow heads or 2/20/96 242 tails fail to properly abut ovals; boxes fail 185 to abut properly. 114 117 120-1 187-8 248 266 ! 1/25/96 lsk 207 The call to removeReference in String::operator[] 2/20/96 should not be there: the call to RCPtr::operator= will make the necessary call to adjust the reference count. 2/ 5/96 sdm 273 Modify the typesetting of "__cplusplus" so it's 2/20/96 295 clear there are two leading underscores. 1/28/96 jep 280 "And of THE course the STL consists" 2/20/96
The following changes were made for the third printing. You need to worry about these changes only if you have a copy of the first two printings of the book:
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- ! 3/21/96 sdm 163 In March 1996, the committee changed the auto_ptr 4/30/96 165 interface so that, among other things, the copy 166 ctor and assignment operators now take const 291 parameters. I added footnotes generally noting the changes and referred readers to updated auto_ptr information at the MEC++ WWW and FTP sites. 4/26/96 psrc 177 In sixth line from bottom, "SmartPtr" is in wrong 4/29/96 font. 4/11/96 sdm 181 Improper alignment of comment near the bottom of 4/29/96 the page. 4/30/96 avk 187 In 3rd paragraph, the text implies that the 4/30/96 String ctor sets the reference count to 1, but that is actually done by the StringValue ctor. Reword. ! 3/19/96 sdm 209 RCIPtr<T>::init refers to counter->pointee 4/30/96 before it is initialized. Change the second if statement to: if (counter->isShareable() == false) { T *oldValue = counter->pointee; counter = new CountHolder; counter->pointee = new T(*oldValue); } ! 3/ 4/96 we 209 RCWidget fails to perform a copy-on-write (COW) 4/30/96 210 when non-const member functions are invoked. This is incorrect: each non-const member function might modify the (shared) Widget the RCWidget points to. To rectify the problem, add an overloaded non-const version of operator-> and operator* to the RCIPtr template, and have these functions perform the COW before returning. This bends the notion of constness, because we're using non-const operators -> and * even though there is no conceptual change in what the smart pointer points to, but that's preferable to having incorrect behavior. (Note: This change is later revoked. See the explanation below for why.) ! 3/21/96 jj 222-223 The calls to removeReference in the two 4/30/96 String::operator= functions should not be there: the calls to RCPtr::operator= will make the necessary calls to adjust the reference counts. 3/ 6/96 lh 237 There is no such thing as a "static member" of a 4/29/96 function. Reword the second-to-last paragraph. ! 3/ 5/96 sk 264 The destructor in AbstractAnimal should be 4/30/96 public, not protected. ! 3/31/96 rcn 293 auto_ptr's reset function should make sure that 4/30/96 p and pointee are different before doing anything, otherwise pointee will end up pointing to a deleted object. (In practice, this is a moot point, because the revised auto_ptr defintion (see point above) contains no "reset" member function.)
The following changes were made for the fourth printing. You need to worry about these changes only if you have a copy of the first three printings of the book:
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 8/31/96 mb 13 Change "cannot change the constness of an 9/26/96 expression" to "cannot remove the constness from an expression". 3/18/96 aid 34 Many compilers fail to enforce the restriction 4/29/96 that const objects returned from functions may invoke only const member functions. Add a footnote noting common non-compliance with the standard. 1/30/96 sdm 45,66 All figures with curves look bad: the curves 9/26/96 117,119 are clearly being approximated by line segments. 124,150 151,173 178,181 184,185 187,188 191,192 203,208 229,248 258,264 266 ! 4/ 9/96 ks 57 If initAudioClip gets an exception from the call 4/30/96 to new AudioClip, it will call cleanup in the try block, and cleanup will delete theAudioClip. But theAudioClip will not yet have been initialized. Instead of calling cleanup in the catch block, manually delete theImage. 7/29/96 sm 64 Change "that" to "that that" at the beginning of 9/26/96 the third line up from the parenthetical paragraph. 2/18/96 clt 67 Paragraph beginning with "Furthermore, 9/26/96 conversions from typed to untyped pointers..." needs a new beginning to clarify that this is the second of the two types of conversions signposted by the last paragraph on page 66. The next paragraph (beginning with "Even here, however") marks the beginning of the final difference between parameter passing and exception throwing, and it needs a clearer introduction, too. 9/26/96 sdm 67 Modify the catch clause taking a void* to take a 9/26/96 const void*. Otherwise it wouldn't be possible to catch an exception of type const T*. 3/ 6/96 jm 70 exception::what has an exception spec, but that 4/29/96 notion isn't introduced until Item 14. Add an xref to the comment so readers who don't know about exception specifications will understand. 5/30/96 sb 74 In the third paragraph, I refer to "two cases" 9/26/96 that are easy to forget. There is no second case. Reword. 5/30/96 sb 90 Wrong font for initial "t" in the pseudocode, 9/26/96 "the appropriate data from the database" 1/28/96 jep 107 Some compilers don't create a temporary in this 4/29/96 109 expression: Rational(lhs) Instead, they (incorrectly) cast away the constness of lhs and return a reference to that! Advise readers to test their compilers before using this technique. ! 7/19/96 sdm 109 In July 1996, the standardization committee 9/26/96 decided that named objects may be treated essentially the same as unnamed objects for purposes of performing the return value optimization. I added a footnote to this effect. 7/ 9/96 ahd 111 There is no need to #include <math.h> for this 9/26/96 program. Also, the logarithms are natural, not base 10. ! 7/23/96 um 115 The statement that abstract classes generally 9/26/96 have no vtbl is incorrect. A vtbl is needed for such classes in case virtual functions are called during construction/destruction of instances of such classes. 4/16/96 tjb 129 In second line, the closing parenthesis is in the 4/29/96 wrong font. ! 5/15/96 sdm 134 Add footnote: 9/26/96 In July 1996, the ISO/ANSI standardization committee changed the default linkage of inline functions to external, so the problem I describe here has been eliminated, at least on paper. Your compilers may not yet be in accord with the emerging standard, however, so your best bet is still to shy away from inline functions with static data. 3/ 6/96 lh 155 The comment in isOnHeap refers to "the memory 4/29/96 occupied by ptr." It should refer to "the memory occupied by *this." 4/10/96 tjb 160 In comment: "defererence" --> "dereference" 4/29/96 5/20/96 sdm 166 Should explicitly mention that sometimes a smart 9/26/96 pointer does NOT delete what it points to in its destructor, otherwise people may think that ALL smart pointers do that. 7/16/96 sdm 169 Replace "iff" in the comment with "if and only 9/26/96 if." Some programmers were unfamiliar with what I thought was a common abbreviation. 9/ 7/96 sdm 178 In first paragraph, T1 and T2 should be T1* and 9/26/96 T2*, respectively. 7/ 5/96 os 180 The text in the middle of the page talks about 9/26/96 assigning non-consts to consts, but the examples show initializations. Modify the text to refer to both initializations and assignments. 5/11/96 en 191 In comment, "refcount" --> "refCount" 9/26/96 7/ 5/96 os 209 The test of counter against 0 at the top of 9/26/96 210 RCIPtr::init is unnecessary, because counter can never be null. Eliminate the test. Ditto for the test of counter inside the RCIPtr destructor and assignment operator. 7/19/96 os 254 I refer to RTTI-based "switch-on-type" 9/26/96 statements, but it's not legal to switch on the return value of a call to typeid. Reword. ! 7/31/96 sdm 276 The statement at the beginning of the second 9/26/96 paragraph that C and C++ have different layout rules for structs is incorrect. The rules are the same. ! 7/20/96 sdm 292 The auto_ptr class definition needs to add this: 9/26/96 293 294 template<class U> friend class auto_ptr<U> This makes auto_ptr<T> a friend of auto_ptr<U> for all T and U, i.e., all auto_ptr classes are friends of one another. This is necessary to allow auto_ptr<T> to access the dumb pointer inside auto_ptr<U>, which it does inside the bodies of its member function templates for copying and assignment.
The following changes were made for the sixth printing. You need to worry about these changes only if you have a copy of the first five printings of the book:
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 11/15/97 sdm xiv Moved some text from the bottom of page xiv to 11/15/97 xv the top of page xv to make more room on xiv for the names of people who've reported bugs. 8/21/97 sdm 8 Change email contact address to 11/14/97 mec++@awl.com. ! 5/29/96 js 13 sw is defined as a const object, and the results 11/14/97 of modifying a truly const object -- even after applying a const_cast -- are undefined. I modified the example to eliminate the problem. 11/ 3/97 mm 14 In the declaration of updateViaRef, the 11/14/97 ampersand should be next to the type name(not the parameter name) in order to be consistent with the style I use throughout the book. !11/11/96 ymk 15 The new cast approximations need an extra set 11/14/97 of parens. They should be as follows: #define static_cast(TYPE,EXPR) ((TYPE)(EXPR)) Otherwise casts like this don't work correctly: static_cast(int*, my_vec)[j] 4/18/97 drk 19 It's not true that all legal workers in the USA 11/14/97 must have a Social Security Number. I removed the example. 4/22/97 sdm 20 Define bestPieces example like this: 11/14/97 EquipmentPiece bestPieces[] = { EquipmentPiece(ID1), EquipmentPiece(ID2), ... EquipmentPiece(ID10) }; This makes it easier for readers to figure out how it generalizes to constructors that take multiple arguments. 1/15/97 ras 36 The comma operator occurs more often than I 11/14/97 suggest, but the most common place to see it is still probably within a for loop. I reworded the paragraph. !12/25/96 dmp 43 In the first full paragraph on the page, I say 11/14/97 there is "only one global operator new." This isn't true. There are actually several forms of operator new at global scope, but only one has the "normal" form, i.e., takes exactly one argument of type size_t. I reworded the paragraph to eliminate the inaccuracy. 1/17/97 dh 59 In the first paragraph, change "if an exception 11/14/97 is thrown while another is active" to "if control leaves a destructor due to an exception while another exception is active". It is legal for an exception to be thrown while you're in the catch clause of an active exception. 3/26/97 sdm 66 Update diagram to include new underflow_error 11/14/97 class that was added to the standard. ! 4/15/96 jr 75 The C++ standard inexplicably forbids 11/14/97 dcs exception specifications in typedefs, so CallBackPtr cannot be a typedef here, at least not portably. As it turns out, both compilers I tested accepted the typedef anyay. I added a footnote explaining the situation and suggesting that portable code would have to use a macro instead of a typedef. 1/31/97 gm 113 "Each class in a program has its own vtbl" --> 11/14/97 "Each class in a program that declares or inherits virtual functions has its own vtbl" 4/ 4/97 sdm 114 Line too thick in lower figure. 11/14/97 4/ 4/97 sdm 115 The first sentence could be misleading: the size 11/14/97 of a class's vtbl is proportional to the total number of virtual functions declared for a class, including those declared in base classes. Note that it's based on DECLARATIONS, not definitions. I reworded things. 4/10/97 sdm 115 The vtbl is often put in the object file 11/14/97 containing the definition of the first non-inline *non-pure* virtual function. 4/ 4/97 dab 115 The vtbl for C1 would be placed in the object 11/14/97 file containing the definition of C1::~C1, not C1::f1. 10/27/97 sdm 118 The virtual call really translates into this, 11/14/97 (*pC1->vptr[i](pC1); because each member function has the "this" pointer passed in from the call site. ! 1/ 4/97 ih 119 In my discussion of virtual base classes, I 11/14/97 120 claim that virtual base pointers must be stored inside objects. This is not true. Some implementations put them inside class vtbls. I reworded things to make clearer that the implementation I show in the book is not the only implementation in use. 6/19/97 lw 167 editTuple's parameter should be passed by 11/14/97 reference, just like it is on page 161. 10/ 3/97 jl 200 There is no need for RCPtr<StringValue> to be a 11/14/97 201 friend of String. The entire first prose 204 paragraph on page 200 is incorrect. (I don't know what I was thinking when I wrote it.) I eliminated the friend declarations wherever they occured, removed the offending paragraph, and moved some text from page 201 to page 200 to avoid having page 200 be too gruesomely empty. 5/12/97 rxc 204 Make RCObject's destructor pure virtual to match 11/14/97 the initial declaration on page 194. 1/15/97 ras 236 The behavior of SpaceShip::lookup does not depend 11/15/97 238 on any SpaceShip data members, so it should be a 239 static member function. I also removed the const 240 from the function, because only non-static member functions may be const. 11/14/97 sdm 240 In first sentence, "we probably have to pay" --> 11/15/97 "we may have to pay". Some compilers are likely to apply the return value optimization (see Item 20) to avoid copying the map. 11/14/97 sdm 242 In first sentence, "likely object layout" --> 11/15/97 "possible object layout". See comment above for changes to pages 119-120. 10/15/97 clf 270 In 1st paragraph, "forego" --> "forgo" 11/14/97 9/24/97 sdm 286 I updated the book information to refer to the 11/14/97 289 latest editions of The C++ Programming Language and Effective C++ and to mention the CD version of Design Patterns. 11/14/97 sdm 289 I removed the footnote saying I'm a columnist 11/14/97 for C++ Report, as my last column appeared there at the end of 1996.
The following changes were made for the seventh printing. You need to worry about these changes only if you have a copy of the first six printings of the book:
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 1/??/98 jww vi The date for the sixth printing was listed as 11/??/98 December 1998, but that printing was actually made in December 1997. The date for the seventh printing is correctly listed as November 1998. So, no, the sixth printing is not more recent than the seventh printing! 10/15/97 clf ix,x, For consistency with Effective C++, I should add 6/10/98 9,12, a period to the end of the title for each Item. 16,19, 24,31, 35,38, 45,50, 58,61, 68,72, 78,82, 85,93, 98,101, 105,107, 110,113, 123,130, 145,159, 183,213, 228,252, 258,270, 277 6/22/98 sdm xiii The C User's Journal is now the 6/27/98 C/C++ Users Journal. 6/22/98 sdm xiv Borland is now Inprise. 6/27/98 12/29/97 sdm xiv Updated acknowledgements to include most recent 12/29/97 xv bug reporters. (This modified a page break.) 12/ 3/97 sdm xiv ANSI/ISO --> ISO/ANSI. 12/29/97 2 59 96 109 134 256 277 285 12/ 4/97 sdm 1 On 11/14/97, the ISO and ANSI standardization 12/29/97 2 committees adopted what can be referred to as an 134 official standard for C++, so all references to 277 "draft standard," "nascent standard," "DIS," 278 etc. should refer to just "the standard." 279 285 6/25/98 sdm 2 In second line, "the accumulated wisdom" ==> 6/27/98 "accumulated wisdom" 6/25/98 sdm 3 In second paragraph, "i.e." ==> "e.g." 6/27/98 6/ 7/98 sdm 4 27 "string class" ==> "string type". As I explain 6/ 7/98 85 on page 279, string is not a class, it's a typedef 183 for an instantiation of the basic_string template. 192 217 256 278 279 280 12/30/97 sdm 4 Replaced HP's STL FTP site with SGI's STL WWW 12/30/97 302 site. WWW is more convenient for many people, and the SGI software is more up-to-date. I had to eliminate two index entries when I did this. 6/ 7/98 sdm 5 The third paragraph states that I use different 6/ 7/98 notations for inheritance in this book and in Effective C++, but that's true only for the first edition of EC++. I added clarifying text. 12/29/97 sdm 8 www.aw.com --> www.awl.com; 12/29/97 287 ftp.aw.com --> ftp.awl.com 6/25/98 sdm 11 At end of second-to-last paragraph, note that 6/27/98 Item 30 describes an interesting exception to the rule that operator[] should return a reference. 6/25/98 sdm 13,52, Bad line breaks. 6/27/98 58,76, 265 6/25/98 sdm 14 In second-to-last paragraph, "almost certainly 6/27/98 want a static_cast" ==> "probaby want a static_cast". 11/14/97 sdm 14,34, Footnote symbols are followed by a period. The 12/29/97 48,59, periods shouldn't be there (and they're not in 75,96, EC++/2E). 109 134 166 291 5/ 5/98 rxb 21 In 6th line on page, "treated as a" ==> 6/ 3/98 "treated as an" 12/30/97 sdm 40 Replace or supplement header names ending in .h 12/30/97 65 with new extensionless header names. 111 12/14/97 sdm 42 "different ... than" --> "different ... from". 12/29/97 223 12/29/97 sdm 48 Added to the parenthetical comment about using 12/29/97 auto_ptr-like classes for arrays the suggestion that using a vector in such cases might well be better. !11/20/97 kga 48 The auto_ptr interface in the book and at the web 12/27/97 sdm 163 site doesn't show the most recent interface, 165 which is nearly identical to the interface I 166 described in the original printing of MEC++. 291 Update book and site to be in accord with the interface approved at the 11/97 ISO/ANSI meeting. This requires removing the auto_ptr-related footnotes I noted above on 3/21/96. 6/26/98 sdm 52 In the first paragraph, it's misleading to refer 6/27/98 to arguments that are "non-null". The arguments are references, and as Item 1 explains, references can't be null. "non-null" ==> "non-empty strings". 12/29/97 sdm 59 Modified footnote wording in light of greater 12/29/97 96 compiler support for features mentioned in the footnotes. 1/ 1/98 sdm 99 unsigned int ==> size_t 6/ 3/98 135 139-140 ! 2/ 6/98 sdm 101 As noted in the footnote on page 109 (added on 6/ 6/98 104 9/26/96), and contrary to the text on these pages, 109 compilers may now apply the same optimizations to 110 named objects that they've always been able to apply to unnamed objects. I reworded things and/or added clarifying footnotes, and one result was that the information formerly in the footnote on page 109 ended up in a new footnote on page 104. 2/ 6/98 sdm 118 There are now more approaches to implementing 6/ 7/98 119 virtual base classes than there used to be, and 120 some of the alternative approaches incur a smaller 121 space overhead. For details, consult pp. 95-101 of Stan Lippman's "Inside the C++ Object Model" (Addison-Wesley, 1996). I modified the text on these pages (including the table on page 121) to make it clear that the implementation of virtual bases I describe in the book is not the only implementation in use. 6/26/98 sdm 140 Added comment "// see below" to end of line 6/27/98 declaring maxObjects with an initial value. 6/ 6/98 sdm 140 For consistency with numObjects, type of 6/ 6/98 141 maxObjects should be size_t, not int. 142 145 12/29/97 sdm 141 "many compilers" ==> "some compilers". The 12/29/97 262 state of compilers has improved since I originally wrote the book in 1995. 2/24/98 cxm 142 maxObjects should be const. 6/ 6/98 145 2/24/98 cxm 144 In last line on page, "Counter<Printer>" should 6/ 6/98 be "Counted<Printer>". 6/26/98 sdm 161 In comment above LogEntry definition, 6/27/98 "template class" ==> "class template" 12/29/97 sdm 165 "transferral" ==> "transferal" 12/29/97 6/26/98 sdm 166 In last paragraph, mention that returning an 6/27/98 object instead of a reference leads to the slicing problem, and provide an xref to Item 13. 4/ 6/96 edw 190 The const version of operator[] should return a 6/ 6/98 2/ 6/98 sdm 204 const char& instead of a char. Otherwise it's 207 not possible to do this: 218 String s; ... const char *p = &s[2]; This is also important for efficiency when char is generalized to an arbitrary type. 2/ 6/98 sdm 192 The standard string type adopts a combination of 6/ 7/98 193 approaches 2 and 3: the reference returned from the non-const operator[] is guaranteed to be valid only until the next operation that might modify the string. I added a footnote to this effect, and this changed the page break. 2/ 6/98 sdm 199 Comment at beginning of RCPtr template isn't 6/ 6/98 quite true. T must support the RCObject interface, but it need not inherit from RCObject. This is explictly mentioned on page 201. 2/ 6/98 sdm 208 Bad break in "CountHolder" in last prose 6/ 6/98 paragraph. 2/ 6/98 sdm 220 Near the end of third line from bottom, insert 6/ 6/98 "so" between "CharProxy," and "the". ! 2/19/98 dxg 224 In String::CharProxy::operator&, there should be 6/ 6/98 no call to removeReference, because the assignment statement on the next line will automatically take care of it. (See also the bug report above of 3/21/96 for pages 222-223.) 2/ 6/98 sdm 226 "non-const references" ==> "references to 6/ 6/98 non-const objects" 6/26/98 sdm 228-29 "a direction and a velocity" ==> "a velocity". 6/27/98 More than one person has sternly pointed out to me that velocity is a vector quantity, hence always includes a direction. (This changed a page break.) 2/ 6/98 sdm 230 "seat-belts" ==> "seat belts" 6/ 6/98 ! 2/ 6/98 sdm 238 According to the standard, the value returned 6/ 6/98 from type_info::name is implementation-defined. In practice, different implementations do different things. I added a footnote clarifying the situation. (See the "Interesting comment" below about Item 31 for more information on this topic.) 6/ 4/98 sdm 280 Last line on page isn't correctly justified. 6/ 5/98
The following changes were made for the eighth printing. You need to worry about these changes only if you have a copy of the first seven printings of the book:
DATE DATE
REPORTED WHO PAGES WHAT FIXED
-------- --- ----- ------------------------------------------------ --------
11/14/98 bwk xi Note that it's the first edition of Hennessy's 5/ 5/99
and Patterson's book that was published in 1990.
A second edition was published in 1996.
xiv Added new bug reporters to acknowledgements. 5/ 5/99
4/ 7/99 jsh 14 The comment in the code attempting to do a 5/ 5/99
dynamic_cast to a double is misleading. The
problem is not that inheritance isn't involved,
it's that the type being cast (firstNumber)
doesn't have any virtual functions. Even if
inheritance was present, if the type being cast
lacked virtual functions, the code still
wouldn't compile.
3/10/99 mr 66 The ellipses after the "throw value" can never 5/ 5/99
be reached. The "..." should go after the "}",
not before it.
4/ 6/99 rhs 96 The footnote at the bottom of the page states 5/ 5/99
that all STL iterators must support operator->,
but that's not quite true: output iterators need
not support it. In the case of the example in
the book, map::find returns a bidirectional
iterator, not an output iterator, so the
footnote is accurate for the example. I modified
the footnote to say that *most* STL iterators
are required to support operator->.
! 4/21/99 ea 149 At the bottom of the page, steps 3 and 4 should 5/ 5/99
be reversed. The second object is a parameter
for the first object's constructor, so the
second object must be constructed before the
first one is.
12/ 8/98 cwg 250 Disagreement in number in the second 5/ 5/99
parenthetical remark in the paragraph at the top
of the page: "effect" ... "are". Ugh.
The following changes were made for the ninth printing. You need to worry about these changes only if you have a copy of the first eight printings of the book:
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- xiv Added new bug reporters to acknowledgements. 10/ 4/99 10/ 3/99 sdm 8 Added mention of my mailing list, which, among 10/ 4/99 other things, announces each time this errata list is updated. ! 7/ 2/99 sdm 21 In the loop in the code at the top of the page, 10/ 4/99 the statement new (&bestPieces[i]) EquipmentPiece (ID Number); should be new (bestPieces+i) EquipmentPiece (ID Number); The form using "&bestPieces[i]" has two problems. Strictly speaking, it yields undefined results, because the bestPieces[i] object hasn't yet been constructed, so referring to it is undefined. (In practice, this is unlikely to be a problem, as any decent compiler will look only at its address.) Second, if EquipmentPiece has defined its own operator&, "&bestPieces[i]" might not do what you expect. Using the "bestPieces+i" form avoids both problems. 6/24/99 pab 56 The functions initImage and initAudioClip should 10/ 4/99 be static member functions, because (1) they need no "this" pointer and (2) they are called before the data members of the object have been initialized. 6/24/99 ea 149 In the discussion that follows this code, 10/ 4/99 new UPNumber(*new UPNumber) I refer to the "first object" and the "second object", but I fail to make clear which is which. The "first object" is the leftmost one above, and the "second object" is the one passed to the constructor of the first.
The following changes were made for the tenth printing. You need to worry about these changes only if you have a copy of the first nine printings of the book:
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 4/10/00 km 51 In the first paragraph, the cross reference to 6/ 4/00 Item 3 should be to Item 4. ! 1/ 1/00 em 56 Contrary to the bug report from pab of 6/24/99 6/ 4/00 that I implemented on 10/4/99, initImage and initAudioClip should NOT be static, because initAudioClip uses delete on theImage, and theImage is a nonstatic data member. (Strictly speaking, initImage could be static, but it's better to declare the two init functions similarly. Better still is to follow the ultimate advice of this Item and use auto_ptr data members instead of the approach based on init functions.) 11/ 2/99 fk 76 In line 20, missing space between "results" and 6/ 4/00 "in". This error seems to have been introduced in the seventh printing. ! 9/16/98 ct 202 Suppose we have RCPtrs to linked list nodes, 6/ 4/00 206 i.e., RCPtr<LLNode> objects. Consider: RCPtr<LLNode> p; ... p = p->next; This invokes RCPtr::operator=, where the parameter rhs is bound (by reference) to p->next. Now consider this statement inside RCPtr::operator=: if (pointee) pointee->removeReference(); If removeReference() decrements the reference count to 0, the LLNode pointed to by pointee will be deleted. But that allows an implementation to trash the memory inside the deleted object, including its "next" field -- which is what rhs is currently bound to! Here's a fix: template<class T> RCPtr<T>& RCPtr<T>::operator=(const RCPtr& rhs) { if (pointee != rhs.pointee) { T *oldPointee = pointee; pointee = rhs.pointee; init(); if (oldPointee) oldPointee->removeReference(); } return *this; } ! 2/19/00 sdm 279 basic_string declaration near bottom of page is 6/ 4/00 incorrect. It should be template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> > class basic_string; 5/28/00 sdm 316 RCIPtr::makeCopy is missing from the index. 6/ 4/00
The following changes were made for the twelfth printing. You need to worry about these changes only if you have a copy of the first eleven printings of the book:
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 6/ 5/00 sdm xiv Remove js2 from the acknowledgements, because his 9/13/01 bug report was rejected at the very last minute when making revisions for the tenth printing. 3/30/01 sdm 2 Material in the book is based on the final C++ 9/13/01 Standard, not the DIS. ! 6/24/00 nxd 16 Macro expansion for dynamic_cast should have 9/13/01 parens around "(TYPE)(EXPR)", just like it does for the other casting macros on page 15. 9/ 6/00 bp 33 In accord with my advice in Item 21 of 9/13/01 Effective C++ to "use const whenever possible," oldValue should be declared const in the code example at the top of the page. 2/21/01 wds 58 In second paragraph, "initializationof" ==> 9/13/01 "initialization of". ! 8/15/00 wcm 142 For consistency with the changes I made on 9/13/01 144 6/6/98 (see above), the Counted template should have objectCount return a size_t instead of an int, and numObjects should be of type size_t, not int. 11/ 2/99 fk 152 The last sentence of paragraph 2 begins with "As 9/13/01 such, ...". "As such" isn't really correct here. Reword. 7/21/00 sdm 177 Italicized text on this page has jaggies. 9/13/01 Regenerate PDF and reprint.
The following changes were made for the thirteenth printing. You need to worry about these changes only if you have a copy of the first twelve printings of the book:
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 1/25/02 csp 4 In last line, 7/12/02 http://www.sgi.com/Technology/STL/ ==> http://www.sgi.com/tech/stl/ 12/20/01 pd 11 Reword final paragraph to indicate that 7/12/02 references are preferable to pointers when there will always be an object to refer to AND you'll always want to refer to the same object OR you're implementing an operator whose syntactic requirements disallow pointers 1/ 8/02 sdm 52 Bad line break: "theAudi-oClip" ==> 7/12/02 "theAudio-Clip" 11/ 2/99 fk 54 In the first prose paragraph, I remark that "As 7/12/02 fully constructed objects, these data members will be automatically destroyed when the BookEntry object containing them is..." This is slightly misleading. Because an exception is thrown, no BookEntry object will be created, hence none will be destroyed. Rather, the fully constructed BookEntry data members will be automatically destroyed as C++ backs out of construction of the object it had been attempting to construct. The important point is this: because the data members of the would-be BookEntry object have been fully constructed, C++ must see to it that they are also destructed. 10/ 1/01 jfn 56 In the last sentence before the code example at 7/12/02 the bottom of the page, "private static member function" ==> "private member function". This is what I hope is the final undo for the incorrect "correction" I added on 10/4/99; consult em's correction reported on 1/1/00 for details. !12/ 5/01 at 62-63 Contrary to what I state on these pages, Section 7/12/02 15.1/5 of the Standard makes clear that implementations may optimize away the creation of exception object under some conditions. However, my remark that a catch block couldn't modify localWidget continues to be true, as does my observation that throwing an exception is typically much slower than passing a parameter. 1/ 8/02 sdm 76 Bad line break: "conver-tUnexpected" ==> 7/12/02 "convert-Unexpected" 12/ 6/02 ddg 99 In last sentence "When countChar returns," ==> 7/12/02 "When the statement containing the call to countChar finishes executing," 3/12/01 wds 120 Bad hyphenation in 2nd-to-last para: "runt-ime" 7/12/02 ==> "run-time". 3/21/00 sdm 152 The information at the "Comments on Item 27" web 7/12/02 site leads me to conclude that the second paragraph on this page takes too hard a line. Reword to soften. 1/19/00 ccr 186,193, Reference counts should be unsigned (i.e., 7/14/02 195,204 size_t), not int. I also updated the on-line code for Item 29. 1/19/00 ccr 204 The RCObject constructors and destructor are 7/14/02 declared protected here, but earlier in the book they were public. I made them public again, both in the book and in the on-line source code. 6/ 4/02 sdm 208 Bad line break in last prose paragraph: 7/12/02 "Coun-tHolder" ==> "Count-Holder". (An entry above for 2/6/98 says this was fixed on 6/6/98, but apparently that fix got lost somewhere between my computer and the printer.) ! 7/31/00 cw 209-210 cw notes that RCIPtr needs a way to make the 7/15/02 pointed-to object unshareable. I note that RCIPtr needs a way to find out whether the pointed-to object is being shared. I edited the book to add a getRCObject member function, but that solution's primary attraction is that it's easy to implement. If I were designing RCIPtr from scratch, I might choose a different tack. ! 3/22/01 wcm 209 RCPtr and RCIPtr behave inconsistently with 7/15/02 210 respect to automatically performing COW on 316 pointee objects. The fundamental problem is that my fix for we's bug report of 3/4/96 above was incorrect, and I should have realized that when I had to "bend" the notion of constness to implement it. The responsibility for performing COW rests not on smart pointers but on smart pointer clients. (Note how on page 207 the non-const String::operator[] creates a new StringValue object.) In responding to we's bug report, I shouldn't have modified RCIPtr. Instead, I should have modified RCWidget::doThis to do a COW, and that's what the book does now. I also removed the modifications I originally added to address we's bug report, and I updated the Item 29 source code, too. ! 5/30/02 agb 210 makeCopy should invoke 7/14/02 counter->markUnshareable() before returning. (The need for this fix is obviated by the above-noted elimination of support for automatic COW in RCIPtr.) 9/22/01 sdm 222 Bad justification of last line on page. 7/12/02 ! 5/ 7/02 mt 238 The footnote suggests identifying a class by the 7/12/02 address of its associated type_info object, but there is no guarantee that each class has only one type_info object. In fact, I am now aware of an implementation that generates multiple type_info objects for a class. I now believe that the best way to identify classes is to wrap type_info objects inside other objects that are easier to work with. Andrei Alexandrescu has written such a wrapper class called TypeInfo, described it in his Modern C++ Design, and implemented it in his Loki library. 5/ 5/02 ar 261 I should note that dynamic_cast throws an 7/12/02 exception only because it is a cast to a reference. dynamic_casting to a pointer would return a null pointer if the cast failed.
The following changes were made for the seventeenth printing. You need to worry about these changes only if you have a copy of the first sixteen printings of the book:
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 10/ 7/03 sdm xiv Font face and size for "Davide Gennaro" is 10/ 7/03 incorrect. 8/14/02 sdm 66-67 Clarify that the inheritance-based conversions 10/ 7/03 described on these pages apply only to public inheritance. (For non-public inheritance, things get a bit more complicated. For details, including a rationale for the behavior, consult the July 2001 column by Jim Hyslop and Herb Sutter, "Baseless Exceptions.") 1/ 6/03 ais 67 In the examples near the top of the page, note 10/ 7/03 that catch-by-value and catch-by-pointer is generally a bad idea and reference Item 13, which discusses why. ! 4/30/03 mh 209 RCIPtr<T>::init should test oldValue against NULL 10/ 7/03 before dereferencing it. I also updated the on-line code for Item 29. 6/ 8/03 sdm 265 Bad line break: AbstractA-nimal ==> Abstract- 10/ 7/03 Animal ! 6/ 7/03 mh 266 2nd and 3rd paras discuss the assumption that 10/ 7/03 base classes have no data. The correct assumption is that derived classes have no data. Revise discussion.
The following changes were made for the nineteenth printing. You need to worry about these changes only if you have a copy of the first eighteen printings of the book:
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- ! 4/19/04 cc 50 The para after the first code fragment says that 7/24/04 the window will always be destroyed, even if an exception is thrown, but this this true only if the exception is caught. If it's never caught, there is no guarantee that local objects will be destroyed. ! 6/ 9/04 jw 70 In 3rd para, bad_typeid isn't thrown when a null 7/24/04 pointer is used with dynamic_cast, it's thrown when a null pointer is dereferenced in a call to typeid.
The following changes were made for the twentieth printing. You need to worry about these changes only if you have a copy of the first nineteen printings of the book:
DATE DATE
REPORTED WHO PAGES WHAT FIXED
-------- --- ----- ------------------------------------------------ --------
1/22/05 tk 54 As noted above by cc about page 50, fully 1/31/05
constructed objects will be automatically
destroyed if an exception is thrown, but this
guarantee applies to local (i.e., auto) objects
only if the exception is caught. I added a
footnote alluding to the footnote on pg. 50.
10/27/04 ms 67 In 1st para, should also mention that a catch 1/31/05
clause for runtime_error can also catch
underflow_error exceptions.
9/12/04 nxd 70 In code at bottom of page, both "what" member 1/31/05
functions should be declared const.
9/12/04 nxd 72 Once the decision to catch by reference has been 1/31/05
made, a decision must be made about whether to
catch by refernce-to-const. As always, const
should be used unless the handler needs to be
able to modify the exception object.
! 8/24/04 ms 155 HeapTracked::operator delete incorrectly throws 1/31/05
an exception if asked to delete the null pointer.
It should do nothing in that case.
The following changes were made for the 21st printing. You need to worry about these changes only if you have a copy of the first 20 printings of the book:
DATE DATE
REPORTED WHO PAGES WHAT FIXED
-------- --- ----- ------------------------------------------------ --------
5/25/05 ir 104 The footnote incorrectly stated that both forms 7/25/05
of operator*
on page 103 could generate the same
object code, but that's not true, because the top
implementation on page 103 (incorrectly) returns
a reference. I changed the footnote wording to
avoid referring to example implementations on
page 103.
The following changes were made for the 22nd printing. You need to worry about these changes only if you have a copy of the first 21 printings of the book:
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 3/ 5/06 rxy 228 At top of page, ArrayIndex ==> ArraySize. 6/ 7/06
The following changes were made for the 25th printing. You need to worry about these changes only if you have a copy of the first 24 printings of the book:
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 1/28/08 sdm xiv Borland is no longer Inprise. 1/28/08 10/24/07 sdm 8 Changed email address for book-related email to 1/28/08 mec++@aristeia.com. 1/28/08 sdm 286 Replaced reference to second edition of 1/28/08 Effective C++ with reference to third edition. 7/17/07 cxs 287 Removed parentheses around discussion of 1/28/08 Cargill's article. 1/28/08 sdm 289-90 Added footnote on pg. 289 noting that both 1/28/08 magazines mentioned on this page have ceased publication. (This led to a revised page break, which is why two pages are affected.)
The following changes were made for the 27th printing. You need to worry about these changes only if you have a copy of the first 26 printings of the book:
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 10/13/09 sdm xii In 4th para, bad line break in the middle of 10/13/09 "Item 9". 10/13/09 sdm xv Page layout is verso (left side) instead of 10/13/09 recto (right side). 3/17/08 sdm 8, Remove all references to the book's FTP site, 10/13/09 287, because the contents of that site have not been 291, kept up to date. Also, all the FTPable content 302 is (and has been) available at web sites. 12/23/08 jxl 193 The para following the first code example says 10/13/09 that two lines needed to be modified, but it's actually three, because the first line of the member initialization list has to have a comma added at the end. 7/ 3/08 sdm 295 Remove index entries for "abstract classes, 10/13/09 312 and vtbls" and "vtbls, and abstract classes". 7/ 3/08 sdm 296 Remove page 2 from index entry for "ANSI/ISO 10/13/09 standardization committee". 7/ 3/08 sdm 299 Remove page 286 from index entries for 10/13/09 303 "design, of libraries" and "libraries, design and implementation". 7/ 3/08 sdm 303 Remove page 276 from index entry for 10/13/09 "language lawyers". 7/ 3/08 sdm 313 Index entry for "RCIPtr::CountHolder" should 10/13/09 316 be under "Classes and Class Templates", not "Functions and Function Templates". 7/ 3/08 sdm 314 Remove page 200 from index entries for 10/13/09 "String
" and "String::StringValue
".
The following changes were made for the 28th printing. You need to worry about these changes only if you have a copy of the first 27 printings of the book:
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 1/21/10 lxg 202 In code foroperator=
, a close curly brace 5/ 3/10 is missing before "return *this
;".
The following changes were made for the 30th printing. You need to worry about these changes only if you have a copy of the first 29 printings of the book:
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 10/25/10 dxh 145 Specializations for both maxObjects definitions 10/18/12 should be preceded by "template<>
". 10/25/10 dxh 175 Specializations forSmartPtr<Cassette>
and 10/18/12SmartPtr<CD>
should be preceded by "template<>
".
What follows are interesting comments about the material in More Effective C++, but I don't expect to be able to modify the book to take these comments into account until (if ever) I write a second edition.
DATE REPORTED WHO PAGES WHAT -------- --- ----- ----------------------------------------------------------- 11/20/97 kga The code in the book isn't exception safe. [My reply: This is true, but the code wasn't designed to be exception safe, and the material in Items 9-15 notwithstanding, I don't believe I ever suggested it was. In 1995 (when I wrote the book), the notion of exception safety was still largely unexplored. I've written myself a note to make sure that all books I write in the future contain only exception-safe code, but in MEC++ (and in EC++/2E), I followed the time-honored tradition of assuming that exceptions never happen (hence we also never run out of memory). I know some will find this disappointing, but I don't think this is unreasonable. The code isn't likely to be thread-safe, either. In the future, I'll shoot for code that's exception safe, but until very recently (most notably the two articles by Herb Sutter in the October and November/December 1997 issues of C++ Report), I don't think the C++ community as a whole knew how to write exception safe code. If this shortcoming of the book becomes sufficiently annoying or misleading, perhaps I'll write a second edition where I can address the problem.] 11/10/01 sdm 8 Despite untold effort, I have been unable to get Addison 287 Wesley to maintain the validity of their URLs, including those published in my books. The most reliable way to get to the web site for any of my publications (including More Effective C++) is to go to the publications page at my web site, then follow the link from there to the publication's web site. I apologize for the highly unprofessional behavior on the part of the webmorons at Addison Wesley. 2/19/96 rw 10 It's not really true that a reference MUST refer to an object. Instead, a reference is EXPECTED to refer to an object (but there is no way to tell if it doesn't). 12/28/97 sdm 15 An alternative way to approximate the new cast forms is to use templates such as the following: template<class Dest, class Source> inline Dest static_cast(Source object) { return (Dest) object; } This allows clients to use the same syntax as the real thing (this example is from the bottom of page 12): double result = static_cast<double>(firstNumber)/secondNumber; This approximation calls for substantial template support, however, and I'd expect any compiler supporting such sophisticated templates to already offer the new cast operators. 10/20/97 nb 15 One way to get the effect of putting doSomething into an array of void (*)() pointers is to write a wrapper function for doSomething: void callDoSomething() { doSomething(); } A pointer to callDoSomething can then be safely placed in funcPtrArray -- no cast is needed. 11/ 4/97 pdb 15 The macro approximations for reinterpret_cast and const_cast don't offer the same semantics as the real new casts, because the approximations might take the inheritance hierarchy into consideration: class MI: public B1, public B2 { ... }; B2* b2; MI* mi = reinterpret_cast(MI*, b2); // ooops... mi and b2 now point at different // locations because the "normal" cast operator // acts like static_cast here pdb offered the following suggested replacement approximation: #define reinterpret_cast(T, O) ((T)(void*)(O)) This approximation assumes that T is a pointer type, however, and the new casts aren't limited to casting pointers. 12/ 5/97 rs Item 3 The best workaround for a polymorphic array is to use an array of base class pointers. In the example in this Item, for example, declare printBSTArray to take an array of const BST* pointers. Provided the base class has a virtual destructor, this should work fine, though you'll want to be mindful of the issues discussed on page 20. 7/13/99 sdm Item 6 For more information on prefix versus postfix versions of operator++ and operator--, check out Andrew Koenig's column, "Pre- or Postfix Increment" in the June 1999 C++ Report. 11/24/01 iw Item 7 As it turns out, it is possible to overload the || and && operators while still preserving their short-circuit semantics, and this is not uncommon in libraries based on template metaprogramming. Because my objection to overloading these operators is based on the loss of short-circuit semantics, I don't oppose libraries that overload them as long as they also preserve their short-circuitedness. For an example of a library that overloads them for good reason, check out the Lambda Library described at the Proceedings of the 2001 Workshop on C++ Template Programming. (iw wasn't the only person to send me the essence of this comment, but his email was the one that inspired me to add it to the errata list.) 11/ 2/07 nxd 33-34 Declaring by-value function return values const will prevent their being bound to rvalue references in C++0x. Because rvalue references are designed to help improve the efficiency of C++ code, it's important to take into account the interaction of const return values and the initialization of rvalue references when specifying function signatures. 2/22/99 bm 37 "throw" cannot be overloaded. I'm adding this as an "Interesting Comment" instead of an error, because I'm not convinced that "throw" is an operator. However, bm notes that "it is used the same way as delete, sizeof, or typeid and the new-style casts, all of which you list as not overloadable." If somebody convinces me that "throw" is an operator, I'll make this a bona fide bug report, and I'll credit it to both bm and the person who convinces me that "throw" is an operator. 6/24/98 sdm Item 8 For reasons unknown to me, it has become rather uncommon to speak of the "new operator" or the "delete operator". Instead, one speaks of "new expressions" and "delete expressions". The C++ standard, for example, mentions new and delete expressions in several places, but it never really speaks of the new or delete operators. What is a new expression? Simply an expression containing a use of -- you guessed it -- the new operator. Similarly, a delete expression is an expression containing a use of the delete operator. The material in the book isn't incorrect, but its terminology with respect to new and delete is increasingly nonstandard. (As regards operator new and operator delete, the book continues to correspond to convention. The terminology on that front has been stable.) 4/30/06 txs 42 In code fragement, "freeShared(pw)" could be changed to "freeShared(sharedMemory)". It wouldn't change the behavior of the code, but it's arguably better style to use the same pointer variable in the allocation and deallocation calls. In practice, of course, the deallocation site may have access only to the pointer to the object, so the code I show is probably typical. ??/??/95 bwk 49-50 Change WINDOW_HANDLE or WindowHandle to a different name so they can be orally distinguished. 3/ 5/96 sk 50-1 Here and elsewhere the ctors (for e.g., Image, BookEntry, etc.) are not declared explicit, yet Item 5 encourages its use. In fact, all constructors should be reviewed to see if they should be declared explicit. At the time I wrote the book, my concern was that using explicit everywhere would confuse readers, most of whom I assumed would be unfamiliar with the feature. 5/ 5/99 da 53 The following material is misleading: If a destructor were invoked on an object that wasn't fully constructed, how would the destructor know what to do? The only way it could know would be if bits had been added to each object indicating how much of the constructor had been executed. Then the destructor could check the bits and (maybe) figure out what actions to take. Notes da (David Abrahams): Adding bits to indicate how much of the constructor had been executed wouldn't be neccessary. An implementation could use extra bits, but the compiler already has to track similar information in order to know which local (auto) objects need to be destroyed in case an exception occurs. A high-quality exception-handling implementation will be able to tell how much has been executed just by looking at the program counter that gets pushed on the stack when the function implementing "throw" in the compiler's runtime library is called. There's also the question, "what would it do with the information, even if it were available?" Certainly, there's no way for the compiler to anticipate which part(s) of the destructor code might correspond to the parts of the constructor that have executed... ... The way we tell the compiler which parts of the constructor and destructor code correspond to each other is by partitioning the corresponding parts into sub-objects and base classes. One easy way to do that with Scott's example is to embed ImageFilePtr and AudioClipPtr sub-objects in his BookEntry class, instead of using pointers directly. This final suggestion is the essence of the solution I present using auto_ptrs (on page 57-58). 10/18/99 cf 56-57 Another problem with the approach shown on these pages is that it will break if we change the relative order in which the data members theImage and theAudioClip are declared. The function initAudioClip is written with the assumption that it will be invoked after initImage has been called, but the order in which initImage and initAudioClip are called is determined by the order in which theImage and theAudioClip are declared in the class. (For details on the initialization order of class members, see Item 13 of Effective C++.) 10/26/98 sdm Item 10 Several people have suggested that function try blocks should have been mentioned in MEC++, because they can be used to avoid resource leaks in constructors. As I replied to one reader, When I wrote MEC++, I was aware of function try blocks, but I decided not to mention them, because they were so new, nobody seemed to really understand how they were supposed to work. I've since resisted mentioning them, because, in general, they don't solve the problem. Suppose a member initialization list has two different initializations (as it does in on p. 56 in my book). Now suppose an exception is thrown during member initialization, so we end up in the catch clause of the function try block. What do we do? We have no way of knowing which initialization gave rise to the exception, so we have no idea what we need to do to clean up. We can't even safely delete pointers, because they may not yet have been initialized. My understanding is that the primary purpose of function try blocks is to translate exceptions from one type to another type. This could be important if the author of class C implemented C using some library L, but the author of C didn't want to expose L's exceptions to C's clients. 8/14/02 sdm 66-67 The inheritance-based conversions described on these pages apply only to public inheritance. For non-public inheritance, things get a bit more complicated. For details, including a rationale for the behavior, consult the July 2001 column by Jim Hyslop and Herb Sutter, "Baseless Exceptions." 5/ 5/99 da Item 15 As I note in the book, there is more than one way to implement the exception-handling features of C++, and different compilers do things in different ways. As a broad generalization, the remarks in Item 15 show a bias towards an implementation that seems to be more common under Windows than under Unix. For example, it's my understanding that Visual C++ implements things in a manner consistent with my description in Item 15. David Abrahams sent the following comments, in which he is thinking of a different way of handling exceptions, a way that is perhaps more popular in the world of Unix compilers (though it's employed by compilers for other platforms, including Windows). Dave refers to this way as "table-based", because the underlying implementation involves the generation of tables of static data. If no exceptions are thrown, the data pages containing these tables will never be touched. Here are Dave's comments: In Item M15, Scott writes: Let us begin with the things you pay for even if you never use any exception-handling features. You pay for the space used by the data structures needed to keep track of which objects are fully constructed (see Item 10), and you pay for the time needed to keep these data structures up to date. In fact, a table-based implementation of exception- handling will not incur any cost at runtime for updating data structures. All of the neccessary information can be had by looking at the program counters saved in active stack frames. He goes on to say: These costs are typically quite modest. Nevertheless, programs compiled without support for exceptions are typically both faster and smaller than their counterparts compiled with support for exceptions. Of course, these comparisons vary from implementation to implementation. If you care about correctly recovering from errors, it is important to only compare programs compiled with EH to programs without EH support but which use other error-recovery mechanisms (e.g. error-return codes). The truth is that using a table-based EH implementation can substantially improve execution speed in the usual case where no errors occur, because the compiler has the information it needs to move error-handling code and data tables out of the cache and VM working set, improving locality of reference. Furthermore (in contrast to the error-return-code approach) until an error occurs, absolutely no special checks must be made. A second cost of exception-handling arises from try blocks, and you pay it whenever you use one, i.e., whenever you decide you want to be able to catch exceptions. Different compilers implement try blocks in different ways, so the cost varies from compiler to compiler. As a rough estimate, expect your overall code size to increase by 5-10% and your runtime to go up by a similar amount if you use try blocks. This assumes no exceptions are thrown; what we're discussing here is just the cost of having try blocks in your programs. To minimize this cost, you should avoid unnecessary try blocks. Again, this is highly implementation-dependent. A table-based implementation will not make you pay anything at all in runtime for entering a try block. Dave has considerable expertise in the area of exception- handling, and he was a significant contributor to the standard library's behavioral guarantees in the presence of exceptions. In other words, he definitely knows what he is talking about. Dave's above comments on Item 15 notwithstanding, one thing on which we both agree is this advice from the end of the Item: If you have performance problems, profile your software to determine if exception support is a contributing factor. If it is, consider switching to different compilers, ones that provide more efficient implementations of C++'s exception-handling features. 6/ 8/05 vxk 97 In the common case where we loop over i from 1 to n, adding data at position i in each case, the algorithm as shown will allocate new memory n/2 times. It would probably be better to act more like std::vector does and have the amount of new memory allocated based on the current size of the DynArray instead of on how far beyond the end of the DynArray the index is. 2/21/96 sdm 101 In several places I refer to Item 6 for an explanation 102 of why operator+ or operator* should return a const 105 object instead of just an object, but Item 6 discusses 107 only operators ++ and --. The problem is that without a const return value, things like this become possible: (a + b) = 14; // assign to the object returned (a * b) = 0; // from the operator! Such assignments are incompatible with the behavior of the built-ins and are also typically nonsensical, so they should generally be prohibited. Returning const objects prohibits them, just like it prohibits expressions like "i++++" (which is discussed in Item 6). (It's possible to get around the prohibition by declaring operator= to be a const member function, but that notion opens doors I prefer to leave both closed and locked.) 1/20/11 jxw 104 The fact that compilers are permitted to optimize temporary objects out of existence means that observable side effects that would take place during their construction and destruction may not occur. 2/27/01 ph Item 20 Writes ph: We faced the problem of getting large float and int arrays from a database. The dimension of the array depended on the time interval passed as an argument to the reading method. Clearly, we had to return an object, and we couldn't rely upon return value optimization. Our solution was to return an auto_ptr<Array<T> > instead of an Array<T>. This way, we had the advantage of returning something as light as a pointer without the problem of potential memory leaks. The only drawback was a slightly heavier syntax but it was worth it. I don't think this is a solution for the method operator* that you used in item 20 but many other methods that have to return large objects may benefit from it. 9/23/01 lz Item 21 One drawback to lots of overloading is that it can lead to ambiguities. For example, if you declare both f(int) and f(long) and a client calls f with a char or a double, the call will be ambiguous until the client casts the char or double to an int or long. 2/12/07 axj Item 21 In practice, the operator+ functions discussed in this Item would often need to be friends of UPInt or would have to call friends of UPInt in order to have direct or indirect access to UPInt's (private) data members. 5/14/00 rz Item 22 Rather than try to summarize rz's points, I'm just going to quote his mail to me: When considering op vs. op=, there's more to consider than consistency and efficiency (return value optimization -- RVO). Whether or not an operation is affected by aliasing must also be considered. Consider matrix multiplication: Matrix& Matrix::operator *=(const Matrix& rhs) { // lhs is both input and output! Matrix result; for (int i = 0; i < size; ++i) { for (int j = 0; j < size; ++j) { result[i, j] = 0; for (int k = 0; k < size; ++k) result[i, j] += (*this)[i, k] * rhs[k, j]; } } (*this) = result; } Because "this" is used for both input and output (and because matrix multiplication requires many input elements to calculate a single output element), it is necessary construct the result in a (named) temporary to avoid problems with aliasing. In this case, * is actually simpler than *=. operator* involves merely constructing a result (as opposed to reassigning the result to an existing object). A better implementation of both * and *= is: Matrix operator *(const Matrix& lhs, const Matrix& rhs) { Matrix result; // set up RVO for (int i = 0; i < size; ++i) { for (int j = 0; j < size; ++j) { result[i, j] = 0; for (int k = 0; k < size; ++k) result[i, j] += lhs[i, k] * rhs[k, j]; } } return result; // hope for RVO } Matrix& Matrix:operator *=(const Matrix& rhs) { Matrix result = (*this) * rhs; return *this = result; } If a compiler applies RVO and eliminates the unnecessary temporary in the construction of the result in *=, this latter implementation is close to optimal and achieves consistency between * and *=. If an efficient swap function is defined, we can avoid the cost of assignment from the result to "this": Matrix& Matrix:operator *=(const Matrix& rhs) { Matrix result = (*this) * rhs; swap(result); return *this; } The definition of op= in terms of op using the idiom above makes sense when aliasing is an issue. In contrast, aliasing is not an issue for matrix addition. Thus we can define + in terms of +=, as you suggest in MEC++ Item 22: Matrix& Matrix::operator +=(const Matrix& rhs) { for (int i = 0; i < size; ++i) for (int j = 0; j < size; ++j) (*this)[i, j] += rhs[i, j]; } Matrix operator +(const Matrix& lhs, const Matrix& rhs) { Matrix result(lhs); // set up RVO result += rhs; return result; // hope for RVO } Note that all of these implementations should be exception-safe (as operator =): Only after calculations are successfully completed in temporaries is the result assigned to "this". 1/ 4/97 ih 108 The template functions for operator+ and operator- don't behave exactly like hand-written versions of the same functions, because the rules for resolving calls to overloaded functions allow slightly different sets of implicit conversions when calling template and non-template functions. !11/23/99 jm2 109 Both the first and second implementations on this page suffer from the problem that they are returning whatever operator=+ returns. In general, there is no way to know what this is (yes, it's a reference to an object of type T, but a reference to which T object?), hence no way for compilers to optimize away the copy to operator+'s return value. The way to write operator+ such that the return value optimization can be performed is with this body: T result(lhs); result += rhs; return result. 9/15/99 sdm 111 A few people have pointed out that in the C++ code on this page, it's possible to pull the calls to setprecision and setiosflags out of the loop, and this might make the C++ code faster. I deliberately left these calls in the loop, because the corresponding C code has to set up the formatting each time printf is called. To be fair, I wanted C++ to have to do the same. 8/10/06 jxb 112 jxx writes: "I have performed comparsions of iostream vs stdio on GCC3, GCC4 and ICC and I found that, while on GCC3 the difference was significant in favor of stdio, on GCC4 and ICC they performed exactly the same. (For every test I made, I was compiling with full optimizations on.)" 12/15/97 sdm Item 26 For a more complete discussion of how to count the number of objects in existence, see my article, "Counting Objects in C++," in the April 1998 Dr. Dobb's Journal. 12/31/97 sdm 130ff The technique of returning a reference to a static object inside a function isn't thread-safe. If thread-safety is important to you, you're better off using a variant on the GOF Singleton technique, though you must then confront the problem of how to get rid of the Singleton. For further discussion of the issue of thread safety, consult Doug Schmidt's papers on the topic. For more on the problem of destroying a Singleton, check out John Vlissides' column, "To Kill A Singleton," in the June 1996 issue of C++ Report (and since reprinted in Vlissides' book, Pattern Hatching). 12/ 3/05 txs 138-140 The Printer destructor code on page 135 should be repeated at least once somewhere in this page range to remind readers of what happens in the destructor. 9/27/99 jb 141-145 The Counted template could take a second argument for the maximum number of objects to create: template<typename BeingCounted, size_t maxObjects> class Counted { ... }; Printer would then specify the value of maxObjects in its class definition: class Printer: private Counted<Printer, 10> { ... }; This way, there'd be no need to remember to define and initialize Counted<Printer>::maxObjects (as discussed on pages 144-145). On the other hand, this would move the value of maxObjects from a .cpp file to a .h file, and that might not be desireable. 12/ 4/99 ag 145-6 Declaring the class destructor private also makes it impossible to use auto_ptr (or most other smart pointers) with heap-allocated objects of the class. A preferable alternative may be to declare all the constructors private. 10/25/10 dxh 145-6 In the same way that declaring the destructor private necessitates the declaration of some kind of public pseudo-destructor, declaring all contructors private would necessitate the declaration of some kind of public pseudo-constructors. 4/17/99 sdm Item 27 Over the years, the issue of determining whether an object is on the heap has attracted a lot of interest, and I've received numerous messages about it. I've thus set up a web page with an extensive discussion of the matter, including several alternatives to the approach I develop in the book. 12/10/98 jed 154 It's dangerous to make HeapTracked::addresses static, because there is no guarantee that that object will be initialized before it's used. The full story is told in Item 47 of Effective C++, but I fail to describe it in this book (primarily because I treat it in Effective C++). A reasonable solution to the problem is to create a private static member function, addresses(), that returns a reference to a list<RawAddress> object that's static inside the function. This is essentially what I do with the map inside SpaceShip on pages 238ff; note how the map is made static inside SpaceShip::lookup. 7/18/09 fxb 154-5 Because addresses is static inside HeaptTracked, all allocated addresses go into a single list. An alternative would be to make HeapTracked a template that's templatized on the class inheriting from it: template <typename DerivedClass> class HeapTracked { ... }; (This is similar to the design I use for counting objects on pp. 141ff.) 7/18/09 fxb 154-5 Rather than use a list for addresses, it might be preferable to use a set. The current design adds addresses in constant time, but removes them only in linear time. Use of a set would have both operations run in logarithmic time. 4/ 6/96 edw 157 Declaring class-specific operator new private doesn't totally prevent heap-based objects, because it's still legal to create them via ::new. 7/20/96 os 175-179 The member template for implicit conversions works, but it creates a temporary return object from a dumb pointer each time a conversion is performed. This is inappropriate for the reference-counting smart pointers of Item 29, because the temporary would create a new value object instead of sharing an existing value object. Supporting inheritance-based conversions for Item 29's smart pointers requires nontrivial changes to the designs in Item 29. 12/ 7/97 sdm 168-70 To allow smart pointers to be tested for nullness, I'm increasingly fond of the technique Don Box described in his March 1996 C++ Report column: have smart pointer classes support implicit conversion to a nested class type: template<class T> class SmartPtr { public: struct NestedClass{}; // empty nested class ... operator NestedClass*() { return ptr; } private: T *ptr; }; SmartPtr<Widget> sp = new Widget; ... if (sp) ... // convert to SmartPtr<Widget>::NestedClass* .. if (sp == NULL) ... // do same conversion as above !12/11/06 jvdb 175-178 The type conversion code yields multiple smart pointers to the same underlying object, but it fails to address the problem of ownership that is raised on pp. 162ff. It should. (auto_ptr is designed to transfer ownership to the auto_ptr created by the conversion function, but, as noted in a defect report to the standardization committee, the design is flawed.) 4/ 6/96 edw 181 Because SmartPtrToConst is a concrete base class, we can still run into the assignment-related problems described in Item 33. For example, if SmartPtr performs a deep copy in its assignment operator but SmartPtrToConst does not, an assignment to a non-const object through a SmartPtrToConst pointer would fail to perform the deep copy. 11/10/96 tb 181-2 The inheritance-based design allows a SmartPtrToConst<T>& to be initialized with a SmartPtr<T> object. This is unsafe (it allows const objects to be modified when they should not be), and the dumb pointer analogue -- initializing a const T*& with a T* -- is prohibited by C++. 2/ 7/96 tu Item 28 The use of member function templates to implement smart pointer type conversions is problematic in the presence of object ownership, because the ownership must be transferred to the temporary object created as a result of the conversion. This is essentially the same as the auto_ptr problem I describe in the book on p. 164. 2/12/96 wrz 190-3 The shareable flag solves one side of this problem, but the flip side remains unsolved: how to guarantee that the reference handed out by the non-const operator[] remains valid for the life of the String object. Consider: String s1 = "Hello"; String s2 = "Goodbye"; char *p = &s1[2]; s1 = s2; // this invalidates p, because it still points // into the memory owned by the StringValue // that s1 USED to point to, sigh The solution to this problem entails finding a way to make sure that s1 never points to a different StringValue object once a reference has been returned. (This is essentially the problem that Tom Cargill attacks in his June 1992 C++ Report article, sigh.) This is, in general, a very hard problem. The ISO/ANSI string class deals with it by saying that any reference returned from operator[] becomes invalid the next time a non-const member function is invoked on the string object. 6/ 3/03 sxb 202 The bug for these pages reported by ct on 9/16/98 is 206 incorrect, because when RCPTR::operator= is called with a raw pointer as an argument (e.g., p->next), a temporary RCPtr will be created from the raw pointer, hence the reference count won't go to 0 when removeReference is called. (I'll keep the code in the book as is, because it behaves correctly, and I don't want to mess with code in the book any more often than I have to.) 7/ 5/96 os 208-210 Because CountHolder is nested inside RCIPtr<T>, it is not possible for an RCIPtr<Base> and an RCIPtr<Derived> to point to the same CountHolder, even though they should be type-compatible. Not even the techniques of Item 28 (i.e., member templates to simulate inheritance for smart pointers) solve the problem. A better design is needed, probably one based on CountHolder being a template class at global scope (like RCPtr). 5/ 5/99 da 208ff As noted above, the code in the book wasn't generally designed to be exception-safe, but it's still worth noting that RCIPtr is likely to lead to resource leaks in the case of an exception. For example: template<class T> RCIPtr<T>::RCIPtr(T* realPtr) : counter(new CountHolder) { ... } If "new CountHolder" throws an exception, it's probable that the realPtr parameter will be leaked. Here's a fix: template<class T> RCIPtr<T>::RCIPtr(T* realPtr) { try { counter = new CountHolder; } catch(...) { delete realPtr; throw; } ... } 1/18/97 sdm Item 29 Because RCObject has a virtual destructor, any class inheriting from it will have a vptr (see Item 24). Some classes won't like this extra baggage. RCObject can be redesigned to have a nonvirtual destructor, but then removeReference can't "delete this" when the reference count is 0. Instead, isShared must be replaced with a function to return the current reference count, and anybody calling removeReference (e.g., operator= and the destructor in RCPtr<T>) must then check to see if the result is 0. If so, they must delete the RCObject. Finally, each value class should privately inherit from RCObject to make the nonvirtual destructor a valid design. This revised design imposes no overhead on classes inheriting from RCObject, but the deletion burden when the reference count goes to 0 is shifted from RCObject to all its clients. 5/ 7/99 vp Item 29 Writes vp: "The RCIPtr class also reference counts null pointers. If I want to use a vector of RCIPtrs and I initialize the vector as vector< RCIPtr<T> > vec(1000), this would create 1000 countholder objects, all of which would point to NULL. Would it not be better to not reference count the null pointer by not creating countholder objects when the RCIPtr contructor is invoked with a null pointer and then using the check "if (counter)" whereever necessary?" My reply is that whether that's better depends on how frequently you expect to deal with null pointers. If you expect null pointers to be a relative rarity, the runtime friction of the nullness tests might be something you'd prefer to avoid. If you expect to deal with null pointers relatively frequently (as the example above suggests), vp's design might be preferable. I've seen smart pointers designed both ways. 4/27/97 sdm Item 30 In a posting to comp.lang.c++.moderated posting of 4/25/97, Brian Parker writes: One problem with the use of proxies that I have found that is not discussed in "More Effective C++" is when they are used for templated types e.g. for complex <double>... . In this scenario, when calling template functions that take complex<T> (e.g. conj() et al) with a returned proxy, the proxy is not converted to a complex<double> but instead a template argument deduction failure results. For further information on this observation, look up the thread, "lvalue/rvalue, non-const/const" initiated by Daniel Hempel on April 21, 1997. 1/26/06 sdm Item 30 Sander Stoks wrote an interesting article ("Syntactic Aspartame: Recreational Operator Overloading") exploring some, er, innovative applications involving proxy classes in the Feburary 2006 Dr. Dobb's Journal. 7/25/09 fxb 219ff There is little to be gained by having the const String::operator[] return a proxy. One might as well retain the declaration for that function shown on page 218. 9/ 5/99 ll 221 As I should have suspected, the const_cast leads to trouble. Consider: const String& str; String::CharProxy cp = str[0]; cp = 'x'; // modify const string! Of course, clients aren't supposed to create local variables of type String::CharProxy, but if it compiles, somebody will do it. Sigh. The solution is to have two different proxy classes, one for const objects (e.g., ConstCharProxy) and one for non-const objects (the current CharProxy). ConstCharProxy would be almost identical to CharProxy, but it wouldn't support assignment or any other lvalue use. Such a design would probably requiring tinkering in many places, e.g., CharProxy would have to be able to be the target of an assignment from a ConstCharProxy. The real moral of the story here is one I should have learned by now: casts are almost always bad! 3/21/96 jj 223 Instead of having both String::CharProxy operator= functions call a common private function, it would be better to make the const CharProxy& version just call the char version. That way there'd be no need for an additional private member function: String::CharProxy& String::CharProxy::operator=(const CharProxy& rhs) { *this = rhs.operator char(); // call operator=(char) return *this; } 6/26/98 sdm 226 One way to make all operations on a T applicable to a proxy for a T is to have Proxy <T> inherit from T. This is the design advocated in the Proxy Pattern in the book, Design Patterns, by Gamma, Helm, Johnson, and Vlissides. Unfortunately, this works only when T can be inherited from, and that's not the case when T is char (as it is in Item 30). 4/ 2/96 rv Item 31 When setting up collision maps, it would be safer to avoid 10/19/97 mr the use of literal strings like "SpaceShip". Instead, the 11/23/99 wek maps should be initialized via calls to typeid. That way it would be harder to accidently use the wrong string for a class name. Furthermore, the standard makes it clear that type_info::name can return just about anything, so there's no portable use for the results of that function. That suggests that the map-related typedefs for the non-member solution of pp. 244-248 should really be these: typedef void (*HitFunctionPtr)(GameObject&, GameObject&); typedef map<pair<const type_info*, const type_info*>, HitFunctionPtr> HitMap; That's not quite right, however, because the map's ordering function would be less<pair<const type_info*, const type_info*> >, and there's no guarantee that that will (1) exist and (2) behave in a reasonable fashion. Furthermore, you're not allowed to define your own version of this class, because it doesn't use any user-defined types. You'd therefore have to write a custom comparison class for the map: class TICompare { ... }; // "type_info compare" typedef void (*HitFunctionPtr)(GameObject&, GameObject&); typedef map<pair<const type_info*, const type_info*>, HitFunctionPtr, TICompare> HitMap; Details are left to the reader, but inside TICompare's operator()(), it would be important to use type_info::before() instead of comparing raw type_info* pointers, because the results of comparing raw pointers is generally undefined. 2/22/99 bm Item 31 The function makeStringPair is no longer needed, because the make_pair function (briefly described on page 247) is more flexible in the final standard than it was in the draft standard I consulted when I wrote the book. When I wrote the book, make_pair<T1,T2> could only create an object of type pair<T1,T2>, but now make_pair<T1,T2> can create an object of type pair<T3,T4> as long as T1 is implicitly converible to T3 and T2 is implicitly convertible to T4. Because const char* is implicitly convertible to string, make_pair<const char*, const char*> can be used to create an object of type pair<string, string>. The code in the book isn't wrong, it's just unnecessarily complicated. (The code in the book is also more portable, because it doesn't rely on compiler support for member function templates. The new, simpler approach does.) 4/26/99 bs Item 31 In cases where symmetry applies (e.g., where the results of processCollision(A, B) are the same as the results of processCollision(B, A)), it may be preferable to store a function pointer in the map for only one of the combinations, then look up the second one if the first one fails, i.e., do lookp(A,B) and, if it fails, do lookup(B,A). Only if both fail would 0 be returned. As Bram notes, "It would make it more difficult for the map to get in a corrupted state. The loss in performance is moderate, and can be compensated by using hash_map instead of std::map." (hash_map is not standard, but many versions of the STL include it.) 6/30/99 pab p239ff If an exception is thrown within initializeCollisionMap, the resources acquired by the map will be leaked. A better design would be to use an auto_ptr<HitMap> both inside initializeCollisionMap and as its return type, like this: auto_ptr<HitMap> initializeCollisionMap() { auto_ptr<HitMap> phm(new HitMap); ... return phm; } This is definitely a superior design, but please keep in mind my comments above on exception safety in reply to kga's observation of 11/20/97. 1/ 9/00 sdm Item 31 On 11/16/99, Edmund Schweppe initiated a thread in the newsgroup comp.lang.c++.moderated with the subject, "Multiple Dispatch -- Better Than Item 31?" As of 1/9/00, there were nearly 100 articles in the thread; I contributed a few of them. On 1/6/00, Rob Santos initiated a similar thread, "Better than item 31." If you're interested in multiple disptach, I encourage you to read these threads. 8/ 5/01 sdm Item 31 For a more contemporary treatment of multiple dispatch than I give in Item 31, check out chapter 11 of Andrei Alexandrescu's excellent Modern C++ Design. 12/18/03 sdm Item 31 Nat Goodspeed's article, "Double Dispatch Revisited," contributes an interesting new approach to implementing support for double dispatch that also supports inheritance. 3/11/08 sdm Item 31 Over a decade after MEC++ was published, interest in double-dispatching (including the closely-related Visitor design pattern) remains high. One recent treatment is Danil Shopyrin's "MultiMethods in C++: Finding a Complete Solution," which is presumbably the same technique described in Shopyrin's article, "Multimethods in C++ Using Recursive Deferred Dispatching," in the May/June 2006 issue of IEEE Software. Another is Anand Shankar Krishnamoorthi's "Cooperative Visitor: A Template Technique for Visitor Creation." (As with all online articles, be sure to check the accompanying online discussions and comments, because they often provide clarifications and insights missing from the articles themselves.) 7/31/98 sdm Item 33 Note that prior to the transformation, it's legal and sensible to assign a Lizard or a Chicken to an Animal. After the transformation, it's not. 10/20/97 nb 269-70 Another possible workaround for inheriting from concrete classes in read-only libraries is to create a new abstract class inheriting from the concrete class, then have your new concrete class inherit from the abstract class so derived. 6/26/98 sdm 269-70 At the end of the third bulleted paragraph, note that the 5/ 5/99 use of private inheritance models "is-implemented-in-terms- of" and DOES allow for virtual functions to be redefined. See also Herb Sutter's column in the October 1998 C++ Report, which discusses the private inheritance solution and also shows how to combine inheritance and composition such that it's possible to override the virtual functions in a base class WITHOUT inheriting from it. !10/10/01 pb 272 Notes pb, "'extern "C"' doesn't mean that name mangling is suppressed. Rather, it means that names should be mangled in the way that the targeted C compiler mangles them. Some C compilers put an underscore at the front of a name; others put an underscore at the end; some don't change the name at all. A name declared as 'extern "C"' should be mangled in the same way." 5/ 5/02 ar 272-3 extern "C" and extern "C++" are actually different types, making something like this illegal: extern int foo(double d); extern "C" int bar(double d); int (*ifp)(double d) = foo; /* OK */ int (*ifp)(double d) = bar; /* type mismatch */ I.e., assigning an extern "C" function to a pointer to a C++ function isn't allowed. Most compilers fail to enforce this restriction. (BTW, the Standard supports both extern "C" and extern "C++". There is little use for the latter, as it is the default language linkage.) 8/28/98 dp 274 Static objects must be destroyed even if exit is called (instead of running off the end of main). The call to performStaticDestruction should really go inside exit(), and that's where compilers employing this scheme actually put it. 1/18/04 sdm Item 34 A topic related to this Item is how to link object code generated from different C++ compilers. For one take on that topic, consult Karsten Hoof's February 2004 CUJ article, "GNU & Native Compilers." For another, see Joe Goodman's March 2004 CUJ article, "Interoperability & C++ Compilers." !11/ 2/00 sdm 291ff Copy constructor and assignment operator are missing and won't be generated from the template member functions. These need to be added elsewhere in the book, too.
Who's who:
lsk = Luis Sergio Kida avk = Andrew V. Klein jep = John E. Potter en = Eric Nagler tu = Tim Uttormark js = Jeffrey Smith msf = Mike Fulkerson sb = Sam Bent ds = Dan Saks ahd = Anton Doblmaier wg = Wolfgang Glunz os = Oleg Shteynbuk wrz = Warren R. Zeigler um = Ulf Michaelis clt = Clovis L. Tondo sm = Sekhar Muddana rw = Rob Wilkinson mb = Michael F. Baker ml = Michael Loftus ymk = Yechiel M. Kimchi lh = Liz Hanks dmp = David Papurt sk = Stefan Kuhlins ih = Ian Haggard jm = Jim McCracken ras = Robert Allan Schwartz we = Wil Evers tb = Thomas Becker jj = John Jacobsma jl = Jon Lachelt aid = Alan I. Duchan dh = David Halpin rcn = Ramesh Nagabushnam mr = Mark Rodgers rv = Roger Vaughn nb = Nick Bishop tjb = Timothy John Buchowski clf = Cheryl L. Ferguson ks = Kirk Swenson mm = Munir Mahmood edw = Ed Willink gm = Graham Mark psrc = Paul S R Chisholm dab = David A. Barrett jr = Jack Reeves drk = Damian R. Kanarek dcs = Douglas C. Schmidt rxc = Ron Coutts lw = Lance Whitesel pdb = Paul Du Bois kga = Klaus-Georg Adams rs = Rao Surapaneni cxm = Chris Morley dxg = David Goh rxb = Rainer Baumschlager jed = James Davis cwg = Charles W. Green bwk = Brian Kernighan dp = David Petrou jww = John Wait rhs = Bobby Schmidt bm = Bernd Mohr jsh = Sivaramakrishnan J. da = David Abrahams ea = Eric Anderson pab = Phil Brabbin bs = Bram Stolk jb = Jaroslav Bradik vp = Vasanth Philomin wek = William Kempf ll = Lou Lavery km = Kurt Miller fk = Feliks Kluzniak jm2 = Jörg Barfurth ct = Christopher Tavares js2 = Julian Smith ag = Andy Glew rz = Rob Zako cf = Carsten Frigaard ccr = Christopher Creutzi em = Evan McLean wcm = William C. Mattison nxd = Niels Dekker bp = Balog Pal ac = Alexsander Chukhlebov cw = Chris Wineinger wds = Dean Stanton csp = Chulsu Park ph = P. Haution ga = Giulio Agostini pb = Pete Becker agb = Alexander Bogdanchikov jfn = John Newell ar = Aharon Robbins lz = Leor Zolman mt = Michael Tegtmeyer pd = Pankaj Datta ais = Adrian Spermezan iw = Ian Whittley ddg = Davide D. Gennaro at = Ani Taggu cc = Chang Chen mh = Matthias Hofmann jw = John Wismar ms = Mark Symonds ir = Ita Ryan fb = Fredrik Blomqvist vxk = Vassili Kaplan tk = Thomas Kim txs = Thomas Schell rxy = Rice Yeh jxb = Jan Bujak axj = Arun Joseph jvdb = Jelle van der Beek cxs = Colas Schretter jxl = Johannes Laire fxb = Florian Bauer lxg = Leonhard Gruenschloss dxh = Dongbo Hu jxw = Jonathan Wheeler
From here you may wish to visit the Amazon Page for More Effective C++.