Vous êtes sur la page 1sur 20

4/30/12

www.horstmann.com/cpp/pitfalls.html

C++ Pitfalls
Cay S. Horstmann
Department of Computer Science
San Jose State University San Jose, CA 95192-0249
cyx0osmn.o a\4hrtancm

Copyright (C) Cay S. Horstmann 1997

What is a pitfall?
C++ code that compiles links runs does something different than you expect Example:
i (05< x< 05 rtr 0 f -. = = .) eun ;

Pitfall:
i (05< x< 05 rtr 0 f -. = = .) eun ;

This expression doesnot test the mathematical condition


-. < x< 15 15 = = .

Instead, it first computes - . < x which is 0 or 1, and then compares the result with 0.5. 05 = , Moral: Even though C++ now has a b o type, Booleans are still freely convertible to i t ol n. Since b o - i tis allowed as a conversion, the compiler cannot check the validity of expressions. In contrast, ol>n the Java compiler would flag this statement as an error.

Constructor pitfalls
www.horstmann.com/cpp/pitfalls.html 1/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

Example:
itmi( n an) { srn a"el"; tig (Hlo) srn b) tig (; srn c=srn(Wrd) tig tig"ol"; / .. / . rtr 0 eun ; }

Pitfall:
srn b) tig (;

This expression does not construct an object bof type s r n . Instead, it is the prototype for a function bwith tig no arguments and return type s r n . tig Moral: Remember to omit the ( )when invoking the default constructor. The C feature of declaring a function in a local scope is worthless since it lies about the true scope. Most programmers place all prototypes in header files. But even a worthless feature that you never use can haunt you. Example:
tmlt<yeaeT epaetpnm > casAry ls ra { pbi: ulc Aryitsz) ra(n ie; T oeao[(n) & prtr]it; AryT&oeao=cntAryT&; ra<> prtr(os ra<>) / .. / . } ; itmi( n an) { Arydul>a1) ra<obe (0; a0 =0 a1 =1 a2 =4 [] ; [] ; [] ; a3 =9 a4 =1; [] ; [] 6 a5 =2;a=3;a7 =4; [] 5 6 [] 9 a8 =6;a9 =8; [] 4 [] 1 / .. / . rtr 0 eun ; }

Pitfall:
a=3; 6

Surprisingly, it compiles:
a=Arydul>3) ra<obe(6;
www.horstmann.com/cpp/pitfalls.html 2/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

ais replaced

with a new array of 36 numbers.

Moral: Constructors with one argument serve double duty as type conversions. Avoid constructors with a single integer argument! Use the e p i i keyword if you can't avoid them. xlct Example:
tmlt<yeaeT epaetpnm > casAry ls ra { pbi: ulc epii Aryitsz) xlct ra(n ie; / .. / . piae rvt: T _aa * dt; it_ie n sz; } ; tmlt<yeaeT epaetpnm > AryT:Aryitsz) ra<>:ra(n ie : _iesz) sz(ie, _aanwTsz) dt(e (ie) { } itmi( n an) { Arydul>a1) ra<obe (0; a1 =6;/ pormcahs [] 4 / rga rse / .. / . }

Pitfall:
tmlt<yeaeT epaetpnm > AryT:Aryitsz) ra<>:ra(n ie : _iesz) sz(ie, _aanwTsz) / sol hv be nwTsz] dt(e (ie) / hud ae en e [ie { }

Why did it compile?


nwTsz) e (ie

returns a T pointer to a single element of type T constructed from the integer s z . * , ie


nwTsz] e [ie

returns a T pointer to an array of s z objects of type T constructed with the default constructor. * ie , Moral: Array/pointer duality is dumb, but unfortunately pervasive in C and C++. The Java compiler would catch this--Java, like most programming languages, supports genuine array types.
www.horstmann.com/cpp/pitfalls.html 3/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

Example:
tmlt<yeaeT epaetpnm > casAry ls ra { pbi: ulc epii Aryitsz) xlct ra(n ie; / .. / . piae rvt: T _aa * dt; it_aaiy n cpct; it_ie n sz; } ; tmlt<yeaeT epaetpnm > AryT:Aryitsz) ra<>:ra(n ie : _iesz) sz(ie, _aaiy_ie+1) cpct(sz 0, _aanwT_aaiy) dt(e [cpct] { }

itmi( n an) { Aryit a10; ra<n> (0) ... / pormsat atn fay / rga trs cig lk }

Pitfall:
AryT:Aryitsz) ra<>:ra(n ie : _iesz) sz(ie, _aaiysz +1) cpct(ie 0, _aanwT_aaiy) dt(e [cpct] { }

Initialization follows the member declaration order, not the initializer order!
AryT:Aryitsz) ra<>:ra(n ie : _aanwT_aaiy) dt(e [cpct] _aaiy_ie+1) cpct(sz 0, _iesz) sz(ie,

Tip: Do not use data members in initializer expressions.


AryT:Aryitsz) ra<>:ra(n ie : _aanwTsz +1] dt(e [ie 0) _aaiysz +1) cpct(ie 0, _iesz) sz(ie,

Example:
casPit ls on
www.horstmann.com/cpp/pitfalls.html 4/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

{ pbi: ulc Pitdul x=0 dul y=0; on(obe , obe ) / .. / . piae rvt: dul _,_; obe x y } ; itmi( n an) { dul a r x y obe , , , ; / .. / . Pitp=( +r*csa,y+r*sna) on x o() i(); / .. / . rtr 0 eun ; }

Pitfall:
Pitp=( +r*csa,y+r*sna) on x o() i();

This should be either


Pitpx+r*csa,y+r*sna) on ( o() i();

or
Pitp=Pitx+r*csa,y+r*sna) on on( o() i();

The expression
( +r*csa,y+r*sna) x o() i()

has a legal meaning. The comma operator discards x + r * c s a and evaluatesy + r * s n a . The o() i()
Pitdul x=0 dul y=0 on(obe , obe )

constructor makes a P i t y + r * s n a , 0 . on( i() ) Moral: Default arguments can lead to unintended calls. In our case, the constructionP i t d u l )is not on(obe reasonable, but the constructionP i t )is. Only use defaults if all resulting call patterns are meaningful. on( Example:
casSae ls hp { pbi: ulc Sae) hp(; piae rvt: vrulvi rst) ita od ee(; Clr_oo; oo clr } ; casPit:pbi Sae ls on ulc hp {
www.horstmann.com/cpp/pitfalls.html 5/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

pbi: ulc / .. / . piae rvt: dul _,_; obe x y } ; vi Sae:ee( {_oo =BAK } od hp:rst) clr LC; vi Pit:ee( od on:rst) { Sae:ee(; hp:rst) _ =0 _ =0 x ; y ; } Sae:hp( {rst) } hp:Sae) ee(;

There is noP i tconstructor--we use the virtual function in the Shape constructor. on Pitfall:
Sae:hp( {rst) } hp:Sae) ee(; Pitp on ;

When constructingP i t the S a e : e e ( , not the P i t : e e ( virtual function is called. Why? on, hp:rst) on:rst) Explanation: Virtual functions do not work in constructors. TheS a esubobject is constructed before theP i tobject. Inside the S a econstructor, the partially hp on hp constructed object is still a S a e hp. Example:
casSae/ a asrc cas ls hp / n btat ls { pbi: ulc Sae) hp(; piae rvt: vi ii(; od nt) vrulvi rst)=0 ita od ee( ; Clr_oo; oo clr } ; Sae:hp( {ii(;} hp:Sae) nt) vi Sae:nt){rst) } od hp:ii( ee(; casPit:pbi Sae/ acnrt drvdcas ls on ulc hp / ocee eie ls { pbi: ulc vrulvi rst) ita od ee(; / .. / . piae rvt: dul _,_; obe x y } ; vi Pit:ee( {_ =_ =0 } od on:rst) x y ;
www.horstmann.com/cpp/pitfalls.html 6/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

Pitfall:
itmi( n an) { Pitp / pormcahs on ; / rga rse rtr 0 eun ; }

Explanation: You cannot create an instance of an abstract class (a class with a pure, = 0 virtual function). ,
Saes / cmietm err Saei asrc hp ; / opl-ie ro; hp s btat

That's a good thing: if you could, what would happen if you called
srst) / rstntdfndfrsae .ee(; / ee o eie o hps

But...I lied. You can create instances of abstract classes. When constructing a concrete derived, for a fleeting moment, the base class exists. If you invoke a pure virtual function before the derived class constructor has executed, the program terminates.

Destructor pitfalls
Example:
casEpoe ls mlye { pbi: ulc Epoe(tignm) mlyesrn ae; vrulvi pit)cnt ita od rn( os; piae rvt: srn _ae tig nm; } ; casMngr:pbi Epoe ls aae ulc mlye { pbi: ulc Mngrsrn nm,srn dp) aae(tig ae tig et; vrulvi pit)cnt ita od rn( os; piae rvt: srn _et tig dp; } ; itmi( n an) { Epoe*saf1] mlye tf[0; saf0 =nwEpoe(HryHce"; tf[] e mlye"ar akr) saf1 =nwMngr"o Sih,"ae"; tf[] e aae(Je mt" Sls) / .. / . fr(n i=0 i<1;i+ o it ; 0 +) safi-pit) tf[]>rn(; fr(n i=0 i<1;i+ o it ; 0 +) dlt safi; eee tf[]
www.horstmann.com/cpp/pitfalls.html 7/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

rtr 0 eun ; }

Where is the memory leak? Pitfall:


dlt safi; eee tf[]

destroys all objects with~ m l y e ) The_ e tstrings of the M n g robjects are never destroyed. Epoe(. dp aae Moral: A class from which you derive must have a virtual destructor. Example:
casEpoe ls mlye { pbi: ulc Epoe(tignm) mlyesrn ae; vrulvi pit)cnt ita od rn( os; vrul~mlye) / <-ita Epoe(; / --piae rvt: srn _ae tig nm; } ; casEpoe ls mlye { pbi: ulc Epoe(tignm) mlyesrn ae; piae rvt: srn _ae tig nm; } ; casMngr ls aae { pbi: ulc Mngrsrn nm,srn sae; aae(tig ae tig nm) ~aae(; Mngr) piae rvt: Epoe*_ertr; mlye sceay } Mngr:aae(tignm,srn sae aae:Mngrsrn ae tig nm) : Epoe(ae, mlyenm) _ertr(e Epoe(nm) sceaynw mlyesae) { } Mngr:Mngr){dlt _ertr;} aae:~aae( eee sceay

What is wrong with the M n g rclass? aae Pitfall:


itmi( n an)
www.horstmann.com/cpp/pitfalls.html 8/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

{ Mngrm =Mngr"al Sih, aae 1 aae(Sly mt" "o Bre"; Je ans) Mngrm =m; aae 2 1 / .. / . }

