Scott Meyers
Modification History and Errata List for Effective C++, Second Edition
Last Updated 31 August 2015
by Scott Meyers
Note: This document applies to only the second edition of Effective C++. Errata for other editions of the book are available as follows:
What follows are my notes on what I've changed in the second edition of 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 third printing, the changes made to the first and second printings 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 Effective C++. To submit comments, send email to ec++@aristeia.com or write to the address listed on page xv of the book's preface.
To be assured of always having the most accurate version of Effective C++ at your fingertips, I encourage you to buy a copy of each printing :-).
The following changes were made for the third printing of the book. These changes apply to your copy of Effective C++, Second Edition only if it's the first or second printing.
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- N/A N/A xix Added apm to the acknowledgements. This changed 9/13/97 xx the break between pages xix and xx, so I had to 248 update the index entries for "Nemeth, Evi", 250 "USENIX Association", and "Internet Engineering 256 Task Force, The". ! 9/ 7/97 apm 20 "<" preceding strdup's return type should be 9/13/97 omitted. (I also moved the comment over to make the example easier to read.) 9/ 2/97 sdm 136 In two comments on this page, "postpones" has an 9/13/97 apostrophe that shouldn't be there. ! 9/13/97 apm 184 In Set<T>::remove, the call to find should 9/14/97 terminate with a semicolon, not a colon.
The following changes were made for the fourth printing of the book. These changes apply to your copy of Effective C++, Second Edition only if you have one of the first three printings.
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 11/13/97 rmw xv In line 1 of 4th paragraph, change "small reward 2/ 3/98 for" to "small reward to". 9/25/97 sdm xviii Bad break in "Item 5" in 5th line from bottom of 2/ 3/98 xix page on page xviii. Caused page break between xviii and xix to shift. 2/ 3/98 sdm xix Updated list of people who reported bugs in 2/ 3/98 xx earlier printings I fixed in this printing. 12/ 4/97 sdm 3 On 11/14/97, the ISO and ANSI standardization 2/ 3/98 6 committees adopted what is all but certain to 224 become the official standard for C++, so all 225 references to "draft standard," "nascent 234 standard," "DIS," etc. should refer to to just 235 "the standard." 236 11/18/97 vb 5 Division by 10.0 unnecessarily forces use of 2/ 2/98 floating point math. Change to division by the int 10 instead. 9/25/97 sdm 5,13, Made minor cosmetic change in punctuation, 9/28/97 24, hyphenation, or wording. 2/ 2/98 40,51, 62,75, 95,98, 99,107, 116,117, 125,144, 161,164, 178,183, 185,195, 219,229, 233 11/ 5/97 en 6,8, The default constructor for my String class sdm 49,72, shouldn't give its parameter a default value of 87,94, 0 (the null pointer), because the standard 95,124 string type exhibits undefined behavior when it receives a null pointer. I modified my examples to be more in accord with the standard string type, which offers the following constructors: string(); // default ctor string(const char *); // ctor from C string 2/ 2/98 sdm 7 Bad line break between "Item" and "22" at the 2/ 2/98 end of the second prose paragraph. Fixed. ! 9/25/97 sdm 7,8, "string class" => "string type". As Item 49 9/28/97 55,65, explains, string is not a class. 2/ 2/98 126,144, 145,146, 227 9/25/97 sdm 9 Missing space between "assignment" and "By" in 9/28/97 first prose paragraph. 12/18/97 mmr 13,14 Contrary to my assertion at the beginning of the 2/ 3/98 Item, the preprocessor IS part of the formal language standard. Furthermore, some implementations DO put the names of macros in the symbol table and DO show their names in error messages. I reworded things to make them true. 12/31/97 sdm 26 The exception should be caught by reference 2/ 2/98 30 instead of by value. This is for efficiency. 32 The full story is in More Effective C++, Item 13. ! 9/25/97 dxs 31 Comments after last two statements in the code 9/27/97 example are incorrect. The call to X::set_new_handler sets the handler to the null pointer, and the subsequent use of "new X" will call *no* error-handling function if allocation fails. 10/13/97 rh 32 Misspelling in last prose sentence: 10/27/97 "newHanderSupport" ==> "newHandlerSupport". 9/25/97 sdm 34 Replaced "0" with "null" in prose where I 9/28/97 35 referred to a pointer's value. This is to avoid implying that the *value* of a null pointer is zero. (If the book makes sense, but this paragraph doesn't, ignore this paragraph.) ! 9/25/97 sdm 38 At beginning of paragraph following second code 9/28/97 block: "By defining a function" => "By declaring a function". 1/ 1/98 sdm 57,59, unsigned int ==> size_t 2/ 2/98 60,62, 95,96 9/25/97 sdm 58 In first Wacko constructor, changed type of s 9/28/97 from char* to const char*. 9/25/97 sdm 66 Bad break in "Item 11" in 3rd prose paragraph. 2/ 3/98 10/16/97 mt 73 In comment inside operator=, "copy old data" ==> 10/27/97 "copy rhs's value" 12/12/97 th 86 On line 20, a space is missing between "call." 2/ 2/98 and "But". 12/10/97 dm 88 "different ... than" ==> "different ... from". 2/ 3/98 sdm 121 162 169 170 9/25/97 sdm 91 Cross reference to Item 1 in 2nd paragraph should 9/28/97 be to both Items 1 and 47. !10/15/97 sdm 91 The type of string literals is now const char[], 2/ 3/98 215 (which almost always decays to const char*), so any place using a string literal to initialize or assign to a char* is technically illegal (it's a violation of const correctness). In practice, I expect all compilers to accept such code on the grounds of C compatibility, but it's still wrong in the book. I added a footnote to page 91 explaining the situation, and I modified the example on page 215 to avoid running into the problem. !10/27/97 sdm 97 strlen returns a size_t, not an int. 2/ 2/98 9/15/97 sdm 108 The avg functions should all return doubles. 9/27/97 9/20/97 sdm 114 Style of Inheritance diagrams isn't consistent 9/27/97 158 with those in More Effective C++ or with that 172 shown on page 231. All diagrams should use the 177 "class names inside ellipses" style of page 231. 198 200 205 209 10/15/97 gb 131 In 1st line of 1st new paragraph, I say that 10/27/97 verifyAddress is protected. It's not, it's private. I adjusted the text. 12/22/97 th 144 "Nobel prize-winning" ==> "Nobel Prize- 2/ 2/98 winning". (Note change in capitalization.) 9/29/97 sdm 149 In last prose paragraph on page, parenthesis 9/29/97 following "virtual constructors" is in wrong font. 9/13/97 apm 163 In 4th para, "only two kinds of plane" => "only 9/27/97 two kinds of planes". 12/31/97 th 174 Wrong font for 's' in first use of "BankAccounts" 2/ 2/98 in 3rd line from bottom. 9/27/97 sdm 177 Space preceding "cred-" at end of page is in 9/27/97 wrong font. 12/17/97 mgh 184 Remove extra closing paren at end of 2/ 2/98 Set<T>::member's implementation. !11/19/97 jh 186 Stack and GenericStack lack a copy constructor 2/ 3/98 191 and an assignment operator, a direct violation of Item 11! I declared them private in both cases and added an xref to Item 27 for each. !11/26/97 tk 187 Stack::empty should return (top == 0), not 2/ 2/98 (top != 0). 9/25/97 sdm 194 In middle of last paragraph of Item 42, cross- 9/28/97 reference should be to Item 43, not Item 44. 9/21/97 apm 203 Three times on this page in the paragraph 9/27/97 following the code, "name" => "theName". !11/24/97 sdm 215 To what string should nameValue refer if the 2/ 3/98 char* constructor is called? I modified the example to eliminate the problem. (See also comment above about pages 91 and 215.) !11/11/97 jg 227 The "basic_ostream<char>" that precedes the 2/ 2/98 "template<class charT" shouldn't be there. The correct declaration for basic_string is: template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> > class basic_string; 12/14/97 sdm 230 In the discussion of the algorithms' performance 2/ 3/98 guarantees, I changed the example from sort to stable_sort, as the latter has a slightly stronger guarantee. 2/ 3/98 sdm 234 ISO stands for "International Organization for 2/ 3/98 Standardization", not "International Standards Organization". Who knew? 10/25/97 kb 235-6 The example code should show f as virtual, 2/ 3/98 because it doesn't affect the example in any way, and using nonvirtual functions unnecessarily violates the guidance of Item 37. 10/28/97 sdm 236 A better way to make Base's f visible in 2/ 3/98 Derived is to add the following using declaration to Derived: using Base::f;
The following changes were made for the fifth printing of the book. These changes apply to your copy of Effective C++, Second Edition only if you have one of the first four printings.
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 9/29/97 sdm x-xx Running header should have section title up 6/ 9/98 against the binding margin. As things stand now, it's inconsistent with the rest of the book and with More Effective C++. 8/ 4/97 ds x,59 The title to Item 14 suggests it may be okay to 6/ 9/98 create base classes with no destructors at all. I changed the Item title to "Make sure base classes have virtual destructors." 6/13/98 sdm xix Added Dave Smallberg to list of pre-publication 6/13/98 reviewers. N/A N/A xix,xx Updated list of bug reporters. 6/11/98 ! 4/ 8/98 atc 26 The assert macro checks its argument only if 6/12/98 240 NDEBUG is undefined. Furthermore, it's poor 250 programming to use assertions to detect conditions that might occur in production code. Running out of memory is such a condition. 2/ 7/98 dxg 42 In line 15, "big enough hold" ==> "big enough 6/ 8/98 to hold" 2/ 7/98 dxg 48 Change first word on page from "is" to "are" 6/ 8/98 6/20/98 sdm 60 At beginning of last paragraph: "topic: when" 6/29/98 ==> "topic. When" 6/17/98 sdm 72 In third prose paragraph, "One of them is 6/29/98 efficiency." ==> "The lesser of them is efficiency." 10/10/97 sdm 78 Header shouldn't have an Item number in it. 6/ 8/98 6/28/98 sdm 91 The footnote I added on 2/3/98 isn't quite 6/29/98 correct. It's true that the type of a string literal is now const char[]. However, the standard includes a loophole that allows char* variables to be initialized with string literals anyway, even though it's not const correct. The practice is deprecated, but, contraray to what I said in the footnote, it's not illegal. I modifed the footnote. 2/ 7/98 dxg 95 The String constructor should initialize 6/ 8/98 lengthIsValid to false, not 0. 7/11/97 das Item 27 Note that the advice of this Item applies to all 6/13/98 compiler-generated functions, not just operator=. It's particularly common for both the assignment operator and the copy constructor to be explicitly disallowed. I added a new paragraph to the end of the Item (on p. 117). 2/ 4/98 sdm 132-3 Space between "step" and "3" should be 6/ 8/98 non-breaking. ! 4/ 2/98 sdm 145 It turns out that the standardization committee 6/ 8/98 12/13/99 sdm 146 has, with only a few exceptions (notably explicit 227 specializations for templates instantiated with 228 user-defined types), forbidden programmers from 247 declaring anything that's in namespace std. The 248 implication is that you shouldn't use the mechanism I show on p. 228 to declare the string type. Instead, you should simply #include <string>. (Edits to page 228 eliminated mention of <iosfwd>, hence the modifications to Index pages 247-248.) 4/ 8/98 atc 147 Move "of" from line declaring returnADate to line 6/12/98 declaring takeADate. This make is less likely that the takeADate comment will be read on its own. 4/ 8/98 atc 159 Reworded last sentence on p. 159 (to improve 6/12/98 160 clarity) and moved that sentence to the top of p. 160 (to more closely associate it with the bulleted paragraphs). 2/10/98 dxg 160 In line 17, "relationship it is" ==> 6/ 8/98 "relationship is" 2/10/98 dxg 163 The last word in line 1 should be "behind" 6/ 8/98 instead of "for". ! 2/10/98 dxg 173 Lines 9 and 11 state that the dynamic or static 6/ 8/98 type of pr is Rectangle or Shape, respectively, but the correct types are Rectangle* and Shape*. 2/10/98 dxg 184 In the 10th line from the bottom of the page, 6/ 8/98 "In code" ==> "In the code" !11/26/97 tk 193 Parameter to GenericStack::push should be of type 6/ 8/98 void*, not const void*. 4/ 8/98 atc 198 Reworded first large prose paragraph to avoid 6/12/98 using "invariably" in a way that really means "almost always". 3/24/98 bc 203 In the paragraph preceding the code, PersonInfo's 6/ 8/98 member function names are incorrect: the "the" prefix has been omitted. 6/ 8/98 sdm 203 Bad line break in third line after code example. 6/ 8/98 11/19/97 jh 217 In the enum solution, Jan is 0, Feb is 1, ... Dec 6/ 8/98 is 11. In the class solution, Jan is 1, Feb is 2, ... Dec is 12. I modified the enum solution to make the numbering match the class-based solution. (It's more intuitive for January to be 1, not 0.) 11/19/97 jh 218 There should be a way to convert Month objects 6/ 8/98 1/22/98 mdr into ints. This would be convenient for comparing Months, etc. 1/22/98 mdr 218 The copy constructor for Month shouldn't be 6/ 8/98 219 private, as there's nothing unsafe about copying an existing month. Allowing copying also makes it possible to define local Month objects with nice names, e.g., Month April = Month::Apr(); ... That eliminates the need for the "final point" discussion on page 219. As a result, I decided to remove the copy ctor declaration and let the compiler generate the default (public) version. 11/10/97 max 221 The text claims it's not possible to determine 6/ 9/98 if it's possible to determine the correct order of initialization, but it also claims it's not possible to determine the correct order. This is contradictory. Reword. 4/ 8/98 atc 229 Append " -- as well as with built-in arrays!" to 6/12/98 the last sentence on the page. 11/18/97 vb 233 In the end, the goal of "compatibility with 6/ 9/98 traditional tools and environments" was not fully achieved, because some inlining and template rules stretch traditional linkers beyond the breaking point. 10/15/97 clf 239 Merge the index headings "Before 0" and "0-9" 6/ 7/98 into "Before A" for consistency with the index in More Effective C++. 12/31/97 th 239-56 Index entries for "Protocol classes", "ABC", and 6/10/98 "Body classes" are incomplete or inconsistent. Fixing these errors caused lots of page breaks in the index to change, so I just decided to reprint the entire index. I also took the opportunity to add Chris Van Wyk and Oleg Zabluda to the index, as they'd been inadvertantly omitted
The following changes were made for the sixth printing of the book. These changes apply to your copy of Effective C++, Second Edition only if you have one of the first five printings.
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 9/15/98 jww xviii "Angelika Langer and Klaus Kreft" ==> 2/ 8/99 "Klaus Kreft and Angelika Langer" 9/15/98 jww xviii "Prentice-Hall, 1978" ==> "Prentice-Hall, 2/ 8/99 initially published in 1978". (A second edition was published in 1988.) 2/ 8/99 sdm xx Added axt, jww, bs, and lf to the 2/ 8/99 acknowledgements. 8/19/98 axt 34,35 Replace "while (1)" with "while (true)". 2/ 8/99 11/30/98 bs 47 Because Airplane::memPool is a non-local static 2/ 8/99 48 object, care must be taken to ensure that it's initialized before it's used. The discussion on these pages should include a cross-reference to Item 47. 8/19/98 axt 60 For consistency with the code shown on page 59, 2/ 8/99 the initialization of EnemyTank::numTanks should be shown. 8/19/98 axt 62 In last paragraph, "NamedArray" is incorrectly 2/ 8/99 broken across lines. 8/19/98 axt 79 In 3rd paragraph on page, "On other hand" ==> 2/ 8/99 "On the other hand" 8/19/98 axt 109 Add comma after "see" in "(see e.g., Item 12)". 2/ 8/99 1/18/98 lf 112 According to the C++ standard, there is no such 2/ 8/99 thing as an "anonymous class". Rather, there are "unnamed classes". Hence, "anonymous class" ==> "unnamed class". 8/19/98 axt 151 In last paragraph, "virtual pointer" ==> 2/ 8/99 152 "virtual table pointer". This changed a page break. 8/19/98 axt 176 Remove reference to Smalltalk in last paragraph, 2/ 8/99 254 as I'm told this remark isn't appropriate for software development in that language. 8/19/98 axt 203 "brace" ==> "square bracket" 2/ 8/99 2/ 8/99 sdm 203 Bad line break in "valueDelimOpen". 2/ 8/99
The following changes were made for the ninth printing of the book. These changes apply to your copy of Effective C++, Second Edition only if you have one of the first eight printings.
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 12/18/99 sdm 12 I added a new paragraph pointing out that I 4/28/00 generally omit mention of the std namespace in example code. Thus, I refer to cout instead of std::cout, etc. 10/ 3/99 sdm xvi Add mention of my mailing list, which announces 4/28/00 each time the on-line errata list is updated. ! 3/ 3/99 gy 5 The minus sign ("-") that precedes a negative 4/28/00 number is not a digit, so the number of digits should not be incremented when the negative number is made positive. 2/22/99 bm 19 In the book, I wrote, "Besides, <iostream> 4/28/00 is less to type than <iostream.h>. For many people, that's reason enough to prefer it." However, bm notes this: "Then these people forget that they have to use qualified names (e.g. "std::cout") or using declarations which require many more letters than the two letters saved :-)" Smiley face, indeed. I removed the sentences. 10/25/99 fk 39 Misaligned comment on 15th line from top of 4/28/00 page. 4/ 9/99 bp 45 Add to the definition of a memory pool that it 4/28/00 never grows larger than the maximum amount of memory requested by its clients AT ANY GIVEN TIME. ! 5/24/99 cp 60 The initialization of EnemyTank::numTargets 4/28/00 should be EnemyTank::numTanks. 4/24/00 id 67 References to the variable x on this page should 4/28/00 be to z, because the last part of the chain of assignments on page 64 involves z, not x. 3/27/99 joh 81 In last line, clarify that "the classes's 4/28/00 interface" means the interfaces of the classes generated from the Array template. ! 3/28/99 joh 178 In first line, "redefined" ==> "defined". The 4/28/00 function creditInterest isn't defined in InterestBearingAccount. ! 2/19/99 mdr 218 Only the Month objects returned from Month's 4/28/00 static member functions are guaranteed to be const. Month objects created by users need not be const, and they may be modified via assignment. ! 1/21/99 sdm 225-6 Contrary to the second bullet on this page, 4/28/00 names introduced via standard C headers *are* in namespace std. Matt Austern explained the reason for this in a posting to comp.lang.c++.moderated on 1/18/00: <cfoo> headers define symbols in namespace std only, while the <foo.h> headers define them in namespace std and then import them into the global namespace as if by using-declarations. This is described in section D.5, paragraph 2, of the C++ standard. It clearly wouldn't work for the <cfoo> headers to define names in namespace std only and for the <foo.h> headers to define names in the global namespace only. If we did it that way then (for example) we'd get two different ldiv_t types, one from <stdlib.h> and one from <cstdlib>. As is we've instead got a single ldiv_t that can be referred to from two different namespaces. Because of Koenig lookup, it makes a difference which one it was originally defined in. I reworded the bullets to state for each one what's in the global namespace, what's in std, and what's in both. 2/15/99 sdm 231 In the diagram, the tail of the arrow attached 4/28/00 to "range_error" pokes into the ellipse. It shouldn't.
The following changes were made for the eleventh printing of the book. These changes apply to your copy of Effective C++, Second Edition only if you have one of the first ten printings.
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 3/ 7/01 das xix "Dave Smallberg" ==> "David Smallberg" 9/10/01 254 ! 8/25/00 pm 68 In operator=, "*ptr = *rhs.ptr;" ==> 9/10/01 "ptr = rhs.ptr;" Modify the comment accordingly. 1/19/00 ccr 90 In order to cache a running average, you need 9/10/01 two pieces of data: the overall total and the number of things being counted. The text in the book implies that only one data member is needed. 8/15/00 cb 104 Eliminate the bulk of the first prose paragraph 9/10/01 105 on this page. When I originally wrote the paragraph for the first edition of EC++, the example involved string objects, not rational numbers, and for strings, the situation was different. When I changed the class for the example, I failed to recognize that the words no longer made sense for the example. To avoid too unbalanced a page, I moved the break between pages 104 and 105. 8/16/00 cb 124 At the end of the 2nd to last sentence on the 9/10/01 page, it would be helpful to xref Item 21's explanation that the data pointed to by a pointer in a const object is not automatically const. ! 2/10/00 ic 212 A class declaring no operator& function(s) 9/10/01 cxh 213 does NOT have them implicitly declared. Rather, 245 compilers use the built-in address-of operator 246 whenever "&" is applied to an object of that type. This behavior, in turn, is technically not an application of a global operator& function. Rather, it is a use of a built-in operator. I eliminated mention of operator& as an automatically generated function and adjusted the index to eliminate entries for the removed material. I also changed the definition of e1 from const Empty to Empty, because I no longer needed a const object to make my point. 7/ 3/00 ms 216 Saying that Lisp is "almost always interpreted" 9/10/01 is inaccurate. Change "these languages are almost always interpreted" to "these languages typically include interpreted components". ! 4/ 8/01 hs 228 The Standardization Committee has now ruled that 9/10/01 library implementers must declare string as defined in the Standard, so my comment about their being allowed to add extra parameters is incorrect. However, the Standard continues to forbid you from declaring string yourself. The advice in this Item stands (#include <string> instead of trying to declare the string type yourself), but the rationale for that advice is no longer valid.
The following changes were made for the twelfth printing of the book. These changes apply to your copy of Effective C++, Second Edition only if you have one of the first eleven printings.
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 9/21/01 pxm 6 I should clarify thatoperator+
is assumed to be 8/26/02 a friend ofString
, hence has access toString::data
. 8/26/02 sdm 24 Bad line break: "ty-pedefs" 8/26/02 7/ 6/01 ga 51 Regarding the para after the first code fragment, 8/26/02 the place pointed to by a and c will actually be deleted three times, because it will already have been deleted when b went out of scope. 7/ 6/01 ga 64 At end of 2nd para, "a twist" ==> 8/26/02 "an aspect to it". This avoids repeating the word "twist," which was used on the previous page. 7/ 6/01 ga 64 "freestanding functions" ==> "actual 8/26/02 functions in the object code" ! 1/22/02 fb 107 The information on this page is correct for 8/26/02 integral types, but for floating point types, it's not. For floating point types, the data on the "minimum possible value" in <limits>, <limits.h>, and <climits> is for the minimum representable positive number, e.g., both DBL_MIN and numeric_limits<double>::min() are greater than zero. For a floating point type FPT, the minimum representable value is typically -numeric_limits<FPT>::max(), though the Standard does not guarantee this. I reworded the discussion to be true and added a footnote to explain the meaning ofnumeric_limits<T>::min
whenT
is a floating point type. 7/ 6/01 ga 182 Another common synonym for layering is 8/26/02 "aggregation". 10/17/01 js 202 In the first para, the phrase "begs the question" 8/26/02 is improperly used. Reword. 7/ 6/01 ga 203 Twice on page, "PersonInfo::name" ==> 8/26/02 "PersonInfo::theName". ! 7/ 6/01 ga 226 In 2nd to last para, "string" isn't a template, 2/26/02 it's a typedef for a template instantiation.
The following changes were made for the fifteenth printing of the book. These changes apply to your copy of Effective C++, Second Edition only if you have one of the first fourteen printings.
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 10/12/02 bj 12 Clarify that the "ISO/ANSI sanctified version 10/ 8/03 of C" I refer to in the book is C89 or later (i.e., C89 or C99). 4/10/03 wk 77 The final bullet point on the page is misleading. 10/ 8/03 C++ imposes constraints on how copy constructors behave, so rather than writing that a copy constructor defines what it means to pass by value, I should say that it defines how an object is passed by value. (But see wk's 6/17/03 comment below regarding pathological cases where the copy constructor is bypassed in favor of a "copying constructor.") 10/20/02 kk 125 In final sentence of 2nd-to-last paragraph, 10/ 8/03 clarify that callers must use the array form of delete. 10/12/02 bj 135 In first para, note that the "C philosophy that 10/ 8/03 variables should be defined at the beginning of a block" is for C prior to C99. !10/12/02 bj Item 41 The first design problem includes the ability to 10/ 8/03 create "stacks of stacks of strings," but the given solution has a private copy constructor, making it impossible to create a Stack of Stacks. Rather than modify the code, I revised the spec :-) 8/13/03 mc 222 There is more to the Singleton pattern than I 10/ 8/03 describe in this Item. In particular, I make no mention of how to limit instantiation of a singleton class to one. As mc notes, "You can't spell 'singleton' without spelling 'single.'" I added a clarifying footnote.
The following changes were made for the sixteenth printing of the book. These changes apply to your copy of Effective C++, Second Edition only if you have one of the first fifteen printings.
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 4/ 4/04 sdm xiii Bad line break in URL in footnote. 6/ 7/04 5/20/04 md 107 At end of 3rd para, "SHRT" is in wrong font. 6/ 7/04 6/ 1/04 md 203 In last line, "theName" ==> "name". 6/ 7/04 6/ 7/04 sdm 203 Bad line break in 2nd-to-last para in 6/ 7/04 "valueDelimOpen". 6/ 2/04 md 218 In 2nd to last line, "accidently" ==> 6/ 7/04 "accidentally". Both spellings are correct but I use the "ally" form everywhere else in the book, so I should use it here, too. 6/30/04 mr 229 At bottom of page, "!." ==> "!" 6/30/04 1/19/04 jyt 231 Arrow tail protrudes slightly into the oval 6/ 7/04 surrounding "overflow_error".
The following changes were made for the seventeenth printing of the book. These changes apply to your copy of Effective C++, Second Edition only if you have one of the first sixteen printings.
DATE DATE REPORTED WHO PAGES WHAT FIXED -------- --- ----- ------------------------------------------------ -------- 5/16/04 md 77 "an intuitive semantics" ==> "intuitive 8/15/04 semantics" 5/16/04 md 83 "minimalness" ==> "minimality" 8/15/04 244 7/ 7/04 al 205 valueDelimOpen and valueDelimClose should be 8/15/04 declared private in MyPerson, because clients should not be able to call them. They're just an implementation detail of the class.
What follows are interesting comments about the material in Effective C++, Second Edition, but note that the book has now been superceded by Effective C++, Third Edition.
DATE REPORTED WHO PAGES WHAT -------- --- ------- ---------------------------------------------------------- 12/18/97 th xv Sir James Murray (first editor of the Oxford English Dictionary) predates Knuth in offering a reward to readers who bring errors to his attention. And Murray was dealing with thousands of pages of fine print, quotations from Old and Middle English, as well as numerous examples in foreign languages (some in non-Roman alphabets). 6/27/05 bxs 5 Line 2 ("number = -number;") won't work as expected on 2s complement machines if number is the smallest representable value. (In general, no arithmetic operations will behave as mathematically expected when variable values are outside the representable range.) 11/13/03 dfc 9, The code for the String class isn't exception safe. When 73 I wrote the book, I frankly wasn't concerned about exception safety, but I should have been. When I write the third edition of the book, I'll be careful to pay attention to exception safety throughout. 3/25/98 sdm Item 1 Many people have expressed concern that if ASPECT_RATIO is defined as a const, it will occupy memory, whereas if it's a macro, it won't. This need not be true. I'd expect any decent compiler to replace use of ASPECT_RATIO with its value and to optimize its storage out of existence. In other words, with a good compiler, use of a const is as space-efficient as use of a macro. 7/ 3/01 jcj Item 1 Regarding the argument that using a macro to represent a floating point literal (such as on page 13) is more space- efficient than using a const object, jcj writes: The [const] form is at least as efficient as the #define because when the preprocessor replaces ASPECT_RATIO with 1.653 in one's source code, that value must be stored somewhere in the binary machine code. Clearly the fact that one has a floating-point literal, in addition to the fact that I can think of no machines that have any instructions that take floating-point immediates, it is quite obvious that 99.9% of the time 1.653 will occupy some typically 64-bit space in memory that will be loaded and used just like a constant would. In fact, when you realize that the preprocessor and the compiler may not be very tightly coupled, the #define form would define a floating point literal at every point ASPECT_RATIO is used, where the const form would only have one instance in memory no matter how many uses there are. When you have consts that refer to types that may be allowed as immediates in the instruction set of the compiler, it is possible that the #define could be faster if the compiler did not optimize in the same way, but in general I'd be more worried about 50 floating-point literals peppered throughout my code than 1 constant used 50 times. 8/ 8/00 cb 15 Observes cb: It appears that the Microsoft Visual C++ 6.0 SP3 compiler requires the "enum hack" to work. Interesting, since it is stated that "Unless you're dealing with compilers of primarily historical interest (i.e., those written before 1995), you shouldn't have to use the enum hack." It appears that Visual C++ 6 was released in mid-1998. Sigh. 1/ 8/02 yy 15 The "enum hack" may be better than static const objects in two cases. The first is when compilers fail to optimize away the storage for the const object; enums occupy no storage. The second is if compilers generate code to initialize the value of the const object at runtime instead of at load time. Because enums occupy no storage, there is no storage to initialize. 10/11/97 ncm 15 Enumerants are in upper case, but this makes them 81 supceptible to replacement via macros. (On the other hand, 171 if an enumerant is macro-replaced by a constant, the enum definition itself should not compile.) 7/11/97 das Item 4 "I've never bought the "Look how dangerous it is to comment out code with /* */" justification for // comments, since the right way to do it in C is #if 0 ... #endif. My justification is that if you look at the middle of a large block of code commented out with #if 0 ... #endif (or /* ... */ for that matter), it isn't obvious that the code is commented out. It's much more apparent when every commented out line starts with //." 8/19/98 axt Item 4 You could mention the "#if 0, #endif" method of commenting out code. This method is convenient for commenting out chunks of code and, contrary to /* ... */, nesting is no problem. 6/20/01 cbs Items cbs points out that the code in Item 30 isn't thread safe, sdm 7,8,10 but the problem exists in Items 8 and 10, too. In fact, I fail to consider thread-safety throughout the book, something I can argue is excusable only because C++ itself fails to consider threading issues. You'll see other comments on how threading interact with my advice in various places in this errata list. If I ever write a third edition, I'll be sure to take threading issues into account. 7/ 6/01 ga Items operator new should include exception specs. This would 7-8 make parts of these Items easier to follow. On the other hand, if I use exception specs here, I'd need to use them in Items 9-10, too, and also on operator delete and possibly elsewhere. The book currently uses almost no exception specs, and perhaps it is more consistent that way. 1/19/98 mdr 32 Inheritance from NewHandlerSupport should really be private, because the lack of a virtual destructor in NewHandlerSupport means that deletion of an X object via a NewHandlerSupport<X>* pointer yields undefined behavior. The details are covered in my April 1998 article in the C/C++ Users Journal, but they're complicated, and I frankly don't know of a graceful way to integrate them into the existing text of the book. 6/24/98 sdm 33 "nothrow" new turns out to be less useful than it initially appears. Consider this statement: Widget *pw = new (nothrow) Widget; As Item 5 points out, two things happen here. First, the nothrow form of operator new is invoked to get enough memory for a Widget object. Second, a Widget constructor is invoked. Use of nothrow new guarantees that no exception will be thrown if operator new is unable to allocate the memory for the Widget, but if operator new succeeds, the Widget constructor will be invoked, and "nothrow" has no impact on that at all. In particular, the constructor is free to exit via an exception. This means that the statement above may yield an exception. If it does, the exception could not have come from the invocation of operator new that preceded the call to the Widget constructor. It may, however, have come from an invocation of a non-nothrow new used inside the Widget constructor. Bottom line: don't read "new (nothrow) Widget" as promosing that no exception will be thrown, because that's not what it means. 4/ 5/99 sdm 38-39 From the discussion on page 236, one might think that a using declaration could be used to avoid hiding the "normal" form of new, i.e., you might think this would solve the problem: class X { public: using ::operator new; ... }; Alas, this won't work, because using declarations within classes can bring only members of base classes into scope. Since ::operator new isn't a member of a base class of X, there's no way to employ a using declaration to avoid hiding the "normal" form of new. 10/ 6/06 txz 42 The call to ::operator new to "allocate a block of memory big enough to hold BLOCK_SIZE Airplane objects" should be to ::operator new[] instead, because we're really allocating memory for an array here. 6/17/98 sdm 46-47 Unlike the Pool class sketched on these pages, an industrial-strength Pool class would adhere to the conventions of allocators in the standard C++ library. You can find a description of such conventions, as well as an example showing how to give a Pool-like class a conforming interface, in section 19.4 of the 3rd edition of Bjarne Stroustrup's The C++ Programming Language. 11/25/97 kga Item 11 Only non-template constructors and assignment operators prevent the generation of the default versions of these functions. Hence, classes with member templates for constructors and assignment operators must still explicitly declare a copy constructor and an assignment operator for classes with pointers. (If this makes no sense to you, don't worry about it.) 6/ 9/98 sdm Item 11 It has been suggested that I add a destuctor to the list of functions that should be declared if a class dynamically allocates memory, but I've decided not to. If a destructor is omitted, the usual effect is a memory leak (see Item 6). However, if a copy constructor or an assignment operator is omitted, the usual result is undefined program behavior. These are qualitatively different situations. Furthermore, it often makes sense to declare, but not define, a copy constructor and/or assignment operator. This is never valid for a destructor. As Item 14 explains, a destructor must always be defined if it's declared. 12/10/03 sn Item 12 As Item 11 explains, bitwise copy is virtually always incorrect for pointer data members, but it is often simpler to "do the right thing" via assignment vis-a-vis initialization for pointer data members. So it may make sense to assign to pointer data members instead of initializing them. 9/19/98 axm 55-57 Another approach is: class DataMbrs { friend class ManyDataMbrs; private: DataMbrs(): a(1), b(1), c(1) {} int a, b, c; }; class ManyDataMbrs: private DataMbrs { public: ManyDataMbrs() { ... } ManyDataMbrs(const ManyDataMbrs& x) { ... } }; In other words, move the common constructor initialization list in ManyDataMbrs into a single constructor of a private base class, using friendship to make sure nobody except ManyDataMbrs has access to the members of the base class. Advantages of this approach: it is more efficient for non built-in types and users don't have to call init() inside each constructor. 12/28/04 mm 58 The code example is bad, because, as I point out in the footnote on page 8, initializing a std::string with a null pointer yields undefined behavior. 6/ 2/00 bk Item 14 Some base classes are designed for public derivation, but not for polymorphic use. This is the case for unary_function and binary_function in the standard library. For such base classes, a protected nonvirtual destructor may be more appropriate than a virtual destructor. 1/ 5/99 ivr 66 It turns out that "(i1 = i2) = i3;" yields undefined results when i1, i2, and i3 are ints, because i1 is modified more than once without an intervening sequence point. This is not a problem for user-defined types, because for such types, the above is equivalent to (i1.operator=(i2)).operator=(i3); and that's fine, because there's a sequence point before and after each function call. The question then arises: why is "i1 = i2 = i3;" allowed when i1, i2, and i3 are ints? A definitive answer was provided by Andrew Koenig in a 1/6/00 posting to comp.std.c++. 6/14/02 es 66 Regarding my comment after the second code example that I know of no practical use for things like (w1 = w2) = w3, es offers the following, which do strike me as reasonable: string str1; string str2("123456"); (str1 =str2).replace(2, 2, "abcdefgh"); (str1 +=str2).replace(5, 2, "XYZ"); 6/ 5/01 sdm 67 In the prose following the third code example, it's not technically true that the temporary is const. Rather, the temporary is an rvalue, and C++ forbids binding references to rvalues unless they are references to const. This is not a bug in the book. Rather, it is a deliberate attempt to spare you from having to know about rvalues, because they're more confusing than helpful, especially if you know the rules for rvalues in C. Setting aside technicalities, the information on this page is accurate in its essentials. 5/30/08 lxz 68 lxz writes: On the 2nd to last line of code, it says, "*ptr = *rhs.ptr;". The errata list (above) says to change this to "ptr = rhs.ptr". But doesn't this introduce a memory leak? What happens to the memory that was being pointed to by ptr? (I believe this issue is discussed on page 73.) Also, now you've got two pointers pointing to the same object. I think you have to do something this: delete ptr; // or should it be delete []ptr; ? ptr = new (T*)(char [sizeof(*rhs.ptr)/sizeof(char)]); *ptr = *rhs.ptr; Hopefully, T's overloaded assignment operator won't leave dangling pointers, etc. Does that sound right? It does. These days my preferred solution would be to use some kind of smart pointer instead of a raw pointer (i.e., change the type of ptr from T* to SomeKindOfSmartPtr<T>), because that would shift the problems onto the smart pointer class. 6/ 4/02 ai 72 ai writes: "In your description about programmers writing "a = a;" you say it's silly, but point out that it could be caused by reference aliasing. I've also noticed it done by programmers new to C++ to get rid of the "usused variable/parameter" warning." 1/19/04 jyt Items There are several example base classes in the book that 16, declare nonvirtual destructors or that declare no 22, destructor at all, a violation of Item 14. 26, 40,43, 49,50 2/17/03 dyx Items "Our programming guidelines now recommend implementing 16-17 assignment operators like this: T const& T::operator=( T other ) { swap( other ); // non-throwing operation return *this; } My reply: This is fine (except for the const return type :-}), as long as you make clear that this approach may be needlessly expensive. For example, if all your data members are of built in types, their assignments can't throw, so doing swaps instead of assignments just burns cycles needlessly. For large objects, you're talking about duplicating an object's contents on the heap in order to be able to perform the non-throwing swap. There's nothing wrong with that, but my experience has been that people advocating this approach to implementing operator= often overlook the cost that it incurs. It's exception safe, but it's often not cheap. 6/17/03 wk 77 There are pathological cases where pass by value is accomplished by instantiations of "copying constructors" rather than by the class' copy constructor. For details, consult the comp.lang.c++.moderated thread on the topic. 1/ 1/98 sdm Item 18 Anybody developing a real Array template (or a template for any other kind of container) should make sure the final product adheres to the conventions of the Standard Template Library (see page 232). 6/22/01 sdm Item 19 The bulleted summary at the end of this Item no longer reflects my full thinking on this topic. For an updated version of the summary as well as an explanation of how and why things changed, please read my February 2000 article, "How Non-Member Functions Improve Encapsulation." 2/19/01 sdm Item 19 When templates enter the picture, things become more complicated. Compilers must not only perform type conversions on actual parameters, they must also perform template type deduction to determine the types of formal parameters. The end result is that templates for functions like operator+ should still be non-members, but they need to be defined as friends inside the class they work with in order to be instantiated correctly. 12/30/02 ma Item 19 It's true that virtual functions must be members, but one can get virtual-acting behavior by having a non-virtual function (possibly a non-member) call a virtual function. This can be especially useful for functions like operator<<, which must be non-members but can be made to act virtual by internally calling a virtual function, e.g., one named "print" or "write". 2/17/03 dys Item 19 Regarding the last bullet on page 88, dys writes: The same rule should be used for other operators that require a different class as a left-hand operand. For example, CORBA uses "Any <<= Type", and for all classes besides Any, operator<<=(T) is a non-member. I propose the following change to your algorithm. Instead of else if (f is operator>> or operator<<) write else if (f is an operator and needs another class as its left-hand operand) 11/ 3/97 dqs Item 20 "When writing code for parallel execution in a multi-threaded environment it is important to know what code can modify what areas of memory. If member data of a class is made public, a critical section of code for that area of memory could be anywhere. As the creator of the class I have no way to ensure that the memory is referenced only under the appropriate access controls. On the other hand, if the member data is made private, I know that all of the critical sections are within the methods of the class. If I ensure that these methods only reference the member data under the appropriate access controls then I can ensure the class is safe to use in a multi-threaded environment. This also holds for returning pointers or references to private data. Returning such a value allows someone to reference my object's memory without using the appropriate access controls." 9/17/00 ch Item 20 Notes ch: Objects are state machines. An Object would lose control of its own states if you allow public data members. This makes it for instance impossible to implement a typical Observer Pattern in an object oriented way. The problems get really nasty for objects with public data members when living in a multithreaded environment .... 9/ 6/00 bxp Item 21 bxp makes the following quite legitimate observation: [Item 21 has] a long discussion on const functions, returning const object values, etc, but a very important approach is missing: const locals. Any local variable you mean not to change later on should be declared const. It is highly documenting, helps the compiler, and helps to avoid bugs too. 6/29/05 bxs Item 21 One way to remember what const refers to when pointers are involved is to read the declarations right to left: char *p // ptr to char const char *p // ptr to char that is const char const *p // ptr to constant char char *const p // constant ptr to char const char * const p // constant ptr to char that is const char const * const p // constant ptr to constant char 4/16/99 sdm 93 Several people have asked why the const version of String::operator[] returns a const char& instead of a char. It's because returning a char would prevent users from taking the address of the result. In other words, if the const operator[] returned a char, this would be illegal, const String s; ... const char *p = &s[5]; // error -- can't take address // of a built-in type returned // by value but this would be legal: String s; ... char *p = &s[5]; // fine -- taking the address of // the char referred to by // operator[]'s return value I don't like that kind of inconsistency. Also, return by reference generalizes much better to other types. 12/21/97 wbr Item 22 Another reason to prefer pass-by-reference is that it works even when the copy constructor is not public. Item 27 gives an example of when this might be the case. 9/10/01 lz 97 If you are using a library with a const-incorrect function prototype such as that for strlen on this page, there is a solution better than using a cast at every point in the program where you call the incorrectly-declared function: write a wrapper function to perform the cast, then call the wrapper. In the example on this page, the wrapper function would look like this: inline strlen(const char *s) { return strlen(const_cast<char*>(s); } 6/11/00 tm 99 In general, it's dangerous for a function to return a 9/18/03 ss reference to a parameter passed by reference-to-const, because the parameter may have been bound to a temporary object that will usually be destroyed at the end of the call. For details, consult FAQ 32.08 of C++ Faqs by Cline, Lomow, and Girou. 2/27/01 ph Item 23 Once you've resigned yourself to returning a new object from functions like operator*, you'll naturally look for ways to make that as cheap as possible. One way to do that is to return a pointer posing as an object. The following comment was sent regarding Item 20 of More Effective C++, but it's relevant to Item 23 of Effective C++, too: 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 [which is the subject of MEC++ Item 20]. 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. 7/ 1/03 sv 108 The last sentence says you must use overloading, but if you are willing to change the API, you have other choices, e.g., you could pass a vector of values. 11/23/97 sdm 111-2 With the conventional definition of NULL as 0, it's legal to write "if (NULL) ... ". This isn't legal with my NULL, because it's ambiguous how to convert from NULL to bool. I'm not going to lose sleep over this, because it's hard to imagine that NULL is used as a condition in much code. 7/10/00 sdm 111-2 Const objects of non-static storage duration that lack 6/26/08 lfr default constructors must be explicitly initialized, even if they contain no data members. As a result, in the remote chance that a NULL object is declared with non-static storage duration (e.g., a local object), the code on these pages should not compile. Because any definition of a NULL object is likely to be at global or file scope (hence of static storage duration), it's unlikely this would be a problem in practice. 1/14/98 sdm Item 29 Item 29 subsumes Item 30. Item 30 9/26/02 mh 127 It might be better if someFamousAuthor returned a const String instead of just a String. This would be consistent with the advice of Item 21. However, the decision as to whether to declare the return value const depends on how callers are likely to use the return value. Some functions return values that clients may well want to modify. 6/15/02 sdm Item 33 Randy Meyers (no relation) has a nice article on inlining in C99, including an explanation of how the C99 rules differ from those in C++, in the July 2002 issue of the C/C++ Users Journal. 7/11/97 das 137 "I don't buy the pathological paging behavior argument, since you still have locality of references. I recall an IEEE Transactions on Software Enginering paper about seven years ago that measured the effects of inlining code bloat in an effort to verify or refute some folk wisdom about thrashing. I think they said it's not a problem." 8/26/02 sdm 149 What I call a "Protocol class" is now almost universally known (especially outside the C++ community) as an Interface. Many languages (notably Java and C#) provide language support for them. 5/19/03 sdm Item 34 Because it's not possible to declare nested classes without defining the class in which they are nested, nested classes can lead to unnecessary compilation dependencies. An alternative design is to unnest the class and include both classes in the same namespace. However, this is viable only when the nested class is public, because namespaces offer no encapsulation. 12/ 6/99 mp Item 34 The use of a factory function to construct a class implies that the "new" happens off the same heap, which is a bad assumption when dealing with libraries (especially dynamic link libraries). This is why I prefer your first recommendation of using the private implementation: class PersonImpl; class Person { ... private: PersonImpl *imp; }; While you do pay a redirection penalty when accessing implementation data in the class, the following more natural code works regardless of the heap situation: Person *pp = new Person(... ); ... delete ppl; AND it allows stack-based or compiler-generated constructors (especially useful with operators). Basically the "imp" pointer is allocated off the calling scope's heap, and the PersonImpl data is allocated off the library's heap. 7/11/97 das Item 35 "A related true story: The professor for the University of Utah's general intro astronomy class, at the first opportunity in the term, tells his class "I'm going to tell you a question that will be on the final, and I'll tell you the correct answer to that question. The question is `Can the Moon be seen during the day?' and the answer is yes. Now, everyone come outside." He takes his class outside, points up to the Moon, and says "See, it's daytime, and there's the Moon." On the final, he asks "Can the Moon be seen during the day?" Usually about a third of the class answers no." 10/13/02 ya Item 36 ya writes: "Nonvirtual functions may call other functions which are virtual. In that case, derived classes are indeed presented with mandadory implementation, but only in the highest, close-to-the-surface level. By overriding virtual functions, the overall behavior of the nonvirtual function can change in derived classes. Such usage of nonvirtuals is very useful and is the basis for the 'template method' design pattern." This is true, but it's important to note that the externally observable behavior of any function is defined by its specification (i.e., it's interface), not by its implementation. A nonvirtual implemented using template method may behave differently for different derived classes, but its behavior is still bounded by its specification. Callers don't care whether the function is virtual or nonvirtual. All they care about is that the function they call exhibits the behavior its specification promises. For implementers of derived classes, it's a different story. They care very much about whether a base class function is virtual or nonvirtual, because that affects what they are allowed to vary. In Item 36, my remarks are focused on the relationship between authors of base and derived classes, not on the relationship between authors of a class and clients of that class. 6/11/03 sf Item 38 A drawback to the advice in this Item is that callers going through the derived class interface must specify all parameter values; the default parameter values apply only via the base class interface. An alternative design is to respecify the (same) default value in each derived class, but then if the default is changed in the base class, all derived classes must be updated with the new value, too. 7/21/04 am 179-180 Another way to deal with the need to downcast if you can't redefine allAccounts or BankAccount is to declare something like NewBankAccount as a sibling class to SavingsAccount and CheckingAccount, then adopt a policy of deriving new classes only from NewBankAccount. You'll still have to downcast from BankAccount to Savings-, Checking-, or NewBankAccount, but once you're in NewBankAcount, you can use a virtual creditInterest. 8/30/15 lfr 184 In Set<T>::remove, "list<T>::iterator it" ==> "typename list<T>::iterator it". 12/18/97 rhs 190 In the second-to-last paragraph, I summarize when to use layering and when to use private inheritance, but that summary doesn't tell the whole story. Sometimes private inheritance is preferable to layering, even though neither protected members nor virtual functions are involved. Nathan Myers, for example, has described why it can be better to inherit from an empty class than to be layered on top of an empty object. For details, see Nathan's article on the topic. 11/12/99 dh 190 In the second-to-last paragraph, I fail to explain why I 11/15/99 jxg prefer layering to private inheritance. My reason is simple (I think layering is a lot easier to understand), but jxg posted additional reasons in a posting to comp.lang.c++.moderated: - [Private inheritance] pollutes the class's scope with names. The name of the private base plus the names of its members are injected into the derived class. They may hide global names there, yielding unexpected 'XXX not accessible' compilation errors (particularly in classes derived from ours). One example: in derived classes you cannot refer to the base class (supposedly an implementation detail subject to change) with an unqualified-id anymore. Global functions and objects may be hidden (and reported as inaccessible) even by private members of the private base. Unexpected ambiguities with other bases may arise. So changing the implementation might break client code. - Even more detrimental effects occur in the classic case of using private inheritance: to override a virtual function. If the private base contains virtual functions they become part of our class's interface and could be overridden by derived classes. This is often not what was intended (we inherit _private_ly after all). If feasible, I would effect such overrides by containing a private nested class nowadays. 8/13/03 mc Item 43 Slightly edited, mc writes: "You are not completely fair in this Item. The trick with auxillary classes is needed in order to allow overloading of any functionalities involved in the name clash, but the clumsiness and the lack of virtual behavior are not valid arguments. Explicit qualification is clumsy, but so is explicit upcasting of pointers (it takes more than one line, compare upper page 196 with middle page 197). So clumsiness is not the argument here. Moreover, calling pls->draw(); on page 197 would still be an error just as on page 196, so don't bad-mouth the first approach on that account. Reversely, applying the pointer upcast trick on page 196 instead would conserve the virtual behavior. So the only argument left is that only one of the methods can be overloaded, the other will be lost (this is reason enough to apply the technique you offer). In the end, there's no error in your text, but I think that the code and the formulation makes the reader focus on the wrong aspects of the problem." 5/13/98 lc Item 47 Using a function-local static object to guarantee initialization of non-local static objects isn't thread safe. This is true, but adding thread safety is hard, especially if you also want to prevent resource leaks. For details, consult John Vlisside's June 1996 C++ Report article. It discusses this and related issues. 10/31/98 sdm 202-205 In his column in the October 1998 C++ Report, Herb Sutter describes an alternative design that allows MyPerson to redefine PersonInfo's virtual functions without actually inheriting from PersonInfo (though inheritance is involved elsewhere). Note that though this discussion takes place in my Item devoted to MI, this particular aspect of the design is not in any way dependent on MI. ! 7/13/00 ykc 207 This design can't work, because code like this, CartoonCharacter *pc = new Cricket; is ambiguous: Cricket inherits from CartoonCharacter twice (once directly, once through Grasshopper).
Who's who:
das = David Smallberg ds = Daniel Steinberg apm = Arunprasad P. Marathe dxs = Doug Stapp ncm = Nathan Myers rh = Robert Hall gb = Gary Bartlett mt = Michael Tamm kb = Kendall Beaman kd = Keith Derrick dqs = Dave Schneider jg = Joe Gottman max = Max Hailperin rmw = Richard Weeks vb = Valentin Bonnard kga = Klaus-Georg Adams jh = Jun He tk = Tim King en = Eric Nagler th = Ted Hill dm = Don Maier rhs = Bobby Schmidt mgh = Mark Harrison wbr = William Rubin mmr = Michael Rubenstein mdr = Mark Rodgers dxg = David Goh bc = Brenton Cooper lc = Laurent Chardonnens atc = Andy Thomas-Cramer axt = Antoine Trux axm = Alex Marmer jww = John Wait bs = Brian Sharon lf = Liam Fitzpatrick gy = Gary Yee joh = John O'Hanley bp = Brady Patterson cp = Christopher Peterson da = Darin Adler fk = Feliks Kluzniak mp = Mark Pietras dh = Dave Harris jxg = Jörg Barfurth ivr = Igor Rafienko id = Isi Dunietz bm = Bernd Mohr bk = Bill Kempf ccr = Christopher Creutzi ms = Mark Stickel ykc = Yi-Kan Cheng ch = Claus Hoeltzcke cb = Clay Budin cbs = ChangBae Suh ic = Ian Cooper cxh = Carl Harris pm = Panayotis Matsinopoulos ph = P. Haution tm = Tomasz Muldner hs = Herb Sutter bxp = Balog Pal lz = Leor Zolman ga = Giulio Agostini pxm = Pajo Misljencevic js = Jimmy Snyder jcj = Jeffrey Jacobs fb = Fredrik Blomqvist ai = Aaron Isaksen es = Eugene Surman yy = Yang Yinghua wk = Witold Kuzminski mh = Matthias Hofmann ya = Yuval Aharoni kk = Kazunobu Kuriyama ma = Michael Anderson bj = Byrial Jensen dys = Dirk Schreib sf = Shmulik Flint sv = Suzanne Vogel ss = Sachin Shenoy mc = Michael Christensen dfc = Diego Funes sn = Santha Nirmala jyt = Jorge Yáñez Teruel md = Mark Davis mr = Marty Rabinowitz al = Ares Lagae am = Alexander Medvedev mm = Marlene Miller bxs = Balbir Singh txz = Tal Zur lxz = Lyle Ziegelmiller lfr = Fraser Ross