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 that operator+ is assumed to be 8/26/02
a friend of String, hence has access to
String::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 of
numeric_limits<T>::min when T 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