The destructors of both m and m will delete the same E p o e object. 1 2 mlye Moral: A class with a destructor needs a copy constructor
Mngr:aae(os Mngr) aae:Mngrcnt aae&

and an assignment operator


Mngr Mngr:prtr(os Mngr) aae& aae:oeao=cnt aae&.

The Big 3: It's not just a good idea--it's the law (Marshall Cline)

Inheritance pitfalls
Example:
casEpoe ls mlye { pbi: ulc Epoe(tignm,srn dp) mlyesrn ae tig et; vrulvi pit)cnt ita od rn( os; srn dp( cnt tig et) os; piae rvt: srn _ae tig nm; srn _et tig dp; } ; casMngr:pbi Epoe ls aae ulc mlye { pbi: ulc Mngrsrn nm,srn dp) aae(tig ae tig et; vrulvi pit)cnt ita od rn( os; piae rvt: / .. / . } ; vi Epoe:pit)cnt od mlye:rn( os { cu < _ae< ed; ot < nm < nl } vi Mngr:rn( cnt od aae:pit) os { pit) / pitbs cas rn(; / rn ae ls cu < dp( < ed; ot < et) < nl }

Pitfall:
vi Mngr:rn( cnt od aae:pit) os
www.horstmann.com/cpp/pitfalls.html 9/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

{ pit) / pitbs cas rn(; / rn ae ls cu < dp( < ed; ot < et) < nl }

Despite what the comment says,p i t )selects the print operation of theM n g rclass. In contrast, d p ( rn( aae et) selects the operation of theE p o e class sinceM n g rdoes not redefine it. mlye aae Moral: When calling a base class operation in a derived class operation of the same name, use scope resolution:
vi Mngr:rn( cnt od aae:pit) os { Epoe:pit) / pitbs cas mlye:rn(; / rn ae ls cu < dp( < ed; ot < et) < nl }

Example:
vi Mngr:rn( cnt od aae:pit) os { Epoe:rn(;/ pitbs cas mlyepit) / rn ae ls cu < dp( < ed; ot < et) < nl }

Pitfall:
Epoe:rn(; mlyepit)

It should be
Epoe:pit) mlye:rn(;

But why does it compile?E p o e :is a goto label! mlye Moral:Even language features that you never use can bite you! Example:
casEpoe ls mlye { pbi: ulc vi rieslr(obeb_ecn) od as_aaydul ypret; / .. / . } ; casMngr:pbi Epoe ls aae ulc mlye { pbi: ulc / .. / . } ; vi mk_hmhpyEpoe*e itn) od aete_ap(mlye , n e { fr(n i=0 i<n;i+ o it ; e +) ei.as_aay01) []rieslr(.0; }
www.horstmann.com/cpp/pitfalls.html 10/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

itmi( n an) { Epoe e2] mlye [0; Mngrm5; aae [] m0 =Mngr"o Bs" "ae"; [] aae(Je uh, Sls) / .. / . mk_hmhpye 2) aete_ap(, 0; mk_hmhpym+1 4;/ ltssi Je aete_ap( , ) / e' kp o rtr 0 eun ; }

Pitfall:
vi mk_hmhpyEpoe*e itn) od aete_ap(mlye , n e; Mngrm5; aae [] mk_hmhpym+1 4; aete_ap( , )

Why does it compile? The type of m + 1isM n g r .Because of inheritance, a M n g r is convertible to an E p o e *base aae* aae* mlye class pointer. m k _ h m h p y receives anE p o e * Everyone is happy. aete_ap mlye. What is the problem? The array computation e i computes an offset of i s z o ( m l y e . [] *iefEpoe) Moral: Pointers are overused in C++. Here we see two interpretations of anE p o e * e mlye . 1. epoints to either anE p o e or a derived class object, such as a M n g r mlye aae. 2. epoints to either anE p o e or a bunch ofE p o e objects, stacked up in an array. mlye mlye These two interpretations are incompatible. Mixing them leads to runtime errors. However, the intention of the programmer is hidden to the compiler since both ideas are expressed by the same construct--a pointer. Example:
casEpoe ls mlye { pbi: ulc Epoe(hrnm[) mlyeca ae]; Epoe(os Epoe&b; mlyecnt mlye ) ~mlye) Epoe(; Epoe&oeao=cntEpoe&b; mlye prtr(os mlye ) ... piae rvt: ca*_ae hr nm; } ; casMngr:pbi Epoe ls aae ulc mlye { pbi: ulc Mngrca nm[,ca dp[) aae(hr ae] hr et]; MngrcntMngr b; aae(os aae& ) ~aae(; Mngr)
www.horstmann.com/cpp/pitfalls.html 11/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

Mngr oeao=cntMngr b; aae& prtr(os aae& ) ... piae rvt: ca*_et hr dp; } ; Mngr:aae(os Mngr b aae:Mngrcnt aae& ) :_etnwca[tlnb_et +1) dp(e hrsre(.dp) ] { srp(.dp,_et; tcyb_et dp) } Mngr:Mngr) aae:~aae( { dlt[ _et eee] dp; } Mngr Mngr:prtr(os Mngr b aae& aae:oeao=cnt aae& ) { i (hs= &)rtr *hs f ti = b eun ti; dlt[ _et eee] dp; _et=nwca[tlnb_et +1; dp e hrsre(.dp) ] srp(.dp,_et; tcyb_et dp) rtr *hs eun ti; }

Pitfall:
Mngr Mngr:prtr(os Mngr b aae& aae:oeao=cnt aae& ) { i (hs= &)rtr *hs f ti = b eun ti; dlt[ _et eee] dp; _et=nwca[tlnb_et +1; dp e hrsre(.dp) ] srp(.dp,_et; tcyb_et dp) rtr *hs eun ti; }

Constructors and destructors automatically call the base constructors and destructors. But o e a o =does not prtr automatically invoke the o e a o =of the base class. prtr Moral: When redefining o e a o =in a derived class, explicitly call o e a o =of the base class: prtr prtr
Mngr Mngr:prtr(os Mngr b aae& aae:oeao=cnt aae& ) { i (hs= &)rtr *hs f ti = b eun ti; Epoe:oeao=b; mlye:prtr() dlt[ _et eee] dp; _et=nwca[tlnb_et +1; dp e hrsre(.dp) ] srp(.dp,_et; tcyb_et dp) rtr *hs eun ti; }

Thanks to Johann Deneux for pointing out another pitfall: The copy constructor for M n g ris wrong. It wants aae to call the default constructor for E p o e , but there isn't one. And, of course, it wouldn't be appropriate to mlye call it if there was one. A corrected version is
Mngr:aae(os Mngr b aae:Mngrcnt aae& )
www.horstmann.com/cpp/pitfalls.html 12/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

:Epoe(ti) _etnwca[tlnb_et +1) mlye*hs, dp(e hrsre(.dp) ] { srp(.dp,_et; tcyb_et dp) }

Stream pitfalls
Example:
ls<n>a itit ; wie(cnef) hl !i.o() { itx n ; cn> x i > ; i (cnef) aps_akx; f !i.o() .uhbc() }

Pitfall:
wie(cnef) hl !i.o() { / .. / . }

This may be an infinite loop. If the stream state turns to fail, the end of file will never be reached. The stream state will be set to fail if a non-digit is encountered when trying to read an integer. Moral: e f )is only useful in combination with f i ( , to find out whether EOF was the cause for failure o( al) Example:
wie(i.od) hl cngo() { itx n ; cn> x i > ; i (i.od) aps_akx; f cngo() .uhbc() }

Pitfall:
cn> x / <- myscedadte ecutrEF i > ; / -- a uce n hn none O i (i.od) aps_akx; f cngo() .uhbc()

This codemay miss the last element in the input file, if it is directly followed by EOF. Remedy: Use f i ( : al)
wie(cnfi() hl !i.al) { itx n ; cn> x i > ; i (cnfi()aps_akx; f !i.al) .uhbc() }

The type conversion b s c i s - - > v i *is identical to ! a l ) ai_o -- od fi(:


www.horstmann.com/cpp/pitfalls.html 13/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

wie(i) hl cn { itx n ; cn> x i > ; i (i)aps_akx; f cn .uhbc() }

Moral: There are four stream test functions:g o ( , b d ) e f ) andf i ( . (Note thatb d )doesnot od) a(, o(, al) a( mean ! o d ) Only one of them is useful: f i ( . g o ( .) al)

Overloading pitfalls
Example:
casCmlx ls ope { pbi: ulc Cmlxdul =0 dul =0; ope(obe , obe ) Cmlxoeao+Cmlxb cnt ope prtr(ope ) os; Cmlxoeao-Cmlxb cnt ope prtr(ope ) os; Cmlxoeao*Cmlxb cnt ope prtr(ope ) os; Cmlxoeao/Cmlxb cnt ope prtr(ope ) os; Cmlxoeao^Cmlxb cnt ope prtr(ope ) os; / .. / . piae rvt: dul _e _m obe r, i; } ; itmi( n an) { Cmlxi0 1; ope (, ) cu < i2+1 / ii i ot < ^ ; / * s 1 rtr 0 eun ; }

Why won't it print (0,0)? Pitfall:


cu < i2+1 ot < ^ ;

Using the C/C++ operator precedence rules, we can add parentheses:


cu < ( ^( +1) ot < i 2 );

The ^operator is weaker than +(but stronger than < ). < Moral: You cannot change the operator precedence when overloading operators. Do not overload an operator if its precedence is not intuitive for the problem domain. The precedence of ^is fine for XOR but not for raising to a power. Example: The stream classes support a type conversion b s c i s - - > v i * for testing if a stream is ai_o -- od
www.horstmann.com/cpp/pitfalls.html 14/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

happy:
wie(i) hl cn { itx n ; cn> x i > ; / .. / . }

Why convert to v i * A conversion to b o would seem to make more sense. od? ol


tmlt<yeaeC tpnm T=ca_risC > epaetpnm , yeae hrtat<> casbscis ls ai_o { pbi: ulc / .. / . oeao bo( cnt prtr ol) os { i (al) rtr fle f fi() eun as; es rtr tu; le eun re } piae rvt: / .. / . } ;

Pitfall:
wie(i) hl cn { itx n ; cn< x i < ; / .. / . }

Note the typo--it should be c n > x i > . Butc n < xhas an unintended meaning:c n o e a o b o ( , converted to an i tand shifted by xbits. i < i.prtr ol) n Moral: Use conversion tov i * not conversion to i tor b o , to implement objects yielding truth values. od, n ol Unlike i tor b o , v i *have no legal operations other than = comparison. n ol od = Example: An array class with a [ operator that grows the array on demand ]
casAry ls ra { pbi: ulc Ary) ra(; ~ra(; Ary) ArycntAry) ra(os ra&; Ary oeao=cntAry) ra& prtr(os ra&; it oeao[(n) n& prtr]it; piae rvt: it_;/ cretnme o eeet n n / urn ubr f lmns it _;/ pit t ha ary n* a / ons o ep ra } ;
www.horstmann.com/cpp/pitfalls.html 15/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

it Ary:prtr]iti n& ra:oeao[(n ) { i ( >_) f i n { it p=nwiti; n* e n[] fr(n k=0 k<_;k+ o it ; n +) pk =_[] [] ak; fr( k<i k+ pk =0 o ; ; +) [] ; dlt[ _; eee] a _ =p a ; _ =i n ; } rtr _[] eun ai; } itmi( n an) { Arya ra ; fr(n s=1 s< 10 s+ o it ; = 0; +) as =s*s [] ; rtr 0 eun ; }

Pitfall:
vi sa(n&x it y od wpit , n& ) { ittm =x n ep ; x=y ; y=tm; ep } itmi( n an) { Arya ra ; a3 =9 [] ; sa([] a4) wpa3, []; rtr 0 eun ; }

The s a function gets references to a 3 , then to a 4 ,but the second computation moves the array and wp [] [] invalidates the first reference!a 4 is swapped with a wild reference. [] Moral: You cannot simultaneously relocate a memory block and export a reference to it. Either make [ not grow the array, or use a data structure in which elements never move (i.e. a sequence of ] chunks, such as in s d : e u ). t:dqe

Exception pitfalls
Example:
vi ra_tf(os ca flnm[) od edsufcnt hr ieae] { FL*f =fpnflnm,""; IE p oe(ieae r) d_edn(p; oraigf) fls(p; coef) }
www.horstmann.com/cpp/pitfalls.html 16/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

Why is that an "exception pitfall"? There aren't any exceptions in the code! Pitfall:
FL*f =fpnflnm,""; IE p oe(ieae r) d_edn(p; oraigf) fls(p;/ <-mynvrgthr coef) / - a ee e ee

If d _ e d n throws an exception, or calls a function that throws an exception, it never comes back!f is oraig p never closed. Moral: Exception handling drastically alters control flow. You cannot take it for granted that a function ever returns. Remedy 1: (popular but dumb)
vi ra_tf(os ca flnm[) od edsufcnt hr ieae] { FL*f =fpnflnm,""; IE p oe(ieae r) ty r d_edn(p; oraigf) cth.. ac(.) { fls(p; coef) trw ho; } fls(p; coef) }

Remedy 2: (smart)
vi ra_tf(os ca flnm[) od edsufcnt hr ieae] { ftemf(ieae isbs:i) sra pflnm, o_ae:n; d_edn(p; oraigf) }

Even ifd _ e d n throws an exception, f is closed by the ifstream destructor. oraig p Moral: In code with exception handling (i.e. all C++ code starting in 1994), relinquish resources only in destructors! Example:
dul fn_aayices(uoprEpoe>; obe idslr_nraeat_t<mlye) vi d_tf(os ca nm[) od osufcnt hr ae] { at_t<mlye p =nwEpoe(ae; uoprEpoe> e e mlyenm) / cntue / a' s / Epoe*p =nwEpoe(ae / mlye e e mlyenm) / ta' ntntecpinsf / hts o o xeto ae dul rt =fn_aayices(e; obe ae idslr_nraep) p-rieslr(ae; e>as_aayrt) }

www.horstmann.com/cpp/pitfalls.html

17/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

Pitfall:
fn_aayices(e; idslr_nraep)

invokes the copy constructor of a t _ t < m l y e which transfers ownership to the copy. uoprEpoe> Only one a t _ t can own a heap object. The owning a t _ t calls the destructor when it goes out of uopr uopr scope. Remedy: Don't copy an a t _ t into a function. uopr
dul fn_aayices(mlye) obe idslr_nraeEpoe*; vi d_tf(os ca nm[) od osufcnt hr ae] { Epoe*p =nwEpoe(ae; mlye e e mlyenm) at_t<mlye ae=p;/ ueol frdsrcin uoprEpoe> p e / s ny o etuto dul rt =fn_aayices(e; obe ae idslr_nraep) p-rieslr(ae; e>as_aayrt) }

Container pitfalls
Example: A set of pointers
stEpoe* saf e<mlye> tf; vco<tig nms etrsrn> ae; fr(n i=0 i<nmssz(;i+ o it ; ae.ie) +) safisr(e Epoe(ae[]; tf.netnw mlyenmsi)

Pitfall: Ordered containers (s t m p m l i e , m l i a ) use <for comparison. It is assumed that <is a e, a, utst utmp total ordering. [NOTE: Thanks to Liam Devine and Richard Smith for pointing out that this out of date. Of course, the containers really use l s < >for comparison, and The C++ standard Section 20.3.3.8 states: "For templates esT g e t r l s , g e t r e u l and l s _ q a , the specializations for any pointer type yield a total order, rae, es rae_qa, eseul even if the built-in operators < > < , > do not." When I wrote this, segmented memory models were still , , = = widely used, and the C++ standard was still a work in progress :-)] In a set of pointers
stEpoe* saf e<mlye> tf;

the pointers are compared with < . Given two arbitrary E p o e *pointers pand q is p < qdefined? Only if they point to the same array. mlye , In a segmented memory model, only offsets are compared. Ex. p = 0 7 0 0 0 and q = 0 7 1 0 0 = x4A04 = xCB04 compare identical. Remedy: (risky) Only write code for a flat memory space where pointer word size == integer word size and
www.horstmann.com/cpp/pitfalls.html 18/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

comparison happens to be a total ordering. Remedy: (tedious) Supply an ordering:


bo epoe_t_escntEpoe*a cntEpoe*b ol mlyeprls(os mlye , os mlye ) { rtr a>aay)<b>aay) eun -slr( -slr(; } stEpoe* bo ()cntEpoe* cntEpoe*> e<mlye, ol *(os mlye, os mlye) safepoe_t_es; tf(mlyeprls)

Example: Runaway iterators


ls<n>a b itit , ; / .. / . ls<n>:trtrp itit:ieao =fn(.ei(,bed) 10; idabgn) .n(, 0) i ( ! aed) bps_ak*) f p = .n() .uhbc(p;

Pitfall:
fn(.ei(,bed) 10;/ op,sol hv be aed) idabgn) .n(, 0) / os hud ae en .n(

To see why this code crashes dramatically, look at the implementation of f n : id


tmlt<yeaeI tpnm T epaetpnm , yeae > Ifn( fo,It,cntT tre) idI rm o os & agt { wie(rm! t & *rm! tre) hl fo = o & fo = agt +fo; +rm rtr fo; eun rm }

When f o reaches a e d ) * r mand + f o are undefined. rm .n(, fo +rm Moral: Iterators don't know their state. There is no reason why a l s < n >iterator couldn't know its state, itit but STL was built with the objective to make iterators no more powerful than pointers into a C array. Benefit: You can call the standard algorithms with C arrays:
ita3] n [0; it p=fn(,a+3,10; n* ida 0 0)

Drawback: Programming with iterators is pointer-oriented, not object-oriented. Remedy: Use Safe STL (http://www.horstmann.com/safestl.html) Example: Muddled iterators
ls<n>a itit ; ls<n>b itit ; ls<n>:trtrp=abgn) itit:ieao .ei(;
www.horstmann.com/cpp/pitfalls.html 19/20

4/30/12

www.horstmann.com/cpp/pitfalls.html

aisr(0; .net5) bisr(0) .net10; beaep; .rs() cu < alnt( < ed;/ lnt i 1 ot < .egh) < nl / egh s cu < blnt( < ed;/ lnt i 0 ot < .egh) < nl / egh s

Pitfall: In
beaep; .rs()

the iterator ppointed inside a The behavior is undefined, but the standard STL implementation does the ! following:
* is erased p

from whatever list it happens to be in, just by following the forward and backwards link the length of bis decremented Moral: Iterators don't know their owner. Remedy: Use Safe STL (http://www.horstmann.com/safestl.html)

www.horstmann.com/cpp/pitfalls.html

20/20

Vous aimerez peut-être aussi