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