Você está na página 1de 872

I HC QUC GIA THNH PH H CH MINH TRNG I HC KHOA HC T NHIN TPHCM KHOA: CNG NGH THNG TIN LP 06C2 ******

BI DCH TEACH YOURSELF C++ THIRD EDITION


GVHD: MN : Th.s.NGUYN TN TRN MINH KHANG PP LP TRNH HNG I TNG

NHM THC HIN: 8BIT 1.Trnh Vn Long 2.on Minh Bo Long 3.H An Phong 4.Trn Quang long 5.Nguyn Thnh Long 6.Nguyn Vn Nm 7. Trng Long 8. Dng Hunh ngha 0612229 0612231 0612330 0612227 0612223 0612326 0612232 0612285

LI M U ***
c s gip , hng dn ca thy cc thnh vin ca nhm 8BIT cng nhau tho lun,nghin cu dch sch Teach Yourselt C++, Third Editon ngha l T Hc C++, n bn 3 ca tc gi Herbert Schildt. y l mt cun sch rt hay, d hiu v rt hu ch cho vic hc tp b mn ny cng nh cc b mn sau ny . c bit l nhng ai mi bc vo con ng tr thnh lp trinh vin quc t hay mt chuyn vin phn mm, Phng Php Lp Trnh Hng i Tng n nh hng cho ngi lp trnh mt cch tng quan v lp trnh. i vi nhng sinh vin trong giai on i cng th n i xuyn sut bn nm hc. Cc thnh vin ca nhm c gng nhng cng khng trnh khi nhng sai st do thiu kinh nghim dch sch, mong thy v qu v c gi thng cm. cho cun sch c hon thin rt mong s gp ca cc c gi. Nhm 8BIT xin chn thnh cm n.

Nhm 8BIT

MC LC
CHNG 1........................................................................................................................6 AN OVERVIEW OF C++ - TNG QUAN V C++...................................................6 1.1. WHAT IS OBJECT-ORIENTED PROGRAMMING ?- LP TRNH HNG I TNG L G ?...................................................................................................10 1.2. TWO VERSIONS OF C++ - HAI PHIN BN CA C++...................................18 1.3. C++ CONSOLE I / O - BN GIAO TIP NHP/XUT C++..........................28 1.4. C++ COMMENTS LI CH GII TRONG C++.............................................38 1.5. CLASSSES: A FIRST LOOK - LP : CI NHN U TIN ............................40 1.6. SOME DIFFERENCE BETWEENCE AND C++ - MT S KHC BIT GIA C V C++......................................................................................................................50 1.7. INTRODUCING FUNCTION OVERLOADING - DN NHP S NP CHNG HM:..............................................................................................................58 1.8. C++ KEYWORDS T KHA TRONG C++ ...................................................66 CHNG 2......................................................................................................................69 Gii Thiu Lp (Introducing Classes)........................................................................69 2.2. CONSTRUCTORS THAT TAKE PARAMETERS - THAM S CA HM TO ........................................................................................................................................84 2.3. INTRODUCING INHERITANCE - GII THIU TNH K THA:..................96 1.4. OBJECT POINTERS - CON TR I TNG:...............................................109 1.6. IN-LINE FUNCTION - HM NI TUYN:.....................................................126 1.7. AUTOMATIC IN-LINING - HM NI TUYN T NG:...........................132 CHAPTER 3...................................................................................................................141 A CLOSER LOOK AT CLASSES - Xt K Hn V Lp.........................................141 1.1. Assigning Objects - Gn i tng:......................................................................143 1.2. PASSING OBJECTS TO FUNCTIONS Truyn cc i tng cho hm:.........155 1.3. RETURNING OBJECT FROM FUNCTIONS Tr v i tng cho hm:......167 CHNG 4....................................................................................................................194 ARRAYS, POITERS, AND REFERENCES - Mng, con tr v tham chiu...........194 1.1. ARRAYS OF OBJECTS - MNG CC I TNG......................................197 1.2. USING POINTERS TO OBJECTS S dng con tr i tng:.......................206 1.4. USING NEW AND DELETE - Cch dng ton t new v delete:......................215 1.5. MORE ABOUT NEW AND DELETE - M Rng ca new v delete:................220 1.6. REFERENCES - Tham chiu:..............................................................................229 1.7. PASSING REFERENCES TO OBJECTS Truyn tham chiu cho i tng:..237 1.8. RETURNING REFERENCES - Tr v tham chiu:............................................243 1.9. INDEPENDENT REFERENCES AND RESTRICTIONS - THAM CHIU C LP V NHNG HN CH :...................................................................................250 CHAPTER 5...................................................................................................................257 FUNCTION OVERLOADING Np chng hm.....................................................257 5.1. OVERLOADING CONSTRUCTOR FUNCTIONS - QA TI CC HM TO: ......................................................................................................................................260 5.2. CREATING AND USING A COPY CONSTRUCTOR - TO V S DNG HM TO BN SAO:...............................................................................................270 5.3. THE OVERLOAD ANACHRONISM - S Li Thi Ca T kha Overload:...288

5.4. USING DEFAULT ARGUMENTS - S dng cc i s mc nh:...................289 5.5. OVERLOADING AND AMBIGUITY - S QU TI V TNH KHNG XC NH:..........................................................................................................................302 5.6. FINDING THE ADDRESS OF AN OVERLOADED FUNCTION - TM A CH CA MT HM QU TI:...............................................................................309 CHNG 6....................................................................................................................320 INTRODUCING OPERATOR OVERLOADING ....................................................320 GII THIU V NP CHNG TON T...............................................................320 THE BASICS OF OPERATOR OVERLOADING - C S CA QU TI TON T................................................................................................................................323 OVERLOADING BINARY OPERATORS - QU TI TON T NH NGUYN.326 OVERLOADING THE RELATIONAL AND LOGICAL OPERATORS - QU TI CC TON T QUAN H V LUN L...............................................................339 OVERLOADING A UNARY OPERATOR - QU TI TON T N NGUYN ......................................................................................................................................343 6.5. USING FRIEND OPERATOR FUNCTION - S DNG HM TON T FRIEND.......................................................................................................................350 6.6. A CLOSER LOOK AT THE ASSIGNMENT OPERATOR - Mt Ci Nhn V Ton T Gn................................................................................................................362 6.7. OVERLOADING THE [ ] SUBSCRIPT OPERATOR - QU TI CA TON T [ ] CH S DI .................................................................................................368 CHNG 7 ...................................................................................................................384 INHERITANCE - TNH K THA............................................................................384 1.1. BASE CLASS ACCESS CONTROL IU KHIN TRUY CP LP C S ......................................................................................................................................390 1.2. USING PROTECTED MEMBERS - S DNG CC THNH VIN C BO V.......................................................................................................................404 1.3. CONSTRUCTORS, DESTRUCTORS, AND INHERITANCE - HM TO, HM HY V TNH K THA...............................................................................413 1.4. MULTIPLE INHERITANCE - TNH A K THA ......................................432 1.5. VIRTUAL BASE CLASSES - CC LP C S O......................................448 CHNG 8....................................................................................................................468 INTRODUCING THE C++ I/O SYSTEM - DN NHP H THNG NHP/XUT C++..................................................................................................................................468 1.1. SOME C++ I/O BASICS - C s Nhp/Xut C++..............................................474 1.2. FORMATTED I/O - Nhp/Xut C nh Dng...................................................478 1.3. USING WIDTH( ), PRECISION( ), AND FILL( ) S DNG HM WIDTH(), PRECISION( ), V FILL( ):.......................................................................................493 1.4. USING I/O MANIPULATORS S DNG B THAO TC NHP XUT...499 1.5. CREATING YOUR OWN INSERTERS TO B CHN VO:.....................506 1.6. CREATING EXTRACTORS TO B CHIT:...............................................517 CHAPTER 9...................................................................................................................526 ADVANCE C++ I/O NHP XUT NNG CAO CA C++...................................526 9.1. CREATING YOUR OWN MANIPULATORS TO CC THAO TC RING ......................................................................................................................................529 9.2. FILE I/O BASICS NHP XUT FILE C BN............................................536

9.3. UNFORMATTED, BINARY I/O - NHP XUT NHI PHN KHNG INH DANG..........................................................................................................................548 9.4. MORE UNFORMATTED I/O FUNCTIONS - THM MT S HM NHP/XUT KHNG C NH DNG...........................................................559 9.5. RANDOM ACCESS - TRUY XUT NGU NHIN.........................................566 9.6. CHECKING THE I/O STATUS - KIM TRA TRNG THI I/O......................572 9.7. CUSTOMIZED I/O AND FILES - FILE V I/O THEO YU CU..................578 CHAPTER 10.................................................................................................................585 VIRTUAL FUNCTIONS - HAM AO........................................................................585 10.2. INTRODUCTION TO VIRTUAL FUNCTIONS TNG QUAN V HM O ......................................................................................................................................591 10.3. MORE ABOUT VIRTUAL FUNCTIONS - NOI THM V HAM AO..........605 10.4. APPLYING POLYMORPHISM - AP DUNG A HINH.................................612 CHAPTER 11.................................................................................................................628 TEMPLATES AND EXCEPTION HANDLING - NHNG BIU MU V TRNH IU KHIN BIT L................................................................................................628 11.1. GENERIC FUNCTIONS NHNG HM TNG QUT...............................630 11.2. GENERIC CLASSES LP TNG QUT......................................................641 11.3. EXCEPTION HANDLING- IU KHIN NGOI L...................................653 11.4. MORE ABOUT EXCEPTION HANDLING - TRNH BY THM V IU KHIN NGOI L.....................................................................................................665 11.5. HANDLING EXCEPTIONS THROWN - S DNG NHNG NGOI L C NM................................................................................................................679 CHAPTER 12.................................................................................................................689 RUN-TIME TYPE IDENTIFICATION AND THE CASTING OPERATORS KIU THI GIAN THC V TON T P KHUN............................................689 13.1. UDERSTANDING RUN-TIME TYPE IDENTIFICATION (RTTI) - TM HIU V S NHN DNG THI GIAN THC ..............................................................692 1.2. USING DYNAMIC_CAST S DNG DYNAMIC_CAST............................714 1.3. USING CONST_CAST, REINTERPRET_CAST, AND STATIC_CAST - CCH DNG CONST_CAST, REINTEPRET_CAST V STATIC_CAST.........................727 CHAPTER 13 ................................................................................................................737 NAMESPACES, CONVERSION FUNCTIONS, AND MISCELLANEOUS TOPICS - NAMESPACES, CC HM CHUYN I V CC CH KHC NHAU.737 13.1. NAMESPACES..................................................................................................739 13.2. CREATING A CONVERSION FUNCTION TO MT HM CHUYN I ......................................................................................................................................756 13.3. STATIC CLASS AND MEMBERS LP TNH V CC THNH PHN...762 13.4. CONST MEMBER FUNCTIONS AND MUTABLE - HM THNH PHN KHNG I V C TH THAY I.....................................................................773 13.5. A FINAL LOOK AT CONSTRUCTORS - CI NHN CUI CNG V HM ......................................................................................................................................779 13.6. USING LINKAGE SPECIFIERS AND THE ASM KEYWORD......................786 13.7. ARRAY-BASE I/O MNG C S NHP/XUT......................................791 CHAPTER 14.................................................................................................................800 INTRODUCING THE STANDARD TEMPLATE LIBRARY GII THIU V

TH VIN GIAO DIN CHUN...............................................................................800 14.1. AN OVERVIEW OF THE STANDARD TEMPLATE LIBRARY TNG QUAN V TH VIN GIAO DIN CHUN...........................................................804 THE CONTAINER CLASSES LP CONTAINER.................................................809 14.3.VECTORS............................................................................................................811 LISTS - DANH SCH................................................................................................823 14.4.MAPS - BAN :...............................................................................................836 14.5.ALGORITHMS - Nhng thut giai.....................................................................846 14.6.THE STRING CLASS - Lp Chui....................................................................857

CHNG 1 AN OVERVIEW OF C++ - TNG QUAN V C++


Chapter object :
1.1. WHAT IS OBJECT-ORIENTED PROGRAMMING ?. Lp Trnh Hng i Tng L G ? 1.2. TWO VERSIONS OF C++.

Hai Phin Bn C++


1.3. C++ COMMENTS I/O

Nhp / Xut C++


1.4. C++ COMMENTS.

Li Nhn Xt C++
1.5. CLASSES: A FIRST LOOK.

LP : Ci Nhn u Tin. 1.6. SOME DIFFERENCE BETWEEN C AND C++.

Mt S Khc Bit Gia C v C++.


1.7. INTRODUCING FUNCTION OVERLOADING.

Dn Nhp S Qu Ti Hm.
1.8. C++ KEYWORDS SHILLS CHECK.

Cc T Kha Trong C++.

C++ is an enhanced version of C language. C++ includes everything that is part of C and adds support for object-oriented programming (OOP for short). In addition to, C++ contains many improvements and features that simply make it a better C, independent of object-oriented programming. With very few, very minor exceptions, C++ is a superset of C. While everything that you know about the C language is fully applicable to C++, understanding its enhanced features will still require a significant investment of time and effort on your part. However, the rewards of programming in C++ will more than justify the effort you put forth. C++ l mt phin bn ca ngn ng C. C++ bao gm nhng phn c trong C v thm vo l s h tr cho Lp trnh hng i tng (vit tt l OOP). Cng thm vo , C++ cn cha nhng ci tin v nhng ci tin m n gin nhng to ra mt phin bn C tt hn, khng ph thuc vo phng php lp trnh hng i tng. Vi rt t ring bit, C++ c th xem l tp cha ca C. Trong khi nhng g bn bit v C th hon ton c th thc hin trong C++, th vic hiu r nhng tnh nng ca C++ cng i hi mt s u t thch ng v thi gian cng nh n lc ca chnh bn thn bn. Tuy nhin, phn thng cho vic lp trnh bng C++ l s ngy cng chng minh c nng lc ca bn thn bn hn vi nhng g bn th hin.

The purpose of this chapter is to introduce you to several of the most important features of C++. As you know, the emplements of a computer language do not exist in a void, separate from one another. Instead, they work together to form the complete language. This interrelatedness is especially pronounced in C++. In fact, it is difficult to discuss one aspect of 7

C++ in isolation because the features of C++ are highly integrated. To help overcome this problem, this chapter provides a brief overview of several C++ features. This overview will enable you to understand the examples discussed later in this book. Keep in mind that most topics will be more thoroughly explored in later chapters. Mc tiu ca chng ny l gii thiu cho bn 1 s tnh nng quan trng nht ca C++. Nh bn bit , nhng thnh phn ca ngn ng my tnh th khng cng tn ti trong 1 khong khng, m tch bit vi nhng ci khc, thay v lm vic cng nhau to thnh mt ngn ng hon chnh. Mi tng quan ny c c bit pht biu trong C++. S tht l rt kh tho lun v mt din mo ca C++ trong s c lp bi v nhng tnh nng ca C++ c tch hp cao . gip vt qua vn ny, chng ny cung cp mt ci nhn ngn gn v mt s tnh nng ca C++. Ci nhn tng quan ny s gip bn hiu hn v nhng th d m chng ta s tho lun phn sau cun sch. Hy ghi nh tt c nhng mc chng s gip bn hiu thu o nhng chng tip theo. Since C++ was invented to support object-oriented programming this chapter begins with a decriptions of OOP. As you will see, many features of C++ are related to OOP in one way or another. In fact, the theory of OOP permeates C++. However, it is important to understand that C++ can be used to write programs that are and are not object oriented. How you use C++ is completely up to you. V C++ c thit k ra h tr cho vic lp trnh hng i tng, nn chng ny chng ta bt u vi vic m t v lp trnh hng i tng (OOP). Ri bn s thy rng nhng tnh nng khc ca C++ cng lien quan ti OOP bng cch ny hay cch khc. S tht l l thuyt v lp trnh hng i tng c tri dn trong C++. Tuy nhin, phi hiu rng vic dng C++ vit chng trnh th c hoc khng c hng i tng. Vic s dng C++ nh th no l hon ton ph thuc vo bn. At the time of this writing, the standardization of C++ is being finalized. For this reason, this chapter describes some important differences between versions of C++ that have been in common use during the past several years and the new Standard C++. Since this book teaches Standard C++, this material is especially important if you are using an older compiler. Vo thi im vit cun sch ny th vic chun ha C++ ang c hon tt. V l do chng ny s m t nhng s khc bit quan trng gia hai phin bn ca C++ m tng c dung ph bin trong nhng nm 8

trc y v tiu chun mi trong C++. V cun sch ny dy v Tiu chun C++, nn ti liu ny s c bit quan trng nu bn s dng nhng trnh bin dch c.

In addition to introducing several important C++ features, this chapter also discusses some differences between C and C++ programming styles. There are several aspects of C++ that allow greater flexibility in the way that you are write programs. While some of these features have little or nothing to do with object-oriented programming, they are found in most C++ programs, so it is appropriate to discuss them early in this book. Thm vo vic gii thiu mt s tnh nng quan trng trong C++, chng ny cng tho lun v nhng khc bit gia hai phong cch lp trnh C v C++. y c vi yu t ca C++ cho php n c tnh linh ng hn trong vic vit chng trnh. Trong khi nhng yu t ny th rt t hoc khng c lp trnh hng i tng th n c tm thy hu nh y trong nhng chng trnh ca C++, v th s tht tha ng nu tho lun v chng trong khi u quyn sch ny.

Before you begin, a few general comments about the nature and form of C++ are in order. First, for the most part, C++ programs physically look like C programs. Like a C program, a C++ program begins execution at main( ). To include command-line arguments, C++ uses the same argc, argv convention that C uses. Although C++ defines its own, object-oriented library, it also supports all the functions in the C standard library. C++ uses the same control structures as C. C++ includes all of the built-in data types defined by C. Trc khi bn bt u, mt vi ch gii tng qut v bn cht v biu mu s a ra trong trnh t. u tin, dnh cho hu ht cc phn, theo ci nhn l tnh th cc chng trnh C++ trng nh cc chng trnh C. Cng nh C, mt chng trnh C++ cng bt u thi hnh bi hm main(). bao gm nhng i s bng c ch dng lnh (command-line), C++ cng s dng quy c argc, argv m C dng. Mc d C++c nh ngha vi nhng s hu bn thn, th vin hng i tng (object-oriented), n cng h tr nhng hm trong th vin chun ca C. C++ s dng cu trc iu khin nh C. C++ cng bao gm nhng kiu d liu c xy dng trong C.

This book assumes that you already know the C programming language. In other words, you must be able to program in C before you can learn to program in C++ by using this book. If you dont knowC, a good starting place is my book Teach Yourseft C, Third Edition (Berkeley: Osborne/McGraw-Hill, 1997). It applies the same systematic approach used in this book and thoroughly covers the entires C language. Quyn sch ny xem nh bn bit v ngn ng lp trnh C. Ni cch khc bn phi bit lp trnh trong C trc khi bn hc nhng chng trnh C++ c vit trong cun sch ny. Nu bn cha bit v C, hy bt u vi cun sch Teach yourseft C, Third Edition (Berkeley: Osborne/McGrawHill, 1997).Vic ng dng cch tip cn c h thng c s dng trong cun sch ny s bao ph ton vn ngn ng C.

Note: This book assumes that you know how to compile and execute a program using your C++ compiler. If you dont, you will need to refer to your compilers instructions. (Because of the differences between compilers, it is impossible to give compolation instructions for each in this book). Since programming is best learned by doing, you are strongly urged to enter, compile, and run the examples in the book in the order in which they are presented.

Ch :
Cun sch ny cho rng bn bit bin dch v thc thi mt chng trnh bng trnh bin dch C++. Nu cha, bn s cn nhiu s hng dn hn bi trnh bin dch ca bn. (Bi v nhng s khc nhau gia cc trnh bin dch, s l khng kh thi khi a ra nhng s hng dn v s bin dch trong cun sch ny.) V chng trnh l bi hc thc hnh tt nht, nn bn cn phi mnh m tin n, bin dch v chy th nhng v d trong cun sch ny bit dc ci g ang hin din ng sau nhng v d y.

1.1.

WHAT IS OBJECT-ORIENTED PROGRAMMING ?- LP TRNH HNG I TNG L G ?


Object-oriented programming is a powerful way to approach the task of

10

programming. Since its early beginnings, programming has been governed by various methodologies. At each critical point in the evolution of programming, a new approach was created to help the programmer handle increasingly complex programs. The first programs were created by toggling switches on the front panel of the computer. Obviously, this approach is suitable for only the smallest programs. Next, assembly language was invented, which allowed longer programs to be written. The next advance happened in the 1950s when the first high-level language (FORTRAN) was invented.

Lp trnh hng i tng l con ng mnh m tip cn nhim v lp trnh. T bui bnh minh, lp trnh b chi phi bi nhiu phng php khc nhau. Ti mi thi im cch tn trong s pht trin ca lp trnh, mt cch tip cn mi li ra i vi mc ch gip lp trnh vin gia tng kh nng kim sot hay iu khin nhng chng trnh mang tnh phc tp. Nhng chng trnh u tin c to ra bi nhng cng tc o chiu c gn trn mt ci bng pha trc my tnh. Hin nhin l cch tip cn ny ch p ng c cho nhng chng trnh nh nht. Tip theo, ngn ng assembly (hay cn gi l Hp ng) c ra i, cho php vit nhng chng trnh di hn. Mt ngn ng cp cao hn xut hin vo nhng nm ca thp nin 1950 khi ngn ng cp cao u tin c ra i ( l FORTRAN).

By using a high-level language, a programmer was able to write programs that were several thousands lines long. However,the method of programming used early on was an ad hoc, anything-goes approach. While this is fine relatively short programs, it yields unreadable (and unmanageable) spaghetti code when applied to larger programs. The elimination of spaghetti code became feasible with the invention of structured programming languages in the 1960s. These languages include Algol and Pascal. In loose terms, C is a structured language and most likely the type of programming relies on well-defined control structures, code blocks, the absence (or at least minimal use) of GOTO, and stand-alone subroutines that support recursion and local variables. The essence of structured programming is the reduction of a program into its constituent elements. Using structured programming, the average programmer can create and maintain programs that are up to 50,000 lines long. Bng vic s dng ngn ng cp cao, cc lp trnh vin c th vit nn nhng chng trnh c di ln ti vi ngn dng. Tuy nhin, phng php lp trnh ny cng ch c s dng nh mt th tm thi, khng g tin trin c. Trong khi nhng mi quan h gia cc chng trnh ngn kh kh quan

11

th vic thit k li qu kh (v khng kim sot c) loi m spaghetti ny khi ng dng cho cc chng trnh c ln. Cuc u tranh vi loi m spaghetti ny to cho s pht minh ra cu trc ca nhng ngn ng lp trnh vo thp nin 1960. Nhng ngn ng ny l Algol v Pascal. Trong tng lai khng chc chn, C l mt ngn ng c cu trc v gn nh l kiu lp trnh m bn bn ang dung vi tn gi l lp trnh c cu trc. Lp trnh cu trc da trn nhng cu trc iu khin, nhng khi m d xc nh, s vng mt (hay gn nh rt t s dng) ca GOTO, v nhng th tc con ng mt mnh h tr cho phng php quy v nhng bin cc b. Bn cht ca lp trnh cu trc l s gim thiu nhng yu t cu thnh bn cht bn trong ca mt chng trnh. Bng vic s dng phng php lp trnh ny, nhng lp trnh vin vi nng lc trung bnh cng c th to ra v bo tr nhng chng trnh m c khi lng ln ti 50.000 dng.

Although structured programming has yielded excellent results when applied to moderately complex programs, even it fails at some point, after a program reaches a certain size. To allow more complex programs to be written, a new approach to the job of programming was needed. Towards this end, objectoriented programming was invented. OOP takes the best of ideas embodied in structured programming and combines them with powerful new concepts that allow you to organize your programs more effectively. Object-oriented programming encourages you to decompose a problem into its constituent parts. Each component becomes a seft-contained object that contains its own instructions and data that relate to that object. In this way, complexity is reduced and the programmer can manage larger programs. Mc d lp trnh cu trc t c nhng kt qu xut sc khi ng dng vo nhng chng trnh c phc tp trung bnh, thm ch ch trc trc vi ch sau khi mt chng trnh t n mt kch thc chc chn. cho php vit c nhng chng trnh phc tp hn, mt cch tip cn mi cng vic lp trnh c ra. Cui cng, phng php lp trnh hng i tng c pht minh. Lp trnh hng i tng (OOP) c th ha nhng tng tuyt vi vo trong cu trc lp trnh v kt hp chng vi nhng khi nim cht ch m cho php bn t chc nhng chng trnh ca mnh mt cch c hiu qu. OOP khuyn khch bn phn tch mt vn nm trong bn cht ca n. Mi b phn u c th tr thnh mt i tng c lp m cha ng nhng cu lnh hay d liu lien quan n i tng . Bng cch ny s phc tp s c gim xung v lp trnh vin c th qun l nhng chng trnh c ln.

12

All OOP languages, including C++, share three common defining traits: encapsulation, polymorphism, and inheritance. Lets look at the three concepts now. Tt c nhng ngn ng lp trnh hng i tng, bao gm c C++, u mang ba c im chung sau y: tnh ng gi, tnh a dng, v tnh k tha. Cn ch g na phi tm hiu cc khi nim trn ngay thi.

Encapsulation
Encapsulation is the mechanism that binds together code and the data it manipulates, and keep both safe from outside interference and misuse. In an object-oriented language, code and data can be combined in such a way that a seft-contained black box is created. When code and data are linked together in this fashion, an object is created. In other words, an object is the device that supports encapsulation.

Tnh ng gi hay tch hp:


Tnh ng gi l vic rng buc nhng on m v nhng d liu li, iu khin v gi chng an ton khi nhng nh hng v nhng mc ch sai tri. Trong mt ngn ng lp trnh hng i tng, m v d liu c th c kt hp theo cch m mt ci hp en c lp c to ra. Khi m v d liu lin kt vi nhau theo kiu ny th mt i tng c to nn. Ni cch khc, mt i tng l sn phm ca s ng gi vi nguyn liu l m v d liu.

With an object, code, data, or both may be private to that object or public. Private code or data is known to and accessible only by another part of the object. That is, private code or data cant be accessed by a piece of the program that exists outside the object. When code and data is public, other parts of your program can access it even though it is defined within an object. Typically, the public parts of an object are used to provide a controlled interface to the private elements of the object. Trong phm vi ca mt i tng, m, d liu , hay c hai c th l phn private (phn ring) ca i tng hay l public (phn chung). Phn m v d liu trong phn private c bit v truy xut bi cc thnh phn khc ca i 13

tng. Ngha l m v d liu trong phn private khng th c truy xut bi bt k phn no trong chng trnh m tn ti bn ngoi i tng . Khi m v d liu l kiu public th mi phn trong chng trnh ca bn u c th truy xut n n ngay c khi n c nh ngha bn trong mt i tng. c trng l nhng phn c kiu public ca mt i tng th c s dng cung cp mt giao din iu khin nhng thnh phn kiu private ca mt i tng.

For all intents and purposes, an object is variable of a user-defined type. It may seem strange that an object that links both code and data can be thought of as a variable. However, in an object-oriented programming, this is precisely the case. Each time you define a new type of object, you are creating new data type. Each specific instance of this data type is a compound variable.

ph hp vi nhiu nh v mc ch, mt i tng c th xem l mt bin theo kiu ngi dng t nh ngha. C th bn s ly lm ngc nhin khi mt i tng c th lin kt nhng lnh(hay m) v d liu li m li xem nh l mt bin. Tuy vy, trong lp trnh hng i tng th iu l hon ton chnh xc. Mi khi bn nh ngha mt loi i tng th bn ang to ra mt kiu d liu mi. Mi mt kiu i tng nh vy c coi l mt bin phc hp (hay bin ghp).

Polymorphism Polymorphism (from the Greek,meaning many form) is the quality that allows one name to be used for two or more related but technically different purposes. As it relates to OOP, polymorphism allows one name to specify a general class of actions. Within a general class of actions, the specific action are be replied is determined by the type of data. For example, in C, which does not significantly support polymorphism, the absolute value action requires three distinct function names: abs( ), labs( ), and fabs( ). Three functions compute and return the absolute value of an integer, a long integer, and float-point value, respectively. However, in C++, which supports polymorphism, each function can be called by the same name, such as abs( ). (One way this can be accomplished is shown later in the chapter.) The type of the data used to call the function determines which 14

specific version of the function is actually executed. As you will see, in C++, it is possible to use ine function name for many different purposes. This is called function overloading.

Tnh a dng
Polymorphism (Theo ting Hy Lp c ngha l a th ) hay tnh a dng l c tnh cho php mt tn c th c s dng cho hai hay nhiu h nhng vi nhiu mc ch ng ngha khc nhau. V n lin quan n OOP, nn tnh a dng cho php mt tn ch r nhng hnh ng chung ca mt lp. Bn trong nhng hnh ng chung ca mt lp, hnh ng c trng ng dng c nh ngha bi mt kiu d liu. V d, trong C, th tnh a dng khng c h tr mt cch ng k, hnh ng ly gi tr tuyt i i hi ba tn hm ring bit l: abs( ), labs( ), fabs( ). Nhng hm ny tnh ton v tr li gi tr tuyt i ca mt s nguyn, mt s nguyn di, v mt s thc mt cch ring bit. Tuy nhin, trong C++, c h tr cho tnh a dng th ch cn mi hm abs( ). (Cch ny s c nu y trong phn sau ca chng ny). Kiu d liu dng trong hm s quyt nh vic hm no s c gi thc thi. Bn s thy, trong C++, hon ton c th s dng mt tn hm cho nhiu mc ch khc nhau. iu ny cn c gi l s qu ti hm (function overloading). More generally, the concept of polymorphism is characterized by the idea of one interface, multiple methods, which means using a generic interface for a group of related activities. The advantage of polymorphism is that it helps to reduce complexity by allowing one interface to specify a general class of action. It is the compilers job to select the specific action as it applies to each situation. You, the programmer, dont need to do this selection manually. You need only remember and utilize the general interface. As the example in the preceeding paragraph illustrates, having three names for the absolute value function instead of just one makes the general activity of obtaining the absolute value of a number more complex than it actually is. Tng qut hn, khi nim tnh a dng c c trng bi tng mt giao din nhng nhiu cch thc , iu ny c ngha l s dng mt giao din chung cho mt nhm nhng hnh ng c lin quan. S thun li ca tnh a dng l gip gim bt s phc tp bng cch cho php mt giao din ch r hnh ng ca mt lp tng qut. l cch m trnh bin dch chn hnh ng c trng thi hnh trong tng tnh hung c th. Bn, mt ngi lp trnh, khng cn phi lm cng vic ny mt cch th cng. Bn cn nh v

15

tn dng ci giao din chung. Nh th d minh ha trong on vn trc, cn n ba tn hm ly gi tr tuyt i thay v ch cn mt tn nhng ch hnh ng chung l tnh gi tr tuyt i ca mt s m tht s phc tp.

Polymorphism can be applied to operators, too. Virtually all programming languages contain a limited application of polymorphism as it relates to the arithmetic operators. For example, in C++, the + sign is used to add integers, long intefer, characters, and floating-point values. In these cases, the compiler automatically knows which type of arithmetic to apply. In C++, you can extend this concept to other types of data that you define. This type of polymorphism is called operator overloading. Tnh a dng cng c th s dng cho cc ton t. Hu nh mi ngn ng lp trnh u c mt s gii hn tnh a dng i vi cc ton t s hc. V d, trong C, du + c dung cng cc gi tr s nguyn, s nguyn di, k t v s thc. Trong nhng trng hp ny, trnh bin dch t ng hiu loi s hc no s c p dng. Trong C++, bn c th m rng khi nim ny i vi nhng kiu d liu khc m bn nh ngha. Loi a dng ny c gi l s qu ti cc ton t (operator overloading).

The key point to remember about polymorphism is that it allows you to handle greater complexity by allowing the creation of stamdard interfaces to related activities. im mu cht ca vn cn nh l tnh a dng cho php bn kim sot vi phc tp rt cao bng cch cho php to ra mt giao din tiu chun cho cc hnh ng c lin quan.

Inheritance Inheritance is the process by which one object can be acquire the properties of another. More specifically, an object can inherit a general set of properties to which it can add those features that are specific only to itself. Inheritance is important because it allows an object to support the concept of hierarchical classification. Most information is made manageable by hierarchical classification. For example, think about the description of a house. A house is part of the general called building. In turn, building is

16

part of the more general class structure, which is part of the even more general of objects that we call man-made. In each case, the child class inherits all those qualities associated with the parent and adds to them its own defining characteristics. However, through inheritance, it is possible to describe an object by stating what general class (or classes) it belongs to along with those specific traits that make it unique. As you will see, inheritace plays a very important role in OOP.

Tnh k tha
Tnh k tha l mt qu trnh m mt i tng c th s dng cc c tnh ca mt i tng khc. C th hn, mt i tng c th tha hng nhng c tnh chung ca i tng khc thm cc tnh nng khc ny vo nh mt phn ca chnh bn thn n. S tha hng rt quan trng bi v n cho php mt i tng chp nhn khi nim s phn loi c tn ti (hierachial classification). Hu ht cc thng tin c to ra c qun l bi s s phn loi c trt t ny. V d, hy ngh v mt ngi nh. Mt ngi nh l mt phn ca mt lp tng qut gi l cng trnh. C nh vy, cng trnh li l mt phn ca mt lp chung gi l kin trc, v kin trc li l thnh phn ca mt lp m ta gi l nhng vt phm nhn to. Trong mi trng hp, lp con tha hng tt c nhng tnh cht c lin i vi lp cha v cng thm vi nhng tnh cht c nh ngha bn trong chng. Nu khng s dng s phn loi c trt t th mi i tng s phi nh ngha tt c nhng c tnh m r rng lin quan n n. Tuy nhin, qua s k tha, ta c th m t mt i tng bng cch pht biu nhng c im ni bt thuc v mt lp tng qut (hay mt h cc lp) m to nn tnh c nht ca chng. Bn s thy rng s tha hng chim mt vai tr rt quan trng trong OOP.

CC V D :
1.S ng gi khng phi l iu g hon ton mi m trong OOP. Theo mt gc th s ng gi c th l mt thnh tu khi s dng ngn ng C. V d, khi bn s dng mt hm ca th vin th tht ra bn ang s dng th tc ci hp en, nhng g bn trong bn u th gy nh hng hay tc ng vo chng (tr phi, c l l ch b tay vi nhng hnh ng c tnh ph hoi). Hy xem xt hm fopen( ), khi dng m mt file, th vi bin ni b s c khi to v gn gi tr. Cng xa khi vng chng trnh lin quan th cng b n v kh truy cp. Tuy nhin, C++ vn cung cp cho bn nhiu cch tip cn s ng gi ny an ton hn. 2.Trong th gii thc, nhng v d v s a dng th kh l chung chung. V d, hy xem xt ci v-lng li xe ca bn. N lm vic cng ging nh chic xe bn s dng thit b li nng lng, thit b li bng thanh rng, hay tiu chun, thit 17

b li th cng. Vn l ci b ngoi hnh dng khng quan trng m l ci cch cc ng c bnh rng hot ng (hay ci phng thc). 3.S tha k cc c tnh v khi nim tng qut hn v lp l nn tng hiu cch t chc. V d, rau cn ty l mt loi(hay thnh vin) ca lp rau qu, rau qu li l mt thnh phn ca lp cy trng. Ln lt nh vy, cy trng l nhng sinh vt sng, v c th. Nu khng c s s phn loi trong tn ti th h thng h thng kin thc s khng kh thi.

BI TP:
Hy ngh v vai tr ca s phn loi v tnh a dng trong cuc sng hng ngy ca chng ta.

1.2.

TWO VERSIONS OF C++ - HAI PHIN BN CA C++


At the time of this writing, C++ is in the midst of a transformation. As explained in the preface to this book, C++ has been undergoing the process of standardization for the past several years. The goal has been to create a stable, standardized, featured-rich language, that will suit the needs of programmers well into the next century. As a result, there are really two versions of C++. The first is the traditional version that is based upon Bjarne Stroustrups original designs. This is the version of C++ that has been used by programmers for the past decade. The second is the new Standard C++, which was created by Stroustrup and the ANSI/ISO standardization committee. While these two versions of C++ are very similar at there core, Standard C++ contains several enhancements not found in traditional C++. Thus, Standard C++ is essentially a superset of traditional C++. Vo thi im vit cun sch ny, C++ l ci chuyn giao ca mt s thay i. Nh ni trong phn m u ca cun sch, C++ phi tri qua mt qu trnh chun ha trong vi nm trc y. Mc tiu l to ra s n nh, c chun ha, mt ngn ng giu tnh nng phc v tt cho cc lp trnh vin trong th k tip theo. Kt qu l to ra hai phin bn C++. Ci u tin l phin bn truyn thng m da trn ngun gc bn phc tho ca Bjarne Stroustrup. y l phin bn C++ c cc lp trnh vin s dng trong thp nin trc y. Ci th hai l new Standard C++, c to ra bi Stroustrup v y ban tiu chun ANSI/ISO. Trong khi hai phin bn C++ c ht nhn kh l ging nhau th Standard C++(hay C++ chun) cha vi s ni bt m khng

18

tm thy c trong phin bn C++ truyn thng. V vy, C++ chun mt cch c bit l mt tp cha ca bn C++ truyn thng.

This book teaches Standard C++. This is the version of C++ defined by the ANSI/ISO standardization committee, and it is the version implemented by all modern C++ compilers. The code in this book reflects the contemporary coding style and practices as encouraged by Standard C++. This means that what you learn in this book will be applicable today as well as tomorrow. Put directly, Standard C++ is the future. And, since Standard C++ encompasses all features found in earlier versions of C++, what you learn in this book will enable you work in all C++ programming environments. Cun sch ny vit v C++ chun. Phin bn ny c nh ngha bi y ban tiu chun ANSI/ISO, v n l cng c ca tt c cc trnh bin dch C++ hin nay. Nhng on m vit trong cun sch ny tng ng vi phong cch vit m ng thi v nhng bi thc hnh theo bn C++ chun. iu ny c ngha l nhng g bn hc trong cun sch ny th u s dng c hin nay cng nh tng lai. Ni trc tip l C++ chun chnh l tng lai. V, v C++ hon thin tt c cc tnh nng c trong cc bn C++ trc nn tt c nhng g bn hc u c th lm vic trong tt c cc mi trng lp trnh C++. However, if you are using an older compiler, it might not accept all the programs in this book. Heres why: During the process of standardization, the ANSI/ISO committee added many new features to the language. As these features were defined, they were implemented by the compiler developers. Of course, there is always a lag time between the addition of a new feature to the language and the availability of the features in commercial compilers. Since features were added to C++ over a period of years, an older compiler might not support one or more of them. This important because two recent additions to the C++ language affect every program that you will write-even the simplest. If you are using an oder compiler that does not accept these new features, dont worry. There is an easy workaround, which is described in the following paragraphs. Tuy nhin, nu bn s dng mt trnh bin dch qu c, th c kh nng n s khng chp nhn nhng on m vit trong cun sch ny. y cng l nguyn nhn m trong sut qu trnh chun ha, y ban tiu chun ANSI/ISO tng thm nhiu tnh nng mi cho ngn ng ny. Khi nhng tnh nng ny c nh ngha th chng cng c cc nh pht trin trnh bin dch b 19

sung. Tt nhin y lun l mt khong thi gian chm chp gia vic thm tnh nng vo mt ngn ng v vic bin n kh thi trong cc trnh bin dch. Nhng tnh nng ny c thm vo trong mt thi k nhiu nm nn nhng trnh bin dch i c th khng c h tr bin dch chng. V quan trng hn l hai s b sung gn y vo ngn ng C++ s nh hng n mi chng trnh m bn s vit, thm ch d l chng trnh n gin nht. Nu bn s dng trnh bin dch c hn th n s khng chp nhn nhng tnh nng mi ny, nhng ng qu lo lng. C mt cch gii quyt d dng s c m t trong nhng on tip theo. The differences between old-style and modern code involve two new features: new-style headers and the namespace statement. To demonstrate these deffirences we will begin by looking at two versions of a minimal, donothing C++ program. The first version, shown here, reflects theway C++ programs were written until recently. (That is, it uses old-style coding.) S khc bit gia phong cch vit m c v hin i lin quan n hai tnh nng sau: nhng kiu khai bo mi v s gii thiu khng gian tn (namespace). chng minh nhng s khc bit ny chng ti s bt u bng vic nhn li hai bn C++ mt kha cnh nh, khng ng n chng trnh C++. Trong bn u tin, hy xem sau y, phn nh cch nhng chng trnh C++ c vit trong thi gian gn y. ( l vit m theo phong cch c.) /* Nhng chng trnh vi phong cch C++ truyn thng /*

#include <iostream.h>

int main() { /* vng m chng trnh */ return 0; }

20

Since C++ is built on C, this skeleton should be largely familiar, but pay special attention to the #include statement. This statement includes the file iostream.h, which provides support for C++s I/O system. (It is to C++ what stdio.h is to C.) Here is the second version of the skeleton, which uses the modern style: V C++ c xy dng da trn C, nn ci khung rt quen thuc nhng hy ch n dng pht biu #include. Li pht biu ny khai bo file iostream.h m c cung cp cho h thng nhp xut ca C++ (trong C l stdio.h). Sau y l bn th hai ca ci khung ny, s dng phong cch hin i: /* Phong cch lp trnh C++ hin i l s dng li khai bo theo phong cch mi v vng khng gian tn */ #include <iostream> using namespace std;

int main() { /* vng m chng trnh */ return 0; } Notice the two lines in this program immediately after the first comment; this is where the changes occur. First, in the #include statement, there is no .h after the name iostream. And second, the next line, specifying a namespace, in new. Although both the new-style headers and namespaces will be examined in detail later in this book, a brief overview is in order now.

21

Ch hai dng trong chng trnh ny ngay lp tc sau khi c xong li nhn xt u tin; y c s thay i. y tin, trong li pht biu #include khng c .h theo sau tn iostream. V ci th hai l dng tip theo, ch r vng khng gian tn, y l nt mi. Mc d c hai ci ny s c kim nghim chi tit trong cc phn sau ca cun sch nhng chng ta hy xem xt ngn gn chng.

The new C++ headers


As you know from your C programming experience, when you use a library function in a program, you must include its header file.This is done using the #include statement. For example, in C, to include the header file for the I/O functions, you include stdio.h with a statement like this.

Nhng u mc mi ca C++
Nh bn bit t nhng kinh nghim lp trnh bng C++, khi bn s dng mt hm th vin trong mt chng trnh, bn phi bao hm li khai bo file. Ci ny dng cch pht biu #include. V d, trong C, khai bo s dng file cho hm I/O (hm nhp xut), bn s dng stdio.h vi li pht biu nh sau: #include <stdio.h> Here stdio.h is the name of the file used by the I/O functions, and the preceding statement causes that file to be included in your program. The key point is that the #include statement includes a file. y, stdio.h l tn file c s dng bi hm nhp xut, v vic li pht biu ny i trc lm cho file c bao hm trong chng trnh ca bn. im mu cht l #include pht biu vic includes a file (bao hm mt file).

When C++ was first invented and for several years after that,it used the same style of headers ad did C. In fact, Standard C++ still supports C-style headers for header files that you create and for backward compatibility. However, Standard C++ has introduced a new kind of header that is used by the Standard C++ library. The new-style headers do not specify filenames. Instead, they simply specify standard identifiers that might be mapped to files by the compiler, but they need not be. The new-style C++

22

headers are abstractions that simply guarantee that the appropriate prototypes and definitions required by the C++ library have been declared. Khi C++ ln u tin c to ra v vi nm sau , n s dng nhng kiu khai bo ca C. Tht ra, C++ chun vn cn h tr nhng khai bo kiu C cho nhng file khai bo m bn to ra v cho s tng thch ngc. Tuy nhin, C++ chun gii thiu mt kiu khai bo mi m c s dng trong th vin C++ chun. Kiu khai bo mi ny khng ch r tn nhng file. Thay v n gin ch r nhng nh dng tiu chun m chng c nh x vo cc file bi trnh bin dch nhng chng khng cn. Kiu khai bo mi ny ca C++ l nhng s tru tng ha nh s m bo n thun, nhng vt mu hay nhng nh ngha thch ng c yu cu bi th vin C++. Since the new-style header is not a filename, it does not have a .h extension. Such a header consist solely of the header name contained between angle brackets supported by Standard C++. V khai bo mi khng phi l mt tn file nn ui .h khng cn thit. Mt khai bo gm c mt tn khai bo n c trong du ngoc < >. V d, sau y l vi kiu khai bo c chp nhn trong C++ chun: <iostream> <fstream> <vector> <string> The new-style headers are included using the #include statement. The only difference is that the new-style headers do not necessarily represent filenames. Nhng u mc kiu mi ny bao gm vic s dng s trnh by #include. S khc nhau duy nht l nhng u mc kiu mi khng tt yu i din cho tn file.

Becase C++ includes th entire C function library, it still supports the standard C-style header files associated with that library. That is, header files such as stdio.h and ctype.h are still available. However, Standard C++ also defines newstyle headers you can use in place of these header files. The C++ versions of the standard C headers simply add c prefix to the filename and drop the .h. For example, the new-style C++ header for math.h is <cmath>, and the one for 23

string.h is <cstring>. Although it is currently permissible to include a C-tyle header file when using C library functions, this approach is deprecate by Standart C++.(That is, it is not recommended.) For this reason, this book will use new-style C++ header in all #include statements. If your compiler does not support new-style headers for the C function library, simply substitute the oldstyle, C-like headers. Bi v C++ bao gm ton b th vin chc nng C, n vn cn h tr C tiu chunnhng h s u mc kiu lin quan n th vin kia. iu , u mc sp xp nh stdio.h v ctype.h th vn cn sn c. Tuy nhin, C++ tiu chun cng nh ngha nhng u mc kiu mi bn c th s dng thay cho nhng h s u mc ny. Nhng phin bn C++ ca nhng u mc C tiu chun n gin thm tin t c vo u tn file v kt thc bng .h .Chng hn, u mc C++ kiu mi cho math.h l <cmath>, v cho string.h l < cstring>. D hin thi tha nhn bao gm mt h s u mc tyle C khi s dng nhng hm th vin C, phng php ny b phn i bi C++ chun.(Ngha l, n khng c khuyn co.). Vi l do ny, sch ny s s dng u mc C++ kiu mi trong tt c cc cu lnh. Nu trnh bin dch (ca) cc bn khng h tr nhng u mc kiu mi cho th vin chc nng C, n gin th nhng u mc c, ging nh C. Since the new-style header is a recent addition to C++, you will still find many, many older programs that dont use it. These programs instead use C-tyle headers, in which a filename is specified. As the old-style skeletal program shows, the traditional way to include the I/O header is as shown here: u mc kiu mi l mt s b sung cho C++, bn s vn cn tm thy gn y nhiu chng trnh c khng s dng n. Nhng chng trnh ny thay vo s dng C- nhng u mc tyle, trong mt tn file c ch r. Nh chng trnh lnh iu khin kiu c hin ra, cch truyn thng bao gm u mc vo/ra c cho thy nh y: #include < iostream.h > This cause the file iostream.h to be include in your program. In general, an oldstyle header will use the same name as its corresponding new-style header with a .h appended. y l cn nguyn tp tin iostream.h c bao gm trong chng trnh ca cc bn. Ni chung, mt u mc kiu c s s dng cng tn vi u mc kiu mi tng ng vi n vi .h c ni vo. As of this writing, all C++ compilers support the old-style headers. However, the 24

old style headers have been declared obsolete, and their use in new programs is not recommended. This is why they are not used in this book. K t s ghi ny, tt c cc trnh bin dch C++ h tr nhng u mc kiu c. Tuy nhin, nhng u mc kiu c c khai bo li thi, v s s dng chng trong nhng chng trnh mi khng c khuyn co. iu ny l l do ti sao chng khng c s dng trong sch ny. Remember: While still common in existing C++ code, old-style headers are obsolete. Ghi Nh : Trong m C++ ph bin hin hu, nhng u mc kiu c l li thi.

NAMESPACES When you include a new-style header in your program, the contents of that header are contained in the std namespace. A namespace is simply a declarative region. The purpose of a namespace is to localize the names of library functions and other such items were simply placed into the global namespace(as they are in C). However, the contents of new-style headers are placed in the std namespace. We will look closely at namespace later in this book. For now, you dont need to worry about them because you can use the statement. Khi bn bao gm mt u mc kiu mi trong chng trnh ca cc bn, ni dung ca u mc kia c cha ng trong namespace std . Mt namespace n gin l mt vng tng thut. Mc ch ca mt namespace l s a phng ha tn ca nhng hm th vin v nhng th mc nh vy khc th n gin c t vo trong ton cc namespace (ging nh trong C). Tuy nhin, ni dung ca nhng u mc kiu mi c t trong namespace std. Chng ti s c ci nhn r rng hn v namespace trong cun sch ny phn sau. By gi, bn khng cn lo lng h bi v bn c th s dng cu lnh using namespace std; to bring the std namespace into visibility (i.e., to put std into the global namespace). After this statement has been complied, there is no difference between working with an old-style header and a new-style one. mang n s r rng cho namespace std ( th d, std c t vo trong namespace ton cc). Sau khi cu lnh ny c tun theo, khng c s khc nhau

25

gia vic lm vic vi mt u mc kiu c v mt u mc kiu mi.

Lm vic Vi mt Trnh bin dch c (WORKING WITH AN OLD


COMPILER): As mentioned, both namespaces and the new-style headers are recent additions to the C++ language. While virtually all new C++ compilers support these features, older compilers might not. If you have one of these older compilers, it will report one or more errors when it tries to compile the first two lines of the sample programs in this book. If this is the case, there is an easy workaround: simply use an old-style header and delete the namespace statement. That is, just replace Nh cp, c namespaces ln nhng u mc kiu mi l s b sung cho ngn ng C++ gn y. Trong khi thc t tt c cc trnh bin dch C++ mi h tr nhng c tnh ny, nhng trnh bin dch c hn c l khng h tr. Nu bn c mt trong s nhng trnh bin dch c ny, n s bo co mt hoc nhiu li khi n th bin tp nhng dng u tin trong chng trnh mu trong cun sch ny. Nu l nguyn nhn, c mt cch gii quyt d dng: n gin s dng mt u mc kiu c v xa khai bo namespace. iu , thay th #include <iostream> using namespace std; bng #include <iostream.h>

This change transforms a modern program into a traditional-style one. Since the old-style header reads all of its contents into the global namespace, there is no need for a namespace statement.

26

S thay i ny thay i mt chng trnh hin i vo trong mt kiu truyn thng. Mt khi u mc kiu c bin dch tt c cc ni dung lm bng lng namespace ton cc, th khng c nhu cu phi khai bo namespace. One other point: For now and for the next few years, you will see many C++ programs that use the old-style headers and that do not include a namespace statement. Your C++ compiler will be able to compile them just fine. For new programs, however, you should use the modern style because it is the only style of program that compiles with Standard C++. While old-style programs will continue to be supported for many years, they are technically noncompliant. Mt im khc: by gi v trong nhng nm tip theo, bn s nhn thy nhiu chng trnh C++ m s dng nhng u mc kiu c m khng bao gm cu lnh namespace. Trnh bin dch C ++ ca cc bn s c kh nng bin tp chng mt cch chnh xc. Cho nhng chng trnh mi, tuy nhin, bn nn s dng kiu mi bi v l kiu duy nht ca chng trnh m s bin tp ph hp vi C++ tiu chun. Trong khi nhng chng trnh kiu c s tip tc c h tr trong nhiu nm, th chng l k thut khng tng hp.

EXERCISE 1. Before proceeding, try compiling the new-style skeleton program shown above. Although it does nothing , compiling it will tell you if your compiler supports the modern C++ syntax. If it does not accept the new-style headers or the namespace statement, substitute the old-style header as described. Remember, if your compiler does not accept new-style code, you must make this change for each program in this book.

Bi tp
1. Trc khi tip tc, th bin tp chng trnh iu khin kiu mi c cho thy trn. Mc d n khng l g, hy bin tp n s cho bn bit nu trnh bin dch ca bn h tr c php C++ hin i. Nu n khng chp nhn nhng u mc kiu mi hay khai bo namespace, thay th u mc kiu xa nh c m t. Nh, nu trnh bin dch ca bn khng chp nhn m kiu mi, bn phi lm s thay i ny cho mi chng trnh trong sch ny.

27

1.3. C++ CONSOLE C++

I / O - BN GIAO TIP NHP/XUT

Since C++ is a superset of C, all elements of the C language are also contained in the C++ language. This implies that all C programs are also C++ programs by default.(Actually, there are some very minor exceptions to this rule, which are discussed later in this book.) V C++ l siu tp hp ca C nn mi phn t ca ngn ng C u c cha trong ngn ng C++. iu ny ch ra rng cc chng trnh C cng l cc chng trnh C++. (Thc s, c mt vi ngoi l nh i vi quy tc s c tho lun vo cui sch). Therefore, it is possible to write C++ programs that look just like C programs. While there is nothing wrong with this per se, it dose mean that you will not be taking full advange of C++. To get the maximum benefit from C++, you must write C++-style programs. This means using a coding style and features that are unique to C++. Do c th vit cc chng trnh C++ trng ging nh cc chng trnh C.Hu ht nhng ngi lp trnh C++ vit cc chng trnh s dng kiu v cc c im duy nht cho C++. L do l gip bn bt u lm quen vi cc thut ng C++ thay v ngh v C. Cng vy, bng cch dng cc c im C++, bn s cho ngi khc hiu ngay chng trnh ca bn l C++ ch khng phi l chng trnh C. Perhaps the most common C++-specific feature used by C++ programmers is its approach to console I/O. While you may still use functions such as printf() anh scanf(), C++ provides a new, and better, way to perform these types of I/O operations. In C++, I/O is performed using I/O operators instead of I/O function. The output operator is << anh the input operator is>>. As you know, in C, these are the left and right shift operators, respectively. In C++, they still retain their original meanings (left and right shift) but they also take on the expanded role of performing input and output. Consider this C++ statement: C l c im thng dng nht ca C++ c nhng ngi lp trnh C++ s dng l bn giao tip nhp/xut. Trong khi bn vn s dng nhng hm nh printf() v scanf(), C++ a ra mt cch mi v tt hn thc hin cc thao tc nhp/xut ny. Trong C++, nhp/xut c thc hin bng cch dng cc ton t nhp/xut thay v cc hm nhp/xut.Ton t xut l << v ton t nhp l >>. Nh bn bit, trong C y l nhng ton t dch chuyn phi v tri. Trong C++, chng vn

28

cn ngha gc (dch chuyn phi v tri) nhng chng cng c mt vai tr m rng thc hin nhp xut. Xt cu lnh C++ sau: cout << This string is output to the screen.\n; This statement causes the string to be displayed on the computers screen. cout is a predefined stream that is automatically linked to the console when a C++ program begins execution. It is similar to Cs stdout. As in C, C++ console I/O may be redirected, but for the rest of this discussion, it is assumed that the console is being used. Cu lnh ny hin th mt chui ln mn hnh my tnh. cout l mt dng (stream) c nh ngha trc, t ng lin kt vi bn giao tip khi chng trnh C++ bt u chy. N tng t nh stdout trong C. Nh trong C, bn giao tip Nhp/Xut C++ c th c nh hng li, nhng trong phn tho lun ny gi thit l bn giao tip vn c dng. By using the << output operator, it is possible to output any of C++s basic types. For example, this statement outputs the value 100.99: Bng cch dng ton t xut << c th xut bt k loi c bn no ca C++. V d cu lnh sau xut gi tr 100,99: cout << 100.99; In general, to output to the console, use this form of the << operator: cout << expression; Here expression can be any valid C++ expression-including another output expression. Ni chung, dng dng tng qut ca ton t << xut: cout << expression; y, expression l mt biu thc C++ bt k - k c biu thc xut khc. To input a value from the keyboard, use the >> input operator. For example, this fragment inputs an integer value into num: nhp mt gi tr t bn phm, dng ton t nhp >>. V d, on sau y nhp mt gi tr nguyn vo num :

29

int num; cin >> num; Notice that num is not preceded by an &. As you know, when you use Cs scanf() function to input values, variables must have their addresses passed to the function so they can receive the values entered by the user. This is not the case when you are using C++s input operator.(The reason for this will become clear as you learn more about C++.) Ch rng trc num khng c du &. Nh bn bit, khi nhp mt gi tr bng cc cch dng hm scanf() ca C th cc bin c a ch c truyn cho hm v th chng nhn c gi tr do ngi s dng nhp vo. Khi dng ton t nhp ca C++ th li khc.(Khi hc nhiu hn v C++, bn s hiu r l do ny). In general, to input values from the keyboard, use this form of >>. Tng qut, nhp mt gi tr t bn phm th dng dng >>: cin >> variable;

Note: The expanded roles of << and >> are examples of operator overloading.
Ch : Cc vai tr m rng ca << v >> l nhng v d v qu ti ton t. In order to use the C++ I/O operators, you must include the header <iostream.h> in your program. As explained earlier, this is one of C++s standard headers and is supplied by your C++ compiler. s dng cc ton t Nhp/Xut C++, bn phi ghi r file tiu iostream.h trong chng trnh ca bn. y l mt trong nhng file tiu chun ca C++ do trnh bin dch C++ cung cp.

CC V D (EXAMPLES):
1. This program outputs a string, two integer values, and a double floating-point value: 1. Chng trnh sau xut mt chui, hai gi tr nguyn v mt gi tr du phy ng kp: #include <iostream> 30

using namespace std; int main() { int i, j; double d; i = 10; j = 20; d = 99.101; cout << Here are some values:; cout << i; cout << ; cout < j; cout < ; cout < d; return 0; } The output of this program is shown here. Chng trnh xut ra nh sau. Here are some values: 10 20 99.101 Remember: If you working with an older compiler, it might not accept the newstyle headers and the namespace statements used by this and other programs in this book. If this is the case, substitute the oldstyle code described in the preceding section. Ghi nh: Nu bn lm vic vi mt trnh bin dch c, n c th khng chp nhn nhng u mc kiu mi v khai bo namespace c s dng bi n v nhng chng trnh trong cun sch ny. Nu gp trng hp ny, thay th bng cc m kiu c c m t trong phn

31

trc.

2. It is possible to output more than one value in a single I/O expression. For example, this version of the program described in Example 1 shows a more efficient way to code the I/O statements: 2. C th xut nhiu hn mt gi tr trong mt biu thc Nhp/Xut n. V d, chng trnh trong v d 1 trnh by mt cch c hiu qu hn m ha cu lnh Nhp/Xut: #include <iostream> using namespace std; int main() { int i, j; double d; i = 10; j =20; d = 99.101; cout << Here are some values:; cout << i << << j << << d; return 0; } Here the line y, dng cout << i << << j << << d; outputs several items in one expression. In general, you can use a single statement to output as many items as you like. If this seems confusing, simply remember that the << output operator behaves like any other C++ operator and

32

can be part of an arbitrarily long expression. xut nhiu mc trong mt biu thc. Tng qut, bn c th s dng mt cu lnh n xut nhiu mc ty bn mun. Nu s nhm ln th ch cn nh rng ton t xut << hot ng nh bt k ton t no ca C++ v c th l mt phn ca mt biu thc di bt k. Notice that you must explicitly include spaces between items when needed. If the spaces are left out, the data will run together when displayed on the screen. Ch rng bn phi nhng khong trng gia cc mc khi cn. Nu c cc khong trng th d liu cng chy khi c biu th trn mn hnh. 3. This program prompts the user for an integer value: 3. Chng trnh sau nhc ngi s dng nhp vo mt gi tr nguyn: #include <iostream>] using namespace std; int main() { int i; cout << Enter a value:; cin >> i; cout << Heres your number: << i << \n; return 0; } Here is a sample run: y l mu c chy: Enter a value: 100 Heres your number: 100

33

As you can see, the value entered by the user is put into i. Nh bn thy, gi tr nhp vo bi ngi s dng c t vo i.

4. The next program prompts the user for an integer value, a floating-point value, and a string. It then uses one input statement to read all three. 4. Chng trnh tip theo nhc ngi s dng nhp mt gi tr nguyn, mt gi tr du phy ng v mt chui. Sau s dng lnh nhp c c ba. #include <iostream> using namespace std; int main() { int i; float f; char s[80]; cout << Enter an integer, float, and string:; cin >> i >> f >> s; cout << Heres your data:; cout << i << << f << << s; return 0; }

As this example illustrates, you can input as many items as you like in one input statement. As in C, individual data items must be separated by whitespace characters (spaces, tabs, or newlines). Trong v d ny, bn c th nhp nhiu mc ty bn mun trong mt cu lnh nhp.

34

Nh trong C, cc mc d liu ring l phi cch nhau mt khong trng (khong trng, tab hoc dng mi). When a string is read, input will stop when the first whitespace character is encountered. For example, if you enter the following the preceding program: Khi c mt chui, my s ngng c khi gp k t khong trng u tin. V d, nu bn nhp d liu cho chng trnh trc y: 10 100.12 This is a test the program will display this: th chng trnh s hin th nh sau: 10 100.12 This The string is incomplete because the reading of the string stopped with the space after This. The remainder of the string is left in the input buffer, awaiting a subsequent input operation.(This is similar to inputting a string by using scanf() with the %s format.) Chui khng c y do vic c chui phi ngng li khi gp khong trng sau This. Phn cn li ca chui nm trong b m, i cho n khi c thao tc nhp tip theo. (iu ny tng t vi vic nhp chui bng hm scanf() c nh dng %) . 5. By default, when you use >>, all input is line buffered. This means that no information is passed to your C++ program until you press ENTER. (In C, the scanf() function is line buffered, so this style of input should not be new to you.) To see the effect of line-buffered input, try this program: 5. Theo mc nh, khi bn dng >>, mi d liu nhp c a vo b m. Ngha l khng c thng tin c truyn cho chng trnh C++ ca bn cho n khi bn nhn ENTER. (Hu ht cc trnh bin dch C cng s dng d liu nhp c a vo b m khi lm vic vi hm scanf(), v th d liu nhp a vo b m khng phi l iu mi l i vi bn.) thy r hiu qu ca d liu c a vo b m, bn th chy chng trnh sau y: #include <iostream> using namespace std; int main() 35

{ char ch; cout << Enter keys, x to stop.\n; do { cout << : ;

cin >> ch; } while (ch != x); return 0; }

When you test this program, you will have to press ENTER after each key you type in order for the corresponding character to be sent to the program. Khi chy chng trnh ny, bn phi nhn Enter sau mi ln bn nh mt phm tng ng vi k t a vo chng trnh.

BI TP(EXERCISES)
1. Write a program that inputs the number of hours that an employee works and the employees wage. Then display the employees gross pay.(Be sure to prompt for input.) 1. Vit chng trnh nhp s gi lm vic ca mt nhn vin v tin lng ca nhn vin. Sau hin th tng tin lng. (Phi chc chn c nhc nhp d liu.) 2. Write a program that converts feet to inches. Prompt the user for feet and display the equivalent number of inches. Have your program repeat this process until the user enters 0 for the number of feet. 2. Vit chng trnh chuyn i feet thnh inches. Nhc ngi s dng nhp feet v

36

hin th gi tr tng ng ca inches. Lp li qu trnh cho n khi ngi s dng nhp s 0 thay cho feet. 3. Here is a C program. Rewrite it so it uses C++-style I/O statements. 3. y l mt chng trnh C. Vit li chng trnh ny bng cch s dng cc cu lnh Nhp/Xut theo kiu C++. /* Convert this C program into C++ style. This program computes the lowest common denominator. */ #include <stdio.h> int main(void) { int a, b, d, min; printf ("Enter two numbers:"); scanf ("%d%d", &a, &b); min = a > b ? b: a; for (d = 2; d < min; d++) if (((a%d)==0)&&((b%d)==0)) break; if (d==min) { printf ("No common denominators\n"); return 0; } printf ("The lowest common denominator is %d\n",d); return 0; }

37

1.4. C++ COMMENTS LI CH GII TRONG C++


In C++, you can include comments in your program two different ways. First, you can use the standard, C-like comment mechanism. That is, begin a comment with /* and end it with */. As with C, this type of comment cannot be nested in C++. Trong C++, bn c th a vo li ch gii trong cc chng trnh theo hai cch. Th nht, bn c th dng c ch chun v li ch gii nh trong C. Ngha l li ch gii bt u bng /* v kt thc bng */. Nh bit trong C, loi li ch gii ny khng th lng nhau trong C++. The second way that you can add a remark to your C++ program is to use the single-line comment. A single-line comment begins with a // and stops at the end of the line. Other than the physical end of the line (that is, a carriage-return/ linefeed combination), a single-line comment uses no comment terminator symbol. Cch th hai a nhng li ch gii vo chng trnh C++ l s dng li ch gii n dng. Li ch gii n dng bt u bng // v kt thc cui dng. Khc vi kt thc vt l ca mt dng (nh t hp quay tr v u dng y dng), li ch gii n dng khng s dng k hiu chm dt dng. Typically, C++ programmers use C-like comments for multiline commentaries and reserve C++-style single-line comments for short remarks. Ni chung, ngi lp trnh C++ dng li ch gii ging C cho nhng li ch gii a dng v dng li ch gii theo kiu C++ khi li ch gii l n dng.

CC V D (EXAMPLES):
1. Here is a program that contains both C and C++-style comments: 1. y l chng trnh c cc li ch gii theo kiu C v C++. /* This is a C-like comment. This program determines whether an integer is odd 38

or even. */ #include <iostream> using namespace std; int main() { int num; // this is a C++, single-line comment // read the number cout << Enter number to be tested:; cin >> num; // see if even or odd if ((num%2)==0) cout << Number is even\n; else cout << Number is odd\n; return 0; } 2. While multiline comments cannot be nested, it is possible to nest a single-line comment within a multiline comment. For example, this is perfectly valid: 2. Trong khi cc li ch gii theo kiu C khng th lng nhau th li ch gii n dng C++ c th lng vo trong li ch gii a dng theo kiu C. V d sau y l hon ton ng. /* This is a multiline comment inside of which // is need a single-line comment. Here is the end of the multiline comment. */

39

The fact that single-line comments can be nested within multiline comments makes it easier for you to comment out several lines of code for debugging purposes. S vic cc li ch gii n dng c th c lng trong cc li ch gii a dng d dng lm cho bn trnh by li ch gii nhiu dng m nhm mc ch g ri.

BI TP (EXERCISES):
1. As an experiment, determine whether this comment (which nests a C-like comment within a C++-style, single-line comment) is valid:

1. Hy xc nh xem li ch gii sau y c hp l khng: // This is a strange /* way to do a comment */ 2. On your own, add comments to the answers to the exercises in Section 1.3. 2. Hy thm nhng li ch gii theo cch ring ca bn cho phn tr li trong phn bi tp 1.3.

1.5. CLASSSES: A FIRST LOOK - LP : CI NHN U TIN


Perhaps the single most important feature of C++ is the class. The class is the merchanism that is used to create objects. As such, the class is at the heart of many C++ freartures. Although th subject of the classes is covered in great detail throughout this book, classes are so fundamental to C++ programming that a brief overview is necessary here. C l c im quan trng ca C++ l lp. Lp l c cu c dng to i tng. Nh th lp l trung tm ca nhiu c im ca C++. Mc du ch ti ca lp c bao ph chi tit khp c sch ny, lp l i tng c bn khai bo mt chng trnh C++ , ngn gn tng quan l s cn thit.

40

A class is declared using the class keywork. The syntax of a class declaration is similar to that of structure. Its general from is shown here: Mt lp c khai bo s dng t kha lp. C php khai bo ca mt lp l cu trc. Dng tng qut nh sau : class { //private functions and variables public: // private functions and variables }; In a class declaration, the object-list is optional. As with a structure, you can declared class object later, as needed. While the class-name is also technically optional, from a practical point of view it is virtually always needed. The reason for this is that the class-name becomes a new type name that is used to decrared object of the class. Trong khai bo lp, danh sch i tng l ty chn. Ging nh cu trc, bn c th khai bo cc i tng sau ny khi cn. Tn lp l ty chn c tnh k thut, t quan im thc hnh thc s n lun lun cn n. L do la tn lp tr thnh mt kiu mi c dng khai bo cc i tng ca lp Function and variables declared inside a class declaration are said to be members of that class. By ddefault, all function and variables declared insinde a class are private to that class. This meas that they are accessible only by other members of that class. To declare public class members, the public keywork is used, followed by a colon. All function and varaibles declared after the public specifter are accessible both by other members of the class and by any other part of the program that contains the class. Cc hm v bin khai bo bn trong khai bo lp cc thnh vin ca lp . Theo mc nh, tt c cc hm v khai bo bn trong ca lp l thuc bn trong ca lp . khai bo thnh vin chung ca lp, ngi ta dng t kha public ,theo sau hai du chm.Tt c cc hm v bin c khai bo sau t kha public c truy cp bi thnh vin khc ca chng trnh ca lp . class-name

41

Here is a simple class declaration y l v d khai bo lp: class myclass { //private to myclass int a; public : void set_a(int num); int get_a(); };

This class has one private variables called a, and two public functions, set_a() and get_a(). Noties are declared within a class using their prototype froms, that are declared to be part of a class are called members functions. Lp ny c mt bin ring l a, v hai hm chung l set_a() v get_a(). Ch rng cc hm c khai bo trong mt lp s dng dng nguyn mu ca n.Cc hm c khai bo l mt phn ca lp c gi l cc hm thnh vin. Since a is private, it is not accessible by any code outside myclass. However, since set_a() and get_a() are members of myclass, they can access a. Further, get_a() and get_a() are declared as public program that contains myclass V mt bin ring, n khng th c truy cp bt k m no ngoi myclass. Tuy nhin, v set_a() v get_a() l cc thnh vin ca myclass nn c th truy cp c a. Hn na, get_a() v set_a() c khai bo l cc hm thnh vin chung ca myclass v c th c gi bt k phn no ca chng trnh c cha myclass. Although the function get_a() and set_a() are declared by myclass, they are not yet defined. To defined a members function, you must link the type name of the class with the class name followed by two colons. The two colons are called the scope reslution operator. For expamle, here is the way the members function set_a() and get_a() are defined :

42

Mc d cc hm get_a() v set_a() c khai bo bi myclass, chng vn cha c nh ngha. nh ngha mt hm thnh vin, bn phi lin kt tn kiu ca lp ca tn hm. Thc hin iu ny cch t trc tn hm ca lp tip theo l hai du chm. Hai du chm c gi l ton t gii phm vi. V d, y l nh ngha cc hm thnh vin set_a() v get_a() : void myclass ::set_a(int num) { a=num; } int myclass :: get_-a() { return a; }

Notice that both set_a() and get_a() have access to a, which is private to myclass. Because set_a() and get_a() are members of myclass, they can direclly access is private data. Ch rng c hai hm set_a() v get_a() c th truy cp a l bin ring ca myclaas. V get_a() v set_a() l cc thnh vin ca myclass nn chng c th truy cp trc tip cc d liu bn trong lp. In general, to define a members function you must this from : Ni chung, khi bn nh ngha mt hm thnh vin th dng: ret-type class-name::func-name(parameter-list) { //body of function } Here class-name is the name of the class to which the function belongs. Tn y lp l lp m ti chc nng thuc v lp

43

The declaration of myclass did not define any object of type myclass- it only defines the type of object that will be created when one is actually declared. To create an object, use the class name as a type specifier. Expamle, this line declares two object of type myclass: Khai bo ca lp khng nh ngha i tng no c kiu myclass- n ch nh ngha kiu i tng s c to ra khi c khai bo thc s. tao ra mt i tng, hy s dng tn lp nh b ch nh kiu. V d, sau y l khai bo hai i tng c kiu myclass: myclass ob1,ob2 // these are object of type myclas

Cn nh (Remember):
A clas declaration is a logical abstraction that defines a new type. It determines what an object of that type will look like. An object declaration creates a physical antity of that type. That is, an object occupies memory space, but a type definition does not. Khai bo lp l mt s tru tng logic nh ngha mt kiu mi xc nh i tng c kiu s trng nh th no.Mt khai bo i tng to ra mt thc th vt l c kiu ..Ngha l mt i tng chim khng gian b nh,cn nh ngha kiu th khng. Once an object of a class has been created, your program can reference its public members by using the dot ( period) operator in much the same way that structure members are accessed. Assuming the preceding object declaration, the following statement calls set_a() for object ob1 and ob2. Khi i tng to ra mt lp, chng trnh ca bn c th c tham chiu ca mt lp to ra cc thnh vin chung ca i tng bng nhng ton t im.Gi s khai bo i tng trc y, cu lnh sau gi hm cho cc i tng ob1 v ob2 ob1.set_a(10) //sets ob1s version of a to 10 ob2.set_a(99) // sets ob2s version of a to 99

As the coments indicate, these statements set ob1s copy of a to 10 and ob2s copy to 99. Each object contains its own copy of all data declared within the class. This means that that ob1s a is distinct and different from the a linked to ob2 Nh ch r trong li ch gii, cc lnh ny t ra bn sao cua a ca ob1 bng 10 v bn sao ca a ca ob2 bng 99. Mi i tng c bn sao ring ca n v mi d

44

liu c khai bo trong lp.Ngha l a trong ob2.

Cn nh (Remember):
Each object of a class has its own copy of every variable declared within the class. Mi i tng ca mt lp c bn sao ring ca cc bin c khia bo trong lp.

Cc v d (Expamles):
14. As a simple first example, this program demonstrates myclass, described in the text. It sets the value of a for ob1 and ob2 and then display as value for each object.

1. Nh l mt v d n gin u tin, chng trnh sau y dng myclass c khai bo trong vn bn. dt gi tr va a cho ob1 v ob2 v hin th gi tr a cho mi i tng : #include "stdafx.h" #include "iostream.h" Using namespace std; class myclass { //private to myclass int a; public : void set_a(int num); int get_a(); }; void myclass::set_a(int num) { a=num; } int myclass::get_a() { return a; } int main() { myclass ob1,ob2; ob1.set_a(10); ob2.set_a(99);

45

cout<<ob1.get_a()<<"\n"; cout<<ob2.get_a()<<"\n"; return 0; } As you should epect, this program display the values 10 and 99 on the sreen. Chng trnh xut ra mn hnh gi tr 10 v 99 ra mn hnh. 2. In myclass from the preceding expamle, a is private. This means that only member function of myclass can access it directly. ( This is one reason why the public function get_a() is required.) If you try to access a private member of a class from some part of of your program that is not a member of that class, a compile-time error will result. For example, assuming that myclass is defined as shown in the preceding example, the following main() function will cause an error : 14. Trong myclass v d trn, a l bin ring.Ch cc hm thnh vin ca myclass mi c th truy cp trc tip. ( y l l do v sao phi c hm get_a())Nu bn truy cp mt thnh vin ring lca lp t mt b phn no ca chng trnh khng phi l thnh vin ca lp , th s n li bin dch. V dm gi s rng myclass c nh ngha nh trong v d trc y, hm main() sau y s to ra li : //This fragment contains an error: int main() { myclass ob1,ob2; ob1.a=10; // ERROR ! cannot access private member ob2.a=99; // by non-member functions. cout<<ob1.get_a()<<"\n"; cout<<ob2.get_a()<<"\n"; return 0; }

3. Just as there can be public member functions, there can be public member variables as well. For example, if a were declared in the public section of myclass, a could be referenced by any part of the program, as shown here: 3. Khi c cc hm thnh vin chung, cng c cc bin thnh vin chung. V d, nu a c khai bo la bin chung ca myclass th a cng c th c tham chiu, nh sau y: #include "stdafx.h" 46

#include "iostream.h" class myclass { //now a is public public : int a; //and there is no need for }; int main() { myclass ob1,ob2; ob1.a=10; ob2.a=99; cout<<ob1.a<<"\n"; cout<<ob2.a<<"\n"; return 0; }

set_a() or get_a()

In this example, since a is declared as a public member of myclass, it is directly accessible from main(). Notice how the dot operator is used to access a. In general, when you are calling a member function or accessing a member variable from outside its class, the objects name following by the dot operator followed by the member you are referring to. Trong v d ny, v a c khai bo l thanh vin chung ca myclass, n c th c truy cp trc tip t hm main(). Ch cch s dng ton t im truy cp a.Ni chung, d bn gi hm thnh vin hoc truy cp bin thnh vin, tn i tng phi c km ton t im, tip theo l tn thnh vin ch r thnh vin no ca i tng no bn cn tham chiu. 4. To get a staste of the power of object, lets look at a more practical example. This program creates a class called stack that implements a stack that can be used to store characters : 4. bit r nng lc ca cc i tng, hy xt mt v d thc t hn:Chng trnh ny to ra lp stack thi hnh mt ngn xp dng lu tr k t : #include "stdafx.h" #include "iostream.h" Using namespace std; #define SIZE 10 // Declare a stack class for characters 47

class stack { char stck[SIZE]; // holds the stack int tos ; // index of top of stack public: void init(); //intialize stack void push(char ch); //push charater on stack char pos(); //pop character from stack }; //Initialize the stack void stack::init() { tos=0; } //Push a charater. void stack::push(char ch) { if(tos==SIZE) { cout<<"Day stack"; return ; } stck[tos]=ch; tos++; } //Pop a charater char stack::pos() { if(tos==0) { cout<<"Stack is empty "; return 0; //return null on empty stack } tos--; return stck[tos]; } int main() { stack s1,s2; //create two stacks int i; // Initialize the stacks s1.init(); s2.init(); s1.push('a');

48

s2.push('x'); s1.push('b'); s2.push('y'); s1.push('c'); s2.push('z'); for(i=0;i<3;i++) cout<<"Pos s1: "<<s1.pos()<<"\n"; for(i=0;i<3;i++) cout<<"Pos s2 : "<< s2.pos()<<"\n"; return 0; }

This program display the following output: Kt qu xut ra mn hnh l: pos pos pos pos pos pos s1: s1: s1: s2: s2: s2: c b a z x y

Lets take a close look at this program now. The class stack contains two private: stack and tos. The array stck actually holds the charaters pushed onto the stack, and tos contans the index to the top of the stack. The publicc stack function are init(), push(), and pop(), which initialize the stack, push a value, and pop a value, respectively. By gi xt chng trnh k hn. Lp stack c hai bin ring, stck v tos. Mng stck cha cc k t ua vo trong ngn xp, v tos cha cc ch s trn nh ngn xp. Cc hm chung ca init(), push() v pos() ln lt khi ng ngn xp, y mt gi tr vo v ko mt gi tr ra. Insider main(), two stacks, s1 and s2, are created, and three charaters are pushed onto each stack. It is important to understand that each object is separate from the other. That is, the charater pushed onto s1 in no way affect the charaters pushed onto s2. Each object contians its own copy of stck and tos. This concept is fundamental to undertanding objects. Although all object creates and maintians its own data. Trong hm main() hai ngn xp s1 v s2 c ta ra v 3 k t c y vo mi ngn xp. iu quan trng l hiu rng mi i tng ngn xp l cch bit nhau. Ngha l cc k t y vo trn s1 khng nh hng n cc k t c y vo s.

49

Mi i tng c mt bn sao stck v tos ring. Khi nin ny l c bn hiu cc i tng. Mc du tt c cc i tng ca mt lp s dng chung cho cc hm thnh vin, mi i tng to v gi d liu ring.

BI TP (Exrcises):
If you have not done so, enter and run the programs shown in the examples for this section. 1. Nu bn khng tin, hy nhp v chy chng trnh trong cc phn ny.

Create a class card that maintians a library card catalog entry. Have the class store a books title, author, and number of copies on hand. Store the title and author as string and the munber on hand as an integer. Use a public member function called store() to store a books information and a public member function called show() to display the information. Include a short main() function to demonstrate the class. 2. Ta lp card gi mc nhp catalog ca th th vin. C lp cha tn sch , tc gi, s bn. Lu tr ta v tc gi di mt chuiv s bn nh mt s nguyn.Dng hm thnh vin chung store() lu tr thng tin v sch v hm thanh vin chung show( ) hin th thng tin.C hm main() ghi r lp. Create a queue class that maintians a circular queue of integers. Make the queue size 100 integers long. Include a short main() function that demonstrates its operation. 3. To mt lp hm queue gi cc hm s nguyn. To mt kch thc hang di 100 s nguyn. C hm main() ch r php ton ca n.

1.6. SOME DIFFERENCE BETWEENCE AND C++ - MT S KHC BIT GIA C V C++
Although C++ is a superset of C, there are some small difference between the two, and a few are woth knowing from the star. Before proceeding, lets take time to example them. Mc d C++ l mt siu tp hp ca C, vn c mt vi im khc bit gia C v

50

C++ m chn s c nh hng n khi bn vit chng trnh C++. D m im khc nhau ny l nh, chng cng c trong cc chng trnh C++. Do cn phi xt qua nhng im khc nhau ny. First, in C, when a function takes no paramentes, its prototype has the work void inside its function parameter list. For example, in C, if a function called f1() takes no parameters (and return a char), its prototype will look like this: Trc ht, trong C, khi mt hm khng c tham s, nguyn mu ca n c ch void bn trong danh sch tham s ca hm. V d, trong C, nu hm c gi l f1() khng c tham s th nguyn mu ca n c dng : char f1(void); However, in C++, the void is optional. Therefore, in C++, the prototype for f1() is usually written like this: Tuy nhin trong C++, void l ty . Do , trong C++, nguyn mu cho f1() thng c vit nh sau : char f1();

C++ differs from C in the way that an empty parameter list is specifed. If the preceding prototype had occurred in a C program, it would simply means that nothing is said about the parameters. This is the reason that the preceding example did not explicitly use void to declare an empty parameter list.(The use of void to declare an empty parameter list is not illegal; it is just reduntant. Since most C++ programmers pursue effciency with a nearly religious zeal, you will almost never see void used in this way.) Remember, in C++, these two declarations are equivalent. C++ khc vi C ch danh sch tham s rng c ch r. Nu nguyn mu trn y xy ra trong mt chng trnh C th khng c g cp n cc tham s ca hm. Trong C++, iu c ngha l hm khng c tham s. y l l do v sao nhiu v d trc y khng s dng void khai bo trong danh sch cc tham s rng. (Dng void khai bo danh sch cc tham s rng l khng hp l, v nh vy l tha. Do hu ht ngi lp trnh C++ rt cn thn nh khng bao gi thy void trong trng hp ny). Cn nh, trong C++, hai loi khai bo ny l tng ng: int f1(); int f1(void);

51

Another subtle differencebetween C and C++ is that in a C++ program, all function must be prototype. Remmember, in C, prototypes are recommended but technically optional. In C++, they are required. As the examples from the previous section show, a member functions prototype contianed in a class also serves as its general prototype, and no othee separate prototupe is required. Mt s khc bit rt t gia C v C++ l ch trong mt chng trnh C++, tt c cc hm phi c dng nguyn mu, cn trong C dng nguyn mu, nhng ch l ty chn. Trong C++ cc hm c i hi nh th. Cc v d trong phn trc ch r, dng nguyn mu ca hm thnh vin trong mt lp cng dng dng nguyn mu ca n v khng c nguyn mu no c i hi. A third difference between C and C++ is that in C++, if a function is declared as returning a value, it must return a value. That is, if a funciton has return type other than void, any return statement within that function must contian a value. In C, a non- void function is not required to actually return a value. If it doesnt , a garbage value is returned im khc bit gia C v C++ l im khai bo cc bin cc b. Trong C, cc bin c b ch khai bo u mi khi, trc bt k cu lnh tc ng no. Trong C++, cc bin c b c khai bo bt k ch no.Li im ca cch ny l cc bin cc b c th c khai bo gn hn chng c dng, iu ny gip trnh c nhng kt qu ngoi mun. In C, if you dont explicitly specify the return of a function, an integer return type is assumed. C++ has dropped the default-to-int rule. Thus, you must explicitly declare the return type of all functions. Trong C, nu bn khng ch nh r s tr v ca mt hm, tr v mt s nguyn khng c tht. One other difference between C and C++ that you will commonly encounter in C++ program has to do with where local variables can be declared. In C, local variables can be declared only at the start of a block, prior to any action statements. In C++, local variables can be declared anywhere. One advantage of this approach is that local variables can be declared close to where they are first used, thus helping to prevent unwanted side effects. Mt trong nhng s khc bit gia C v C++ ch l bn c th bt gp ph bin trong C++ chng trnh phi lm vic vi cc bin s logic c th l cng phai. Trong C, cc gi tr c th cng khai trong mt khi chng trnh,trong cu lnh hnh ng. Trong C++, cc gi tr c th cng khai bt k ch no. Mt s thun li c th l ph bin khi ng n v dng bt k ch no, v th khng lm nh hng n cc gi tr bn cnh.

52

Finally, C++ define the bool data type, which is used to store. Boolean (i.e., true/flase) values. C++ also defines the keywords true and false, which are the only values that a value of type bool can have. In C++, the outcome of the relational and logical operator is a value of type bool,and all conditional statements must evaluate to a bool value. Although this might at first seem to be big change from C, it isnt. In fact, it is virtually transparent. Heres why: As you know,in C, true is any nonzero value and fales is 0. This still holds in C++ because any nonzero value is automatically converted into true and any 0 value is uatomatically converted in to false when used a boolean expression. The reverse also occurs: true is converted to 1 and false is converted to 0 when a bool values is used in an integer expression. The addition of bool allows more thorough type checking and gives you a way to difference between boolean and integer types. Of course, its use is optional; bool is mostly a convenience. Kt lun li, C++ nh ngha kiu d liu bool, chng s cung cp c dung. Bool tr v gi tr true /flase. C++ nh ngha sn gi tr treu flase,s c tr v gi tr khi khai bo kiu d liu bool. Trong C++, lin h ca kt qu v ton t logic ca cho gi tr mc nh, v tt c cc khai ba bt buc trong kiu d liu bool. Mc d n l s thay i ln i vi C, khng phi vy. S tht, n cng ging nh C. Ti v: nu bn hiu trong C , true l khc 0 v false l 0. Cng nh vy trong C++, nu gi tr khc 0 th n t ng cp nht l true cn nu gi tr l 0 th n t ng cp nht l false khi trong biu thc boolean. Xut hin s o ngc : true l 1 cn false l 0 khi trong khai bo kiu d liu bool. Php cng trong bool cho n thc hin nhiu kiu kim tra v, m li cho bn s khc bit vi boolean v kiu s nguyn. Tht vy, n c dng ty chn; bool thng thun li hn. block, prior to any action statements. In C++, local variables can be declared anywhere. One advantage of this approach is that local variables can be declared close to where they are first used, thus helping to prevent unwanted side effects. khi, trc li gi hm. Trong C++, nhng bin cc b c th c khai bo bt c u. Mt trong nhng li th ca cch tip cn ny l nhng bin cc b c th c khai bo gn nht ni m chng c dng ln u, iu ny gip trnh khi nhng tc ng ph khng mong mun. Finally, C++ defines the bool data type, which is used to store Boolean (i.e, true/false) values. C++ also defines the keywords true and false, which are the only values of type bool can have. In C++, the outcome of the relational and logical operators is a value of type bool, and all conditional statements must evaluate to a bool value. Although this might at first seem to be a big change from C, it isnt. In fact, it is virtually transparent. Heres why: As you know, in C, true is any nonzero value and false is 0. This still holds in C++ because any 53

nonzero value is automatically converted into true and any 0 value is automatically converted into false when used in a Boolean expression. The reverse also occurs: true is converted to 1 expression. The addition of bool allows more thorough type checking and gives you a way to differentiate between Boolean and integer types. Of course, its use is optional; bool is mostly a vonvenience. Cui cng, C++ nh ngha kiu d liu c bool, c dng lu gi tr c ( n/sai). C++ cn nh ngha nhng t kha true v false, ci m ch c th cha mt gi tr c duy nht. Trong C++, nhng ton t quan h v ton t logic phi tr v gi tr c. Mc d iu ny c v nh l mt s thay i ln t C, nhng khng phi vy. Thc ra, cng d hiu. y l l do ti sao: Nh bn bit, trong C, true l mt gi tr khc 0 v false l 0. iu ny vn cn trong C++ v bt k gi tr khc 0 no cng t ng c chuyn thnh true v bt k gi tr 0 no cng u t ng c chuyn thnh false khi mt biu thc so snh c gi. Ngc li true c chuyn thnh 1. Vic thm c hiu cng cho php s kim tra k lng hn v a ra cho bn mt cch khc gia php ton Bool v s nguyn. D nhin, vi s chn la ny, c hiu l thun tin nht.

V D (EXAMPLES):
In a C program, it is common practice to declare main() as shown here if it takes no command-line arguments: Trong mt chng trnh C, thng thng khai bo hm main() nh khng c i dng lnh: int main(void) However, in C++, the use of void is redundant and unnecessary. Tuy nhin, trong C++, vic dng void l tha v khng cn thit. This short C++ program will not compile because the function sum() is not prototyped: Chng trnh C++ ngn sau s khng bin dch v hm sum() khng dng nguyn mu. // This program will not compile sau nu nh

54

#include <iostream> using namespace std;

int main() { int a,b,c; cout<< "Enter two numbers: "; cin>>a>>b; c = sum(a,b); cout<<" Sum is: "<<c;

return 0; } // This function needs a prototype. sum(int a,int b) { return a+b; } Here is a short program that illustrates how local variables can be declared anywhere within a block: 3. y l mt chng trnh ngn minh ha cc bin cc b c khai bo bt k ch no trong khi: #include <iostream> using namespace std; int main() {

55

int i; // local var declared at start of block

cout<<" Enter number: "; cin>> i;

// copute factorial int j, fact=1; // vars declared after action statements for(j=i;j>=1;j--) fact = fact * j; cout<<" Factorial is: "<<fact; return 0; } The declaration of j and fact near the point of first use is of little value in this short example: however, in large functions, the ability to declare variables close to the point of their first use can help clarify your code and prevent unintentionla side effects. Khai bo j v fact gn vi im s dng u tin c gi tr nh trong v d ngn ny, tuy nhin, trong mt hm ln c th khai bo bin gn im s dng n u tin, gip lm r rng m v ngn nga c nhng h qu ngai mun. c im ny ca C++ thng c dng trong cc chng trnh C++ khi c cc hm ln. The following program creates a Boolean variable called outcom and assigns it the value false. It then uses this variable. 4. Chng trnh sau to ra bin Boolean gi l outcom v t gi tr c l false. Sau ny n c dng nh l mt bin. #include <iostream> using namespace std;

int main() 56

{ bool outcome;

outcome = false; if(outcome) cout<<" true"; else cout<<" false" ; return 0; }

As you should expect, the program displays false. Nh bn thy, chng trnh hin false

BI TP (EXERCISES):
1. The following program will not compile as a C++ program. Why not? 1. Chng trnh sau s khng bin dch nh mt chng trnh C++. Ti sao? // This program has an error. #include <iostream> using namespace std; int main() { f(); return 0; } void f()

57

{ cout<<" this won't work "; }

2. On your own, try declaring local variables at various points in a C++ program. Try the same in a C program, paying attention to which declarations generate errors. 2. Bn th khai bo cc bin cc b ti cc im khc nhau trong mt chng trnh C++. Th lm nh th trong mt chng trnh C, ch khai bo ny to ra li.

1.7. INTRODUCING FUNCTION OVERLOADING - DN NHP S NP CHNG HM:


After classes, perhaps the next most important and pervasie C++ feature is function overloading. Not only does function overloading provide the mechanism by which C++ achieves one type of polymorphism, it also forms the basis by which the C++ programming evironment can be dynamically extended. Because of the importance of overloading, a brief introduction is given here. Sau lp, c l c im quan trng tip theo ca C++ l s np chng hm( qu ti hm). Khng ch s np chng hm cung cp c ch m nh C++ c c tnh a dng, m hm ny cn to nn c s m rng mi trng lp trnh C++. Do tnh quan trng ca np chng hm nn y trnh by ngn gn v hm. In C++, two or more functions can share the same name as long as either the type of their arguments differs or the number of their arguments differs-or both. When two or more functions share the same name, they are said to be overloaded. Oveerloaded functions can operations to be referred to by the same name.

58

Trong C++, hai hoc nhiu hm c th dng chung mt tn nhng c loi i s hoc s i khc nhau hoc c hai khc nhau. Khi hai hay nhiu hm c cng tn, chng c gi l qu ti ( hay np chng). Qu ti hm gip gim bt tnh phc tp ca chng trnh bng cch cho php cc ton c lin h tham chiu cng mt tn. It is very easy to overload a function: simply declare and define all required versions. The compiler will automatically select the correct version based upon the number and/or type of the arguments used to call the function. D dng thc hin qu ti mt hm: ch cn khai bo v nh ngha cc phin bn cn c ca n. Trnh bin dch s t ng chn phin bn ng gi da vo s v/ hoc kiu i s c dng gi hm

Note: It is also possible in C++ to overload operators. However, before you can
fully understand operator overloading, you will need to know more about C++.

Ch :
Trong C++ cng c th thc hin qu ti cc ton t. Tuy nhin, trc khi bn hiu y v qu ti ton t, bn cn bit nhiu hn v C++.

CC V D (EXAMPLES):
One of the main uses for function overloading is to achieve compile-time polymorphism, which embodies the philosophy of one interface, many methods. As you know, in C programming, it is common to have a number of related functions that deffer only by the type of data on which they operate. The classic example of this situation is found in the C standard library. As mentioned earlier in this chapter, the library contains the functions abs(),labs() and fabs(). Which return the absolute value of an integer, a long integer, and a floating-point value, respectively. Mt trong nhng cch s dng chnh i vi s qu ti hm l t c tnh a dng thi gian bin dch, gm nhiu phng php. Nh bn, bit trong lp trnh C thng c mt s hm c lin quan vi nhau, ch khc nhau kiu d liu m cc hm hot ng. V d c in v trng hp ny c tm thy trong th vin chun ca C. Nh trnh by trong phn u chng ny, th vin cha cc hm abs(), labs(),fabs(), cc hm ny ln lt tr v gi tr tut i ca mt s nguyn, mt s nguyn di v mt gi tr du phy ng.

59

However, because three different names are neede due to the three different data types, the situation is more complicated than it needs to be. In all three case, the absolute value is being returned; only the type of the data differs. In C++, you can correct this situation by overloading one name for the three types of data, as this example illustrates: Tuy nhin, do c 3 tn khc nhau tng ng vi 3 kiu d liu nn tnh trng tr nn phc tp hn. Trong c ba trng hp, gi tr tuyt i c tr v, ch c kiu d liu l khc nhau. Trong C++, bn c th hiu chnh tnh trng ny bng cch qu ti mt tn cho ba kiu d liu nh c trnh by trong v d minh ha sau: #include <iostream> using namespace std; //overload abs() three ways int abs(int n); long abs(long n); double abs(double n); int main() { cout<<" Absolute value of -10: "<<abs(-10)<<"\n\n"; cout<<" Absolute 10L)<<"\n\n"; cout<<" Absolute 10.01)<<"\n\n"; return 0; } // abs() for ints int abs(int n) { value value of of -10L: -10.01: "<<abs("<<abs(-

60

cout<<" In integer abs()\n"; return n<0? -1:n; }

// abs( for longs long abs(long n) { cout<< " In long abs()\n"; return n<0?-1:n; } //abs() for doubles double abs (double n) { cout<<" In double abs()\n"; return n<0? -1: n; }

As you can see, this program defines three functions called abs()-one for each data type. Inside main(),abs()b is called using three difference types of arguments. The compiler automatically calls the correct version of abs() based upon the type of data used as an argument. The program produces the following output: Nh bn thy chng trnh ny nh ngha ba hm gi l abs()-mi hm cho mi kiu d liu. Bn trong hm main(), hm abs() c gi bng cch dng ba kiu i s khc nhau. Trnh bin dch t ng gi 4 phin bn ng ca hm abs(), da vo kiu d liu ca i s. In integer abs() Absolute value of -10: 10 61

In long abs() Absolute value of -10L: 10 In double abs() Absolute value of -10.01: 10.01 Although this example is quite simple, it still illustrates the value of function overloading. Because a single name can be used to describe a general class of action, the artifical complexity caused by three slightly different names-in this case, abs(), fabs(), and lab()-is eliminated. You now must remember only one name-the one that describles the general action. It is left to the compiler to choose the appropriate specific version of the function (that is, the method) to call. This has the net effect of reducing complexity. Thus, through the use of polymorphism, three names have been reduced to one. D y l v d n gin, nhng n vn minh ha ngha ca s qu ti hm. Do mt tn c dng m t mt lp tc ng tng qut, nn tnh phc tp nhn to do 3 tn khc nhau gy ra trong trng hp ny l abs(),labs(),fabs() b loi b. Bn phi nh rng ch mt tn mt tn m t tc ng tng qut. Trnh bin dch s chn phin bn thch hp ca hm gi. Chnh iu ny lm gim bt tnh phc tp. Do , nh s dng tnh a dng, ba tn gim cn li mt tn. While the use of polymorphism in this example is fairly trivia, you should be able to see how in a very large program, the one interface, multiple methods * approach can be quite effective. Ta thy rng cch s dng a dng trong v d ny r rng l khng ng k, nhng khi bn xem xt cch n ng dng trong mt chng trnh ln, phng php mt giao din, a phng thc hon ton c th mang li nhiu kt qu. Here is another example of function overloading. In this case, the function date() is overloaded to accept the date either as a string or as three integers. In both case, the function displays the date passed to it. Sau y l mt v d khc v vic ng dng np chng hm. Trong trng hp ny, hm date() c np chng nhn vo ngy thng dng mt chui hay l dng 3 s nguyn. Trong c 2 trng hp, hm u cho kt qu ngy tng ng.
#include <iostream>

62

using namespace std; void date(char *date); void date(int month, int day, int year);

int main() { date(8/23/99); date(8,23,99); return 0; } void date(char *date) { cout << Date: << date << \n; } void date(int month, int day, int year) { cout << date: << month << / ; cout << day << / << year << \n; }

This example illustrates how function overloading can provide the most natural interface to a function. Since it is very common for the date to be represented as either a string or ass three inegers containing the month, day, and year, you are free to select the most convenient form relative to the situation at hand. V d ny minh ha c ng dng ca np chng hm c th to ra c giao din t nhin nht cho mt hm. Nh vy, ngy c trnh by rt chung hoc l bng mt chui hoc l bng 3 s nguyn gm ngy, thng v nm, ty theo hon cnh hin ti m bn t do la chn dng thch hp nht tng ng.

63

3. So far, you have seen overloaded functions that differ in the data types of their argunments. However, overloaded functions can also differ in the number of arguments, as this example illustrates: 3. n y, bn thy c nh ngha chng cc hm khc nhau v kiu d liu ca i s tng ng vi tng hm. Tuy nhin, nh ngha chng cc hm cng c th khc nhau v s lng i s, nh v d minh ha sau:
#include <iostream> using namespace std;

void f1 (int a); void f1(int a, int b);

int main() { f1(10); f1(10,20); return 0; }

void f1( int a) { cout << In f1(int a)\n; }

void f1(int a, int b) { cout << In f1(int a, int b)\n; }

64

4.

It is important to understand that the return type alone is not a sufficient difference to allow function overloading. If two functions differ only in the type of data they return, the compiler will not always be able to select the proper one to call. For example, this fragment is incorrect because it is inherently ambiguous:

4. Tht quan trng hiu l ch xt kiu gi tr tr v thi th khng khc bit cho php nh ngha chng hm. Nu 2 hm ch khc nhau v kiu d liu tr v th trnh bin dch s khng th no gi c hm thch hp. Chng hn nh on chng trnh di y sai v r rng l n rt nhp nhng, m h:
int f1(int a); double f1(int a);. f1(10); //trinh bien dich se goi ham nao day???

As the comment indicates, the compiler has no way of knowing which version of f1() to call. Theo nh ch thch biu th trn th trnh bin dch khng c cch no bit c cn phi gi phin bn no ca hm f1( ).

Bi tp (Exercises):
1. Create a function called sroot() that returns the square root of its argument. Overload sroot() three ways: have it return the square root of an integer, a long integer, and a double. (To actually compute the square root, you can use the standard library function sqrt().) 1. To lp hm sroot() tr v cn bc hai ca i s a vo. nh ngha chng hm sroot() vi 3 kiu: tr v cn bc hai ca mt s nguyn, mt s nguyn di v mt s thc di. ( Thc ra tnh cn bc hai, bn c th s dng hm th vin chun sqrt() ). 2. The C++ standard library contains these three functions: 2. Th vin chun ca C++ cha 3 hm sau: double atof(const char *s) int atoi(const char *s) long atol(const char *) 65

These functions return the numeric value contained in the string pointed to by s. Specifically, arof() return a double. Atoi() returns an integer, and atol returns a long. Why is it not possible to overload these functions? Nhng hm ny tr v gi tr s cha trong mt chui tr ti a ch bin s. Ni mt cch c th, hm atof() tr v mt s thc di, hm atoi() tr v mt s nguyn, v hm atol() tr v mt s nguyn di. Hy cho bit ti sao khng th nh ngha chng 3 hm trn? 3. Create a function called min() that returns the smaller of the two numeric arguments used to call the function. Overload min() so it accepts characters, and doubles as arguments. 3. To lp hm min() tr v gi tr nh hn ca 2 i s khi gi hm. nh ngha chng hm min() n c th nhn vo i s l k t, s nguyn v s thc. 4. Create a function called sleep() that pause the computer for the number of seconds specified by its single argument. Overload sleep() so it can be called with either an integer or a string representation of an integer. For example, both of these calls to sleep() will cause the computer to pause for 10 seconds: 4. To lp hm sleep() dng my tnh trong mt s lng giy nht nh c xc lp bi mt i s a vo. nh ngha chng hm sleep() n c th c gi vi i s l mt s nguyn hoc mt chui s biu din cho mt s nguyn. V d nh c hai li gi hm di y u thc hin dng my tnh trong 10 giy:
sleep(10); sleep(10);

Demonstrate that your functions work by including them in a short program. (Fell free to use a delay loop to pause the computer). Chng minh rng cc hm ca bn hot ng bng cch t chng trong mt chng trnh ngn. ( S dng mt vng lp tr hon dng my tnh).

1.8. C++ KEYWORDS T KHA TRONG C++


C++ support all of the keywords defined by C and adds 30 of its own. The entire set of keywords defined by C++ is shown in Table 1-1. Also, early versions of

66

C++ defined the overload keyword, but it is now obsolete.


C++ h tr tt c cc t kha c nh ngha trong C v b sung thm 30 t kha

ring. Ton b t kha c nh ngha trong C++ trnh by trong bng 1-1. Cng c nhng phin bn C++ trc y nh ngha t kha overload, nhng by gi n b xa b.

Kim tra cc k nng (Skills Check) :


At this point you should be able to perform the following exercises and answer the questions. Give brief descriptions of polymorphism, encapsulation, and inheritance. How can comments be included in a C++ program? Write a program that uses C++ style I/O to input two integers from the keyboard and then displays the result of raising the first to the power of the second. ( For example, if the user enters 2 and 4, the result is 24, or 16) im ny, bn c th lm cc bi tp v tr li cc cu hi sau: 1. M t tm tt v s a dng, s ng gi v s k tha trong C++. 2. Cc ch thch c th c a vo mt chng trnh C++ nh th no? 3. Vit mt chng trnh s dng dng nhp xut ca C++ nhp vo 2 s nguyn t bn phm v trnh by kt qu ly tha ca s u vi s m l s th hai. ( v d nh nu bn nhp vo l 2 v 4 th kt qu xut ra l 2 4 hay 16).

asm auto bool break case catch char class const

const_cast continue default delete do double

explicit extern false float for friend

int long mutable namspace new operator private protected public

register

switch

union unsigned using virtual void volatile wchar_t while

reinterpret_cast template return short signed sizeof static static_cast struct this throw true try typedef typeid typename

dynamic_cast goto else enum if inline

67

Create a function called rev_str() that reverses a string. Overload rev_str() so it can be called with either one charater array or two. When it is called with one string, have that one string contain the reversal. When it is called with two strings, returns the reversed string tin the second argument. For example: 4. To lp hm rev_str() o ngc mt chui. nh ngha chng hm rev_str() n c th c gi thc hin vi i s l mt mng k t hoc hai. Khi gi hm vi i s l mt chui th n sau khi thc hin chui s nhn gi tr ca chui o ngc. Cn khi gi hm vi i s l 2 chui th chui o ngc s c tr v cho i s th hai. V d: char s1[80], s2[80]; strcpy(s1, "hello"); rev_str(s1, s2); untouched rev_str(s1); //reversed string goes in s2, s1

// reversed string is returned ins1

Given the following new style C++ program, show how to change it into its oldstyle form. 5. Cho chng trnh C++ kiu mi sau, hy i n v dng c: #include <iostream> using namespace std; int f(int a); int main() { cout<< f(10); return 0; }

int f(int a) {

68

return a * 3.1416 }

6. What is the bool data type? Th no l d liu kiu lun l?

CHNG 2 Gii Thiu Lp (Introducing Classes)


2.1 CONSTRUCTOR AND DESTRUCTOR FUNCTION.

Cc Hm Cu To v Hy.
2.2 CONSTRUCTORS THAT TAKE PARAMETERS.

Tham S Ca Hm To.
2.3 INTRODUCING INHERITANCE.

Gii Thiu Tnh K Tha.


2.4 OBJECT POINTERS.

Con Tr i Tng.
2.5 CLASSES, STRUCTURES, AND UNIONS ARE RELATED.

Lp, Cu Trc, v Hi C Lin H.


2.6 IN-LINE FUNCTIONS.

Cc Hm Ni Tuyn.
2.7 AUTOMATIC IN-LINING SKILLS CHECK.

Ni Tuyn T ng.

69

This chapter introduces classes and objects. Several important topics are covered that relate to virtually all aspects of C++ programming , so careful reading is advised. Chng ny gii thiu v lp v i tng . Vi ch quan trng c cp n lin quan n tt c din mo ca chng trnh C++, v th li khuyn l nn c cn thn. N TP: (REVIEW SKILLS CHECK) Before proceeding, you should be able to correctly answer the following questions and do the exercises. Write a program that uses C++ style I/O to prompt the user for a string and then display its length. Create a class that holds name and address information. Store all the information in character string that are private members of the class. Include a public function that displays the name and address. (Call these function store() and display().) Create an overloaded rotate() function that left-rotates the bits in its argument and returns the result. Overload it so it accepts ints and longs. (A rotate is similar to a shift except that the bit shifited off one end is shifted onto the other end.) What is wrong with the following fragment? Trc khi tin hnh bn nn tr li tht nhanh v chnh xc nhng cu hi di y v lm bi tp sau: Thit lp chng trnh s dng C++ style I/O gi ngi s dng 1 chui k t v sau trnh by y . Hy to 1 lp gm tn v thng tin a ch . Lu tr tt c thng tinh trong 1 chui k t m gm nhng thnh vin ca lp. Thm vo phn hm chung nhng lu tr v tn v a ch. (gi nhng hm store() v display()). Hy to 1 hm qu ti m phn xoay bn tri nhng mnh c s i khng v tr v kt qu. Lng qu ti th c chp nhn ints v longs (s lun phin th gn ging nh loi b s dch chuyn m mnh dch chuyn t 1 n cui cng th c dch chuyn ln trn ca ci cui cng khc.) Ci g sai trong on chng trnh di y?
#include <iostream> using namespace std; class myclass

70

{ int i; public: . . }; int { myclass ob; ob.i=10; . . } main()

2.1. CONSTRUCTOR AND DESTRUCTOR FUNCTIONS PHNG THC THIT LP V PHNG THC PH HY:
If you have been writting programs for very long, you know that it is common for parts of your program to require initialization. The need for initialization is even more common when you are working with objects. In fact, when applied to real problems, virtually every object you create will require some sort of initialization. To address this situation, C++ allows a construction function to be included in a class declaration. A classs constructor is called each time an obj of that class is created. Thus, any initialization that need to be performed on an obj can be done automatically by the constructor function. A constructor function has the same name as the class of which it is a part and has no return type. For example, here is a short class that containsa constructor function: Nu bn buc phi thit k mt chng trnh di, bn bit rng tht bnh thng cho chng trnh ca bn khi cn gi tr ban u. S cn thit cho gi tr ban u th 71

hn c s kt hp m bn ang lm vic vi i tng. Thc t th khi p dng cho mt vn thc s, mi i tng bn to nn s cn mt vi s sp xp gi tr ban u. tnh trng ny, C++ cho php phng thc thit lp c th c bao gm trong khai bo lp. S thit lp ca lp th c gi khi m mi ln i tng ca lp c to. V vy, nhng gi tr ban u th cn phi c tin hnh trn mt i tng c th lm vic mt cch t ng bi phng thc thit lp. Phng thc thit lp c cng tn vi lp m n l mt phn v khng c gi tr tr v. V d nh di y c mt on chng trnh ngn c cha phng thc thit lp.
#include <iostream> using namespace std; class myclass { int a; public: myclass(); // constructor void show(); }; myclass :: myclass() { cout << "In construction\n"; a=10; } void myclass :: show() { cout << a; } int main() {

72

myclass ob; ob.show(); return 0; }

In this simple example, the value of a is initialized by the constructotr my class(). The constructor is called when the obj ob is created. An obj is created when that objs declaration statement is executed. It is immportant to understand that in C++, a variable declration statement is an action statement. When you are programming in C, it is easy to think of declaration statement as simple establishing variables. However, in C++, because an obj might have a constructor, a variable declaration statement may, in fact, cause a consideable number of action to occur. Notice how myclass() is defined. As stated, it has no return type. According to the C++ formal syntax rules, it is illegal for a constructor to have a return type. For global obj, an objs constructor is called once, when the program first begins execution. For local obj, the constructor is called each time the declaration statement is executed. The complement of a construction is the destructor. This function is called when an obj is destroyed. When you are working with obj, it is common to have to perform some actions when an obj is destroyed. For example, an obj that allocates memory when it is created will want to free that memory when it is destroyed. The name of a destruction is the name of its class, preceed by a ~. For example, this class contains a destructor function. y l mt v d n gin, gi tr ca a c bt u bi phng thc thit lp myclass(). S thit lp c gi khi i tng ob c to. Mt i tng c to khi m khai bo i tng trnh by c thc hin. Rt l quan trng hiu rng trong C++, khi khai bo c th thay th c l khi hnh ng. Khi bn ang lp trnh chng trnh C tht d dng ngh rng khi khai bo nh l thay i chng trnh thit k mt cch gin n. Tuy nhin, trong C++ bi v i tng c th c phng thc thit lp thay i trnh by khi khai bo. Thc t n l mt con s ng k. Lm th no myclass() c nh ngha. N khng c gi tr tr v. Theo chng trnh C++ c nhiu lnh c php, n l khng hp l nu phng thc thit lp c gi tr tr v.

73

Vi mt i tng tng th, phng thc thit lp ca mt i tng c gi mt ln khi m chng trnh ln u tin c thi hnh. Theo nh ngha tng khu vc, phng thc thit lp c gi mi ln khi khai bo c thi hnh. Hon tt mt thit k l mt ph hy, hm c gi khi i tng c ph hy, khi bn lm vic vi i tng tht l bnh thng khi thc hin nhng hnh ng m i tng b ph hy. V d: Mt i tng cp pht vng nh khi c to s mun gii phng b nh khi n b ph hy. Tn ca phng thc ph hy l tn ca lp t trc n bi du ng. V d: Lp ny c cha mt phng thc ph hy:
#include <iostream> using namespace std; class myclass { int a; public: myclass(); // constructor ~myclass(); // destructor void show(); }; myclass :: myclass() { cout << "In construction\n"; a=10; } myclass :: ~myclass() { cout << "Destructing...\n"; } void myclass :: show() {

74

cout << a << "\n"; } int main() { myclass ob; ob.show(); return 0; }

A classs destructor is called when an obj is destroyed. Local obj are destroyed when they go out of scope. Global obj are destroyed when the program ends. It is not possible to take the address of either a constructor or a destructor. Note: Technically, a constructor or a destructor can perform any type of operation. The code within these function does not have to initialize or rest anything related to the class for which they are defined. For example, a construction for the preceding examples could have computed pi to 100 places. However, have a constructor or destrucotor perform actions not directly related to the initialization or orderly destruction of an obj makes for vary poor programming style and should be avoided. Phng thc ph hy ca mt lp th c gi khi mt i tng b ph hy. i tng xc nh b ph hy khi n ra ngoi phm vi cho php. i tng tng th s b ph hy khi chng trnh kt thc. Khng cn thit ly a ch ca c phng thc thit lp hay phng thc ph hy.

Lu :
Theo k thut, phng thc thit lp hay phng thc ph hy c th thc hin bt c loi s vic. on code trong nhng hm ny khng phi bt u hoc thit lp li bt c th g lin quan n lp m n c inh ngha. V d, mt phng thc ph hy cho v d trc c th nhp pi to 100 ch. Tuy nhin, c phng thc thit lp hay phng thc ph hy thc hin hnh ng khng trc tip lin quan n gi tr ban u hoc tng bc ph hy ca mt i tng lm cho cu trc chng trnh thiu phong ph v nn trnh iu . 75

Examples:
You should recall that the stack class created in Chapter 1 required an initialization function to set the stack index variable. This is precisely the sort of operation that a constructor function was designed to perform. Here is an improved version of the stack class that uses a construcor to automatically initialize a stack obj when it is created:

V D:
1. Bn nn lm li mt ln lp ngn xp to trong chng 1, hm gi tr ban u c thit lp lit k ngn xp c th thay i. l mt lot hot ng m phng thc thit lp thit k thi hnh. y l mt phin bn ci thin ca lp sp xp m s dng phng thc thit lp s dng mt cch t ng ngn xp i tng khi n c ci t:
#include <iostream> using namespace std; #define SIZE 10 // Declare a stack class for characters. class stack { char stck[SIZE]; // hold the stack int tos; // index of top of stack public: stack(); // constructor void push(char ch); // push character on stack char pop(); // pop character from stack };

// Initialize the stack. stack :: stack()

76

{ cout << "Constructing a stack\n"; tos=0; } // Push a character. void stack :: push(char ch) { if(tos==SIZE) { cout << "Stack is full\n"; return; } stck[tos]=ch; tos++; } // Pop a character char stack :: pop() { if(tos==0) { cout<<"Stack is empty\n"; return 0; // return null on empty stack } tos--; return stck[tos]; } int main()

77

{ // Create two stacks that are automatically initialized. stack s1, s2; int i; s1.push('a'); s2.push('x'); s1.push('b'); s2.push('y'); s1.push('c'); s2.push('z'); for(i=0;i<3;i++) cout <<"Pop s1:"<<s1.pop()<<"\n"; for(i=0;i<3;i++) cout <<"Pop s2:"<<s2.pop()<<"\n"; return 0; }

As you can see, now the initialiazation task is performed automatically by the constructor function rather than by a separate function that must be explicitly called by the program. This is an important point. When an initialization is performed automatically when an obj is created, it eliminated any prospect that, by error, the initialization will not be performed. This is another way that obj help reduce program complexity. You, as the programmer, dont need to worry about initialization-it is performed automatically when the obj is brought into existence. Nh bn thy, gi y nhng yu cu gi tr ban u c thc hin t ng bi phng thc thit lp hn l nhng hm tch ri m r rng phi c gi l chng trnh. y l mt im quan trng. Khi m gi tr ban u c thc hin mt cch t ng khi m mt i tng c to, ngoi tr nhng trng hp ny b li nn gi tr ban u s c thc hin. y l mt cch m i tng c th gim bt nhng chng trnh phc tp. Bn-mt ngi lm chng trnh, khng cn quan tm n gi tr ban u, n c thc hin mt cch t ng khi i tng c thi hnh.

78

2. Here is an example that shows the need for both a constructor and a destructor function. It creates a simple string class, called strtype, that contains a string and its length. When a strtype obj is created, memory is allocated to hold the string and its initial length is set to 0. When a strtype obj is destroyed, that memory is released. 2. Sau y l mt v d ch r s cn thit ca c phng thc thit lp v phng thc ph hy. n to mt lp chui k t n gin c gi l strtype, n cha ng chui k t v chiu di ca n. Khi m i tng strtype c to b nh c cp pht gi chui k t v chiu di ban u ca n c bt u t 0. Khi m i tng strtype b ph hy th b nh s c gii phng.
#include <iostream> #include <cstring> #include <cstdlib> using namespace std; #define SIZE 255 class strtype { char *p; int len; public: strtype(); // constructor ~strtype(); // destructor void set(char *ptr); void show(); }; // Initialize a string obj. strtype::strtype() { p=(char *)malloc(SIZE);

79

if(!p) { cout<<"Allocation error\n"; exit(1); } *p='\0'; len=0; } // Free memory when destroying string obj. strtype::~strtype() { cout<<"Freeing p\n"; free(p); } void strtype::set(char *ptr) { if(strlen(p)>=SIZE) { cout<<"String too big\n"; return; } strcpy(p,ptr); len=strlen(p); }

void strtype::show() {

80

cout<<p<<"-length:"<<len; cout<<"\n"; }

int main() { strtype s1, s2; s1.set("This is a test."); s2.set("I like C++.");

s1.show(); s2.show(); return 0; }

This program uses malloc() and free() to allocated and free memory. While this is perfectly valid, C++ does provide another way to dynamically manage memory, as you will see later in this book.

Note:

The preceding program uses the new-style headers for the C library functions used by the program. As mentioned in Chapter 1, if your compiler does not support these headers, simply substitute the standard C header files. This applies to other programs in this book in wich C library functions are used. Chng trnh ny dng malloc() v free() cp pht v gii phng b nh. Trong khi l mt cn c hon ho, C++ cung cp mt cch khc qun l b nh mt cch linh hot hn nh bn c th thy phn sau ca cun sch ny.

LU :

Chng trnh trc s dng tiu phong cch mi cho hm th vin C c s dng bi chng trnh. Nh c nhc n chng 1. Nu trnh bin dch ca bn khng h tr nhng tiu ny, c th thay th bi cc file tiu chun trong C. Nhng p dng trong cc chng trnh khc trong cun sch ny nhng

81

ch s dng hm th vin trong C.


3. Here is an interesting way to use an objects constructor and destructor. This

program uses an object of the timer class to time the interval between when an object of type timer is created and when it is destroyed. When the objects destructor is called, the elapsed time is displayed. You could use an object like this to time the duration of a program or the length of time a function spends withing a block. Just make sure that the object goes out of scope at the point at which you want the timing interval to end. y l mt cch l th khc dng cc hm to v hm hy ca mt i tng. Chng trnh ny dng i tng ca lp timer xc nh khong thi gian gia khi mt i tng c to v khi b hy. Khi hm hy ca mt i tng c gi, thi gian tri qua c hin th. Bn c th dng i tng nh th ny nh khong thi gian ca mt chng trnh hay di thi gian ma mt hm lm vic trong mt khi. Phi m bo rng i tng i qua phm vi ti im m bn mun nh thi gian cho n lc kt thc.

#include <iostream.h> #include <time.h>

class timer { clock_t start; public: timer();// constructor ~timer();// destructor };

timer::timer() { start=clock(); 82

timer::~timer() { clock_t end; end = clock(); cout<<"Elapsed time: start)/CLOCKS_PER_SEC<<"\n"; } "<<(end-

main() { timer ob; char c; // delay... cout<<" Press a key followed by ENTER: "; cin>>c;

return 0; } This program uses the standard library function clock(), which returns the number of clock cycles that have taken place since the program started running. Dividing this value by CLOCKS_PER_SEC converts the value to seconds. Chng trnh ny dng hm th vin chun clock() tr v s chu k ng h xy ra t khi chng trnh bt u chy. Chia gi tr ny cho CLOCKS_PER_SEC chuyn thnh gi tr giy

83

EXERCISES
1. Rework the queue class that you developed as an exercise in Chapter 1 by replacing its initialization function with a constructor. 2. Create a class called stopwatch that emulates a stopwatch that keeps track of elapsed time. Use a constructor to initially set the elapsed time to 0. Provide two member functions called start() and stop() that turn on and off the timer, respectively. Include a member function called show() that displays the elapsed time. Also, have the destructor function automatically display elapsed time when a stopwatch object is destroyed. (To simplify, report the time in seconds.) 3. What is wrong with the constructor shown in the following fragment? 1. Lm li lp queue trong bi tp chng 1 thay hm khi u bng hm to. 2. To lp stopwatch so vi ng h bm gi trong vic xc nh thi gian tri qua. Dng hm to t thi gian tri qua lc u v 0. a vo hm thnh vin show() hin th thi gian tri qua khi i tng stopwatch() b hy.( n gin thi gian c tnh bng giy). 3. iu g sai trong hm to on chng trnh di y: class sample { double a,b,c; public: double sample(); }; // error, why?

2.2. CONSTRUCTORS THAT TAKE PARAMETERS - THAM S CA HM TO


It is possible to pass arguments to a constructor function. To allow this, simply add the appropriate parameters to the constructor functions declaration and definition. Then, when you declare an object, specify the arguments. To see

84

how this is accomplished, lets begin with the short example shown here: C th truyn i s cho hm to. lm iu ny ch cn b sung cc tham s thch hp cho khai bo v nh ngha hm to. Sau , khi bn khai bo mt i tng, bn hy ch r cc tham s ny lm i s. thy r iu ny, chng ta hy bt u vi v d ngn di y: #include <iostream> using namespace std; class myclass { int a; public: myclass(int x);//constructor void show(); };

myclass::myclass (int x) { cout<< "In constructor\n"; a=x; }

void myclass::show() { cout<<a<<"\n"; }

85

int main() { myclass ob(4); ob.show(); return 0; } Here the constructor for myclass takes one parameter. The value passed to myclass() is used to initialize a. Pay special attention to how ob is declared in main(). The value 4, specified in the parentheses following ob, is the argument that is passed to myclass()s parameter x, which is used to initialize a. y, hm to i vi myclass c mt tham s. Gi tr c truyn cho myclass() c dng khi to a. Cn ch cch khai bo ob trong hm main(). Gi tr 4, c ch r trong cc du ngoc sau ob, l i s c truyn cho tham s x ca myclass, c dng khi to a. Actually, the syntax for passing an argument to a parameterized constructor is shorthand for this longer form: myclass ob = myclass(4); Thc s cc php truyn mt i s cho mt hm to c tham s ha c dng di hn nh sau: myclass ob = myclass(4); However, most C++ programmers use the short form. Actually, there is a slight technical difference between the two forms that relates to copy constructors, which are discussed later in this book. But you dont need to worry about this distinction now. Tuy nhin, hu ht nhng ngi lp trnh C++ s dng ngn gn. Thc ra c mt s khc bit nh gia hai dng hm khi to s c ni r trong phn sau ca cun sch ny. Nhng bn khng cn lo lng v iu ny,

Notes

Unlike constructor functions, destructor functions cannot have parameters. The reason for this is simple enough to understand: there exists no mechanism by which to pass arguments to an objects that is being destroyed. 86

CH : Khng ging nh cc hm khi to, cc hm hy khng c tham s.


L do tht d hiu: khng c c ch no truyn i s cho mt i tng b hy.

CC V D (EXAMPLES):
1 It is possible-in fact, quite common-to pass a constructor more than one argument. Here myclass() is passed two arguments: 1 Thc t, rt thng dng, c th truyn nhiu tham s cho mt hm to. Sau y, c hm tham s truyn cho my class(): #include <iostream.h> class myclass { int a,b; public: myclass(int x,int y);//constructor void show(); };

myclass::myclass (int x,int y) { cout<< "In constructor\n"; a=x; b=y; }

void myclass::show()

87

{ cout<<a<<" "<<b<<"\n"; }

int main() { myclass ob(4,7); ob.show(); return 0; }

Here 4 is passed to x and 7 is passed to y. This same general approach is used to pass any number of arguments you like( up to the limit set by the compiler, of course). y, 4 c truyn cho x v 7 cho y. Cch tng qut nh vy c dng truyn mt s i s bt k m bn mun( d nhin, ln n gii hn ca trnh bin dch). 2 Here is another version of the stack class that uses a parameterized constructor to pass a name to a stack. This single-character name is used to identify the stack that is being referred to when an error occurs. y l mt dng khc ca lp stack dng hm to c tham s ha truyn tn cho ngn xp. Tn k t n gin ny dng nhn ra ngn xp no c tham chiu khi c li xy ra. #include <iostream.h> #define SIZE 10

// Declare a stack class for characters. class stack

88

{ char stck[SIZE]; // holds the stack int tos;// index of top-of-stack char who; //identifies stack public: stack(char c); //constructor void push (char ch); //push charater on stack char pop(); // pop character from stack };

// Initialize the stack. stack::stack(char c) { tos = 0; who = c; cout<<" constructing stack "<<who<<"\n"; }

//push a character void stack::push(char ch) { if(tos==SIZE) { cout<<"Stack "<<who<< " is full\n"; return ;

89

} stck[tos] = ch; tos++; }

//pop a character char stack::pop() { if(tos==0) { cout<<" Stack "<<who<<" is empty\n"; return 0;// return null on empty stack } tos--; return stck[tos]; }

int main() { // Creat initialized. two stacks that are automatically

stack s1('A'),s2('B'); int i; s1.push('a'); s2.push('x'); s1.push('b'); 90

s2.push('y'); s1.push('c'); s2.push('z'); // This will generate some error messages. for(i=0;i<5;i++) "<<s1.pop()<<"\n"; for(i=0;i<5;i++) "<<s2.pop()<<"\n"; cout<<" cout<<" Pop Pop s1: s2:

return 0; }

Giving objects a name, as shown I this example, is especially useful during debugging, when it is important to know which object generates an error. Khi cho cc i tng mt tn nh ch r trong v d, th iu quan trng l bit c i tng no to ra li, nn rt hu ch trong qu trnh g ri. 3. Here is a different way to implement the strtype class ( developed earlier) that uses a parameterized constructor function: 3. y l cch khc thc hin lp strtype ( c trnh by) s dng mt hm to c tham s ha: #include <iostream.h> #include <malloc.h> #include <string.h> #include <stdlib.h>

class strtype {

91

char*p; int len; public: strtype(char*ptr); ~strtype(); void show(); };

strtype::strtype(char*ptr) { len=strlen(ptr); p=(char*)malloc(len+1); if(!p) { cout<<" Allocation error\n"; exit(1); } strcpy(p,ptr); }

strtype::~strtype() { cout<<" Freeing p \n"; free(p); }

92

void strtype::show() { cout<<p<<" - length: "<<len; cout<<"\n"; }

main() { strtype s1("This is a test"), s2("i like C++");

s1.show(); s2.show(); return 0; }

In this version of strtype, a string is given an initial value using the constructor function. Trong dng ny ca strtype, mt chui c cho mt gi tr ban u bng cch dng hm to.
4. Although the previous examples have used constants, you can pass an objects

constructor any vaild expression, including variables. For example, this program uses input to construct an object: 4. Mc d cc v d trc dng cc hng s, bn cng c th truyn cho hm to ca i tng mt biu thc, k c cc bin. V d, chng trnh sau dng d liu nhp ca ngi s dng to mt i tng: #include <iostream.h>

93

class myclass { int i,j; public: myclass(int a,int b); void show(); };

myclass::myclass(int a,int b) { i=a; j=b; }

void myclass::show() { cout<<i<<' '<<j<<"\n"; }

main() { int x,y; cout<<" Enter two integers: "; cin>>x>>y;

94

// use variables to construct ob myclass ob(x,y); ob.show(); return 0; }

This program illustrates an important point about objects. They can be constructed as needed to fit the exact situation at the time of their creation. As you learn more about C++, you will see how useful constructing objects on the fly is. Chng trnh ny minh ha mt im quan trng v cc i tng. Cc i tng c th c to ra khi cn lm ph hp vi tnh trng chnh xc ti thi im to ra cc i tng. Khi bit nhiu hn na v C++, bn s thy rt hu dng khi to ra cc i tng theo cch na chng nh th.

BI TP (EXERCISES):
1. Change the stack class so it dynamically allocates memory for the stack. Have the size of the stack specified by a parameter to the constructor function. ( Dont forget to free this memory with a destructor function). 1. Thay i lp stack cho n cp pht ng b nh cho ngn xp. Kch thc ngn xp c ch r bng mt tham s vi hm to ( ng qun gii phng b nh bng mt hm hy). 2. Creat a class called t_and_d that is passed the current system time and date as a parameter to its constructor when it is created. Have the class include a member function that displays this time and date on the screen. (Hint: Use the standard time and date functions found in the standard library to find and display the time and date). 2. Hy to lp t_and_d truyn ngy v gi h thng hin hnh nh mt tham s cho hm to ca n khi c to ra. Lp gm c hm thnh vin hin th ngy gi ny ln mn hnh. (Hng dn: dng cc hm ngy v gi chun trong th vin chun tm v hin th ngy). 95

3. Create a class called box whose constructor function is passed three double values, each of which represents the length of one side of a box. Have the box class compute the volume of the box function called vol() that displays the volume of each box object. 3. Hy to lp box c hm to c truyn 3 gi tr double, din t di cc cnh ca hp. Hy cho lp box tnh th tch ca hnh lp phng v lu tr kt qu trong bin double. To hm thnh vin vol() hin th th tch ca mi i tng box.

2.3. INTRODUCING INHERITANCE - GII THIU TNH K THA:


Although inheritance is discussed more fully in Chapter 7, it needs to be introduced at this time. As it applies to C++, inheritance is the mechanism by which one class can inherit the properties of another. Inheritance allows a hierarchy of classes to be built, moving from the most general to the most specific. Mc d tnh k tha s c tho lun y hn trong chng 7, nhng y cng cn gii thiu v tnh k tha. Khi p dng vo C++, tnh k tha l c ch m nh mt lp c th k tha cc c im ca lp khc. Tnh k tha cho php to ra th bc ca cc lp, chuyn t dng tng qut nht n c th nht. To begin, it is necessary to define two terms commonly used when discussing inheritance. When one class is inherited by another, the class that is inherited is called the base class. The inheritance class is called the derived class. In general, the process of inheritance begins with the definition fo a base class. The base class defines all qualities that will be common to any derived classes. In essence, the base class represents the most general description of a set of traits. A derived class inherits those general traits and adds properties that are specific to that class. bt u, cn phi nh ngha hai thut ng thng c dng khi tho lun v tnh k tha. Khi mt lp c k tha bi mt lp khc th lp c k tha c gi l lp c s (base class) v lp k tha c gi l lp dn xut (derived class). Ni chung, qu trnh k tha bt u t nh ngha lp c s. Lp c s xc nh cc tnh cht m s trnn thng dng cho cc lp dn xut. Ngha l lp c s hin th m t tng qut nht mt tp hp cc c im. Mt lp dn xut k tha cc c im tng qut ny v b sung thm cc tnh cht ring ca lp dn

96

xut. To understand how one class can inherit another, lets first begin with an example that, although simple, illustrates many key features of inheritance. To start, here is the declaration for the base class: hiu cch mt lp k tha lp khc, trc ht chng ta hy bt u bng mt v d, d n gin, nhng cng minh ha nhiu c im chnh ca tnh k tha. Bt u, y l khai bo cho lp c s: // Define base class. Class B { int i; public: void set_i(int n); int get_i(); };

Dng lp c s ny thy r lp dn xut k tha n: // Define derived class Class D: public B { int j; public: void set_j(int n); int mul(); };

97

Look closely at this declaration. Notice that after the class name D there is a colon followed by the keyword public and the class name B. This tells the complier that class D will inherit all components of class B. The keyword public tells the compiler that B will be inherited such that all public elements of the base class will also be public elements of the derived class. However, all private elements of the base class remain private to it and are not derctly accessible by the derived class. Here is an entire program that uses the B and D classes: Xt k hn khai bo ny, ch rng sau tn lp D l du hai chm (:) v tip n l t kha public v tn lp B. iu ny bo cho trnh bin dch bit lp D s k tha cc thnh phn ca lp B. T kha public bo cho trnh bin dch bit B s c k tha sao cho mi phn t chung ca lp c s cng s l cc phn t chung ca lp dn xut. Tuy nhin, mi phn t ring ca lp c s vn cn ring i vi n v khng c truy cp trc tip bi lp dn xut. y l ton b chng trnh dng cc lp B v D: // Asimple example of inheritance #include <iostream.h>

// Define base class. class base { int i; public: void set_i(int n); int get_i(); }; // Define derived class. class derived: public base {

98

int j; public: void set_j(int n); int mul(); };

// Set value in base. void base::set_i(int n) { i=n; }

// Return value of i in base. int base::get_i() { return i; }

// Set value of j in derived. void derived::set_j(int n) { j=n; }

// Return value of base's i times derived's j.

99

int derived::mul() { // derived class can call base class public member functions. return j* get_i(); }

main() { derived ob;

ob.set_i(10); // load i in base ob.set_j(4); // load j in derived

cout<<ob.mul(); //display 40 return 0; } Look at the definition of mul(). Notice that it calls get_i(), which is a member of the base class B, not of D, without linking it to any specific object. This is possible because the public members of B become public members of D. However, the reason that mul() must call get_i() instead of accessing I directly is that the private members of a base class (in this case, i) remain private to it and not accessible by any derived class. The reason that private members of a class are not accessible to derived classes is to maintain encapsulation. If the private members of a class could be made public simply by inheriting the class, encapsulation could be easily circumvented. Xt nh ngha hm mul(). Ch rng hm ny gi get_i() l thnh vin ca lp c s B, khng phi ca D, khng lin kt n vi i tng c th no. iu ny c th c v cc thn vin chung ca B cng tr thnh cc thnh vin chung ca D. Tuy nhin l do m mul() phi gi get_i() thay v truy cp I trc tip l v cc thnh vin 100

ring ca lp c s (trong trng hp ny l i) vn cn ring i vi lp c s v khng th truy cp bi lp dn xut no. L do m cc thnh vin ring ca lp khng th c truy cp bi cc lp dn xut l duy tr tnh ng kn. Nu cc thnh vin ring ca mt lp c th tr thnh chung cho k tha lp th tnh ng kn d dng b ph v. The general form used to inherit a base class is shown here: Dng tng qut dng k tha mt lp c trnh by nh sau: Class nam { . . . }; derived-class-name: access-specifier base-class-

Here access-specifier is one of the following three keywords: public, private, or protected. For now, just use public when inheriting a class. A complete description of access specifiers will be given later in this book y <thnh phn cn truy cp> l mt trong 3 t kha sau: public, private, hoc l protected. Hin gi, ch s dng public khi tha k 1 lp. S m t hon thin hn v <thnh phn cn truy cp> s c a ra vo phn sau ca cun sch ny.

V D (EXAMPLE):
Here is a program that defines a generic base class called fruit that describes certain characteristics of fruit. This class is inherited by two derived classes called Apple and Orange. These classes supply specific information to fruit that are related to these types of fruit. y l mt chng trnh nh ngha lp c s v ging loi c gi l fruit n th hin nhng c im no ca tri cy.Lp ny th c tha hng bi hai

101

lp nhn l Apple and Orange. Nhng lp ny cung cp nhng thng tin c trng v fruit.nhng th lin quan n nhiu loi tri cy khc. // An example of class inheritance. #include <iostream> #include <cstring> using namespace std;

enum yn { no, yes; } enum color { red, yellow, green, orange; }

void out(enum yn x);

char *c[] = {"red", "yellow", "green", "orange"};

// Generic fruit class. class fruit { // in this base, all elements are public public: enum yn annual;

102

enum yn perennial; enum yn tree; enum yn tropical; enum color clr; char name[40]; };

// Derive Apple class. class Apple: public fruit { enum yn cooking; enum yn crunchy; enum yn eating; public: void seta(char*n, enum color c, enum yn ck, enum yn crchy, enum yn e); void show(); };

// Derive orange class. class Orange: public fruit { enum yn juice; enum yn sour; enum yn eating; public: 103

void seto(char*n, enum color c, enum yn j, enum yn sr, enum yn e); void show(); };

void Apple::seta(char*n, enum enum yn crchy, enum yn e) { strcpy(name, n); annual = no; perennial = yes;

color

c,

enum

yn

ck,

tree = yes; tropical = no; clr = c; cooking = ck; crunchy = crchy; eating = e; }

void Orange::seto(char*n, enum yn sr, enum yn e) { strcpy(name, n); annual = no; perennial = yes;

enum

color

c,

enum

yn

j,

104

tree = yes; tropical = yes; clr = c; juice = j; sour = sr; eating = e; }

void Apple::show() { cout << name << "apple is:" << "\n"; cout << " Annual: "; out (annual); cout << "Perennial: "; out (perennial); cout << "Tree: "; out (tree); cout << "Tropical: "; out (tropical); cout << "Color: " << c[clr] << "\n"; cout << "Good for cooking: "; out (cooking); cout << "Crunchy: "; out (crunchy); cout << "Good for eating: ";

105

out (eating); cout << "\n"; }

void Orange::show() { cout << name "orange is:" << "\n"; cout << " Annual: "; out (annual); cout << "Perennial: "; out (perennial); cout << "Tree: "; out (tree); cout << "Tropical: "; out (tropical); cout << "Color: " << c[clr] << "\n"; cout << "Good for juice: "; out (juice); cout << "Sour: "; out (sour); cout << "Good for eating: "; out (eating); cout << "\n"; }

106

void out(enum yn x) { if(x==no) cout << "no\n"; else cout << "yes\n"; }

int main() { Apple a1, a2; Orange o1, o2;

a1.seta("Red Delicious", red, no, yes, yes); a2.seta("Jonathan", red, yes, no, yes);

o1.seto("Navel", orange, no, no ,yes); o2.seto("Valencia", orange, yes, yes, no);

a1.show(); a2.show();

o1.show(); o2.show();

107

return 0; } As you can see, the base class fruit defines several characteristics that are common to all types of fruit. (Of course, in order to keep this example short enough to fit conveniently in the book, the fruit class is somewhat simplified.) For example, all fruit grows on either annual perennial plants. All fruit grows on either on trees or on other types of plants, such as vines or bushes. All fruit have a color and a name. This base class is then inherited be the Apple and Orange classes. Each of these classes supplies information specific to its type of fruit. Nh cc bn c th thy, lp c s fruit nh ngha nhng nt ph bin i vi tt c nhng loi tri cy.(Tuy nhin gi cho v d ngn gn ph hp vi cun sch, lp fruit c phn c n gin ha). V d, t c nhng loi hoa qu pht trin hoc l cy mt nm hoc l cy lu nm. Tt c nhng loi hoa qu hoc l trn cy hoc l mt dng khc ca thc vt., chng hn nh cy leo, hoc l cy bi. Tt c nhng loi tri cy u c mu v tn. Lp c s th c tha k bi lp Apple v Orange. Mi lp ny cp nhng nt in hnh ca mi loi tri cy. This example illustrates the basic reason for inheritance. Here, a base class is created that defines the general traits associated with all fruit. It is left to the derived classes to supply those traits that are specific to each individual case. V d ny lm sng t l do c bn cho vic tha k. y, mt lp c s c to ra nh ngha nhng c im chung lin quan n tt c cc loi tri cy.N th c a n nhng lp nhn cung cp nhng c im , v chng th c th i vi mi trng hp ring bit. This program illustrates another important fact about inheritance: A base class is not exclusively owned by a derived class. A base class can be inherited by any number of classes. Chng trnh ny cn minh ha cho nhiu mt quan trong khc v s tha k: mt lp c s khng ch dnh ring cho mt lp nhn. Mt lp c s c th c tha k bi nhiu lp.

BI TP (Exercise):
Given the following base class

108

1. Cho lp c s sau: class area_cl { public: double height; double width; };

Create two derived classes called rectangle and isosceles that inherit area_cl. Have each class include a function called area() that returns the area of a rectangle or isosceles triangle, as appropriate. Use parameterized constructors to initialize height and width. To 2 lp con l rectangle v isosceles k tha t lp area_cl. Mi lp phi c mt hm area() v tr v din tch ca hnh ch nht hay tam gic tng ng. S dng phng thc thit lp tham s a gi tr ban u vo height v width.

1.4. OBJECT POINTERS - CON TR I TNG:

So far, you have been accessing members of an object by using the dot operator. This is the correct method when you are working with an object. However, it is also possible to access a member of an object via a pointer to that object. When a pointer is used, the arrow operator (->) rather than the dot operator is employed. (This is exactly the same way the arrow operator is used when given a pointer to a structure.) Xa hn, bn c th truy xut mt thnh phn ca i tng bng cch s dng ton t chm. y l mt phng php ung khi bn ang lm vic vi mt i tng. Tuy nhin, cng c th truy xut n mt thnh phn ca i tng qua mt con tr ch i tng . Khi mt con tr c s dng, th ton t mi tn (->) c tn dng nhiu hn ton t chm .(y l mt cch chnh xc tng t. Ton t mi tn c s dng khi ng n con tr ch n mt cu trc.) You declare an object pointer just like you declare a pointer to any other type of

109

variable. Specific its class name, and then precede the variable name with an asterisk. To obtain the address of an object, precede the object with the & operator, just as you do when talking the address of any other type of variable. Bn biu din mt con tr i tng ging nh l bn biu din mt con tr ch n bt k mt loi tham bin no khc. Ch r ra lp tn ring ca n, v sau ghi du * trc tn bin. s dng a ch ca mt i tng, hy t trc i tng ton t &, ngay khi ban thc hin th n s ly a ch ca bt k kiu bin no khc. Just like pointers to other types, an object pointer, when incremented, will point to the next object of its type. Ging nh con tr ch dn cc loi khc, mt con tr i tng khi tng ln s ch n i tng k tip cng loi vi n.

V D (EXAMPLE):
Here is a simple example that uses an object pointer: 1. y l v d n gin s dng mt con tr i tng:

#include "stdafx.h" #include"iostream" using namespace std; class myclass { int a; public: myclass (int x);//thiet lap int get(); };

110

myclass:: myclass(int x) { a=x; }

int myclass::get() { return a; }

int main() { myclass ob(120);// tao doi tuong myclass*p; //tao con tro chi ve doi tuong p=&ob;// dua dia chi cua con tro vao p cout<<"Gia tri doi tuong dang giu:"<<ob.get(); cout<<"\n";

cout<<"Gia tri cua dia chi con tro dang giu:"<<p->get(); return 0; }

Notice how the declaration Ch cch khai bo:

111

myclass *p; Creates a pointer to an object of myclass. It is important to understand that creation of an object pointer does not create an object-it creates just a pointer to one. The address of ob is put into p by using this statement: To mt con tr ch n i tng ca lp myclass. Quan trng l phi hiu vic to mt con tr i tng khng phi l vic to mt i tng- n ch l vic to mt con tr ch n i tng. a ch ca ca ob c gn cho p bng cu lnh: p=&ob; Finally, the program shows how the members of an object can be accessed through a pointer. Cui cng chng trnh minh ha cch mt con tr truy cp n cc thnh phn ca i tng. We will come back to the subject of object pointers in Chapter 4, once you know more about C++. There are several special features that relate to them. Chng ta s tr li ch con tr i tng chng 4, mt khi bn bit thm v C++. C vi im c bit lin quan n chng.

1.5. CLASSES, STRUCTURES, AND UNIONS ARE RELATED - LP CU TRC, V NHNG S KT , HP C LIN QUAN:
As you have seen, the class is syntactically similar to the structure. You might be surprised to learn, however that the class and the structure have virtually identical capabilities. In C++, the definition of a structure has been expanded so that it can also include member functions, including constructor and destructor functions, in just the same way that a class can. In fact, the only difference between a structure and a class is that, by default, the members of a class are private but the members of a structure are public. The expanded syntax of a structure is shown here: Nh cc bn hiu, lp l nhng cu trc c c php ging nhau. Bn c th thy ngc nhin khi hc, lp v cu trc hu nh l c cc chc nng ging nhau. Trong C++, s nh ngha cu trc c m rng n mc n c th bao gm

112

nhng thnh phn hm: hm thit lp v hm ph hy, ging nh nhng g mt lp c th lm. Trn thc t, s khc bit duy nht gia lp v cu trc l: s xc lp mc nh, thnh phn cu lp l private th thnh phn ca cu trc l public. C php m rng ca mt cu trc l c trnh by nh sau: struct type-name { //public function and data members private: //private function and data members }object-list,

In fact, according to the formal C++ syntax, both struct and class create new class types. Notice that a new keyword is introduced. It is private, and it tells the compiler that the members that follow are private to that class. Trn thc t, theo c php C++ thng thng, c srtuct v class u to nn mt kiu lp mi. Gii thiu mt t kha mi. l private, v n ch cho trnh bin dch cc thnh phn theo sau n l ring t i vi lp .

On the surface, there is a seeming redundancy in the fact that structures and classes have virtually identical capabilities. Many newcomers to C++ wonder why this apparent duplication exists. In fact, it is not uncommon to hear the suggestion that the class keyword is unnecessary Bn ngoi , trn thc t c v nh c s d tha l lp v cu trc c nhng kh nng nh nhau. Nhiu ngi mi tip xc vi C++ t hi rng ti sao li c s trng lp r rng nh vy tn ti. Nhng thc t, khng him khi nghe thy rng t kha class th khng cn thit.

113

The answer to this line of reasoning has both a strong and weak form. The strong (or compelling) reason concerns maintaining upward compatibility from C. In C++, a C-style structure is also perfectly acceptable in a C++ program. Since in C all structure members are public by default, this convenience is also maintained in C++. Further, because class is a syntactically separate entity from struct, the definition of a class is free to evolve in a way that will not be compatible with a C-like structure definition. Since the two are separated, the future direction of C++ is not restricted by compatibility concerns. Cu tr li cho l do ca vn l c c mnh m va yu t. L do ch yu(hoc l hp dn) l bao gm vic duy tr cc tnh nng tng thch ca C. Trong C++, cu trc theo phong cch ca C cng c chp nhn hon ton trong mt chng trnh C++. K t khi trong C tt c cc thnh phn cu trc c mc nh dng chung, quy c ny cng c duy tr trong C++. Hn th na, bi v, class l mt thc th c c php ring bit vi struct, nh ngha ca mt lp th t do m ra m khng l tnh nng ca C ging nh l nh ngha ca cu trc. K t khi hai loi ny c tch bit, th phng hng trong tng lai ca C++ khng b hn ch bi nhng mi quan tm v tnh nng tng thch. The weak reason for having two similar constructs is that there is no disadvantage to expanding the definition of a structure in C++ to include member functions. L do ph gii thich cho vic c hai cu trc tng t nhau l khng c bt li no m rng nh ca mt cu trc trong C++ cha thm cc thnh phn hm. Although structures have the same capabilities as classes, most programmers restrict their use of structures to adhere to their C-like form and do not use them to include function members. Most programmers use the class keyword when defining objects that contain both data and code. However, this is a stylistic matter and is subject to your own preference. (After this section, this books reserves the use of struct for objects that have no function members.) Mc d cu trc c nhng tnh nng tng t nh lp, nhng hu ht cc lp trnh vin hn ch s dng nhng cu trc bm cht vo C v khng s dng chng cha cc thnh phn hm. Hu ht nhng lp trnh vin s dng t kha class khi nh ngha nhng i tng bao gm c d liu v on m. Tuy nhin, y ch l vn vn phong, v l ch lin quan n s thch ring ca mi ngi.(Sau phn ny, cun sch ny s duy tr cch s dng cu trc cho i tng khng c thnh phn hm).

114

If you find the connection between classes and structures interesting, so will you find this next revelation about C++: classes are also related. In C++, a union defines a class type that can contain both function and data as members. A union like a structure in that, by default, all members are public until the private specifier is used. In a union, however, all data members share the same memory location (just in C). Unions can contain constructor and destructor functions. Fortunately, C unions are upwardly compatible with C++ unions. Nu bn nhn thy s th v ca mi lin quan gia lp v cu trc th bn s thy c nhng iu mi v C++ : union v class cng c mi quan h. Trong C++, union nh ngha mt loi lp, n c th bao gm c hm v d liu. Mt union th ging nh mt cu trc, trong , mc nh, th tt c cc thnh phn l public mi cho n khi private c s dng. Trong mt union, tt c cc tt c cc thnh phn d liu s dng chung mt vng nh (ch c trong C). May thay, nhng s kt hp trong C tng thch vi nhng s kt hp trong C++. Although structures and classes seem on the surface to be redundant, this is not the case with unions. In an object-oriented language, it is important to preserve encapsulation. Thus, the unions ability to link code and data allows you to create class types in which all data uses a shared location. This is something that you cannot do using a class. Mc d cc cu trc v lp dng nh c v l d tha, iu ny th khng xy ra i vi union. Trong ngn ng lp trnh hng i tng, iu quan trng l gi c s gi gn. Theo cch , nhng kh nng ca union cho php chng ta kt ni cc on m lnh v d liu to nn cc loi class s dng cc thng tin c chia s. y l vi th m bn khng th s dng class. There are several restrictions that apply to unions as they relate to C++. First, they cannot inherit any other class for any other type. Unions must not have many any static members. They also must not contain any object that has a constructor or destructor. The union, itself, can have a constructor and destructor, through. (Virtual functions are described later in this book.) C mt vi hn ch khi ng dng union vo trong C++. u tin, bn khng th tha hng bt k lp no khc, v chng khng th c s dng nh l lp c s trong bt k loi no khc. Union khng c c thnh phn static (tnh). Chng cng khng c c bt k i tng no c phng thc thit lp hoc ph hy. Mc d, t union c th c phng thc thit lp v ph hy. Cui cng, union khng th c hm thnh phn o. (Hm o s c trnh by sau trong cun sch ny).

115

There is a special type of union in C++ called an type of union in C++ called an anonymous union An anonymous union does not have a type name, and no variables can be declared for this sort of union. Instead, an anonymous union tells the compiler that its members will share the same memory location. However, in all other respects, the members are accessed directly, without the dot operator syntax. For example, examine this fragment: C mt vi loi union c bit trong C++ c gi l union n danh (anonymous union). Mt union n danh th khng c tn loi, v khng mt tham chiu no c th c khai bo theo kiu union ny. Thay vo , union n danh ch cho trnh bin dch rng nhng thnh phn ca n s s dng chung vng nh. Tuy nhin, trong tt c cc kha cnh khc, nhng thnh phn hot ng v c i x ging nh nhng tham chiu bnh thng khc. l, nhng thnh phn c truy cp mt cch trc tip, khng cn c php ton t chm. V d, nghin cu on chng trnh sau:

union { //an anonymous union int I; char ch[4]; i=10;//access I and ch directly ch[0]=X; }

i=10;//truy xuat i va ch mot cach truc tiep ch[0]=X;

As you can see, i and ch are accessed directly because they are not part of any object. They do, however, share the same memory space. 116

Nh bn c th thy, i v ch c truy xut mt cch trc tip bi v chng khng phi l thnh phn ca i tng. Chng hot ng, tuy nhin, dng chung mt vng nh. The reason for the anonymous union is that it gives you a simple way to tell the compiler that you want two or more variables to share the same memory location. Aside from this special attribute, members of an anonymous union behave like other variables. Hm nc danh cung cp cho cc bn mt cch n gin bo cho trnh bin dch rng, bn mun hai hay nhiu tham chiu cng dng chung mt vng nh. Ngoi tnh nng c bit ny, tt c cc thnh phn ca union u c i x ging nh cc bin khc. Anonymous unions have all of the restrictions that apply to normal unions plus these additions. A global anonymous union must be declared static. An anonymous union cannot contain private members. The names of the members of anonymous union must not conflict with identifiers within the same scope. Hm nc danh cng c tt c nhng hn ch nh cc hm nc danh bnh thng khc, v thm mt vi ci khc. Mt hm nc danh ton cc phi c khai bo kiu static. Mt hm nc danh khng th c cc thnh phn ring t. Tn ca cc thnh phn trong hm nc danh khng c trng vi nhng dng khc m khng cng phm vi.

V D (EXAMPLES):
Here is a short program that uses struct to create a class: y l mt on chng trnh ngn s dng kiu struct to mt lp #include "stdafx.h" #include"iostream.h" #include"cstring" // su dung struct de dinh nghia mot class struct st_type {

117

st_type(double b,char *n); void show(); private: double balance; char name[40]; }; st_type::st_type(double b,char*n) { balance=b; strcpy(name,n); } void st_type:: show() { cout<<"Name :"<<name; cout<<": $"<<balance; if(balance<0.0) cout <<"***"; cout<<"\n"; }

int main() { st_type acc1(100.12,"Johnson"); st_type acc2(-12.34,"Hedricks");

118

acc1.show(); acc2.show();

return 0; }

Notice that, as stated, the members of a structure atr public by default. The private keyword must be used to declare private members. Ch rng, cc thnh phn ca cu trc c mc nh l chung. T kha private c dng m t cc thnh phn ring bit. Also, notice one difference between C-like and C++-like structures. In C++, the structure tag-name als becomes a complete type name that can be used to declare objects. In C, the tag-name requires that the keyword struct precede it before it becomes a complete type. Mt im khc gia cu trc trong phong cch lp trnh C v C++: trong C++, tn ca cu trc cng tr thnh tn ca i tng cn m t. Trong C, tn cu trc cn c t kha l struct pha trc n n c th tr thnh tn ca i tng. Here is the same program, rewritten using a class y l chng trnh ging nh trn nhng s dng mt lp: #include <iostream> #include <string> using namespace std; class cl_type() { double balance; char name[40]; public:

119

cl_type(double b, char *n); void show(); }; cl_type::cl_type(double b, char *n ) { balance *b; strcpy(name,n); } void cl_type::show() { cout<< Name : <<name; cout<< : $<<balance; if(balance<0.0) cout<<***; cout<<\n; } int main() { cl_type acc1( 100.12,JohnSon); cl_type acc2( -100.12,Hedricks);

acc1.show(); acc2.show();

return 0; }

120

2. Here is an exemple that use a union to display the binary bit pattern, byte by byte, contained within a double value Di y l mt v d v s dng cu trc union biu din cc bit nh phn, tng byte mt, cha trong double #include <iostream> using namespace std; union bits { bits(double n); void show_bits(); double d; unsigned char c[sizeof(double)]; }; bits::bits(double n) { d=n; } void bits::show_bits() { int i,j; for ( j=sizeof(double)-1l;j>=0;j--) { cout<<"Bit pattern in byte "<<j<<" :"; for( i=128;i;i>>=1) if( i&c[j]) cout<<"1"; 121

else cout<<"0"; cout<<"\n"; } } int main() { bits ob(1991.829); ob.show_bits(); return 0; }

Kt qu xut ra mn hnh l : Bit pattern in bye 7:01000000 Bit pattern in bye 6:10011111 Bit pattern in bye 5:00011111 Bit pattern in bye 4:01010000 Bit pattern in bye 3:11100101 Bit pattern in bye 2:01100000 Bit pattern in bye 1:01000001 Bit pattern in bye 0:10001001

3. Both structures and unions can have constructors and destructors. The following example shows the strtrype class reworks as a structure. It contains both a constructor and a destructor function. 3. C cu trc structures v union c th c phng thc thit lp v ph hy. V d di y s ch ra l lp scrtype nh mt cu trc structure. N bao gm c

122

phng thc thit lp v phng thc ph hy.

#include <iostream> #include <cstring> #include <cstdlib> using namespace std; struct strtype { strtype(char *ptr); ~strtype(); void show(); private: char*p; int len; }; strtype::strtype(char *ptr) { len=strlen(ptr); p=(char*)malloc(len+1); if(!p) { cout<<" Allocation error\n"; exit(1); } strcpy(p,ptr);

123

} strtype::~strtype() { cout<<"Freeing p\n"; free(p); } void strtype::show() { cout<<p<< "- length= "<<len; cout<<"\n"; } int main() { strtype s1(" This is a test."), s2(" I like C++"); s1.show(); s2.show(); return 0; }

4. This program uses an anonymous union to display the individual bytes that comprise a double.( This program assumes that double are 8 bytes long.) 4. y l chng trnh s dng cu trc nc danh union din t cc byte ring bit ca kiu double .

#include <iostream> using namespace std; 124

int main() { union { unsigned char byte[8]; double value; }; int i; value =859655.324; for(i=0;i<8;i++) { cout<<(int)byte[i]<<" "; } return 0; }

As you can see, both value and bytes are accessed as if they were normal variables and not part of a union. Even though they are at the same scope level as any other local variables declared at the same point. This is why a member of an anonymous union cannot have the same name as any other variable known ti its scope. Nh cc bn thy, c value v bytes c truy cp nh l 1 bin bnh thng khng phi l 1 thnh phn ca cu trc nc danh union. Mc d chng c cng b l 1 thnh phn ca cu trc nc danh union, tn ca chng th cng chung 1 phm vi v chng dng chung 1 a ch nh. l ti sao m cc thnh phn ca cu trc nc danh union khng c t trng tn.

BI TP (Exercises):
125

Rewrite the stack class presented in Section 2.1 so it uses a structure rather than a class. Vit li lp chng trnh by mc 2.1 n s dng mt cu trc tt hn mt lp. Use a union class to swap the low and high-order bytes of an integer( assuming 16-bit integers; If yours computer uses 32- bit integer. Swap the bytes of a short int.) S dng mt lp union hon v cc byte thp v cao hn ca mt s nguyn (ly s nguyn 16 bit, nu my tnh bn s dng s nguyn 32 bit, hon v cc byte ca s nguyn ngn). 3. Explain wht an anonymous union is and how it differs from a normal union. Gii thch mt lp kt hp nc danh l g v cch phn bit n vi mt lp kt hp bnh thng.

1.6. IN-LINE FUNCTION - HM NI TUYN:


Before we continue this examination of classes, a short but related digression is needed. In C++, it is possible to define functions that are not actually called but, rather, are expanded in line, at the point of each call. This is much the same way that a C-like parameterized macro works. The advantage of in-line functions is that they have no overhead associated with the function aca be executed much faster than normal functions. ( Remember, the machine instructions that generate the function call and return take time each time a function is called. If there are parameters, even more time overhead is generated.) Trc khi chng ta tip tc vi bi kim tra ca kha hc, chng ta s tn mn 1 cht bn l.. Trong C++, mt hm c th thc s khng dc gi, nht l ch trn 1 dng, ngay thi im mi ln gi. Ging nh l cch thc th hin ca cc macro c tham s. The disadvantage of in-line functions is that if they are too large and called too often, your program grows larger. For this reason, in general only short functions are declared as in-line functions. iu thun li ca cc hm ni tuyn l chng khng phi c s kt hp gia hm 126

gi v hm tr v. iu c ngha rng hm ni tuyn c th thc thi nhanh hn cc hm bnh thng.( Nh rng,li ch dn my c hm gi v tr v mi ln hm dc gi. To declare an in-line function, simply precede the functions definition with the inline specifier. For example, this short program shows how to declare an inline function. iu bt cp ca hm ni tuyn l chng qu ln v thng c gi nhiu ln, chng trnh ca bn s ln hn. V l do, ch nhng hm ngn mi c m t nh l mt hm ni tuyn. In this example, the function even(), which returns true if its argument is even, is declared as being in-line. This means that the line . biu th mt hm ni tuyn, nh ngha hm vi t pha trc l inline. V d mt chng trnh ngn c s dng hm ni tuyn.

#include <iostream> using namespace std; inline int even(int x) { return !(x%2); } int main() { if(even(10)) cout<< 10 is even\n; if(even(11)) cout <<11 is even\n; return 0; }

In this example, the function even(), which returns true if its argument is even, is declared as being in-line. This means that the line 127

Trong v d trn, hm even() s tr v gi tr true nu biu thc xy ra, dc biu th nh l hm ni tuyn. 2 dng c ngha tng ng nhau: if(even(10)) cout<< 10 is even\n; if(!(10%2)) )) cout<< 10 is even\n; This example also points out another important feature of sung inline: an inline function must be defined before it is first called. If it isnt, the compiler has no way to know that it is supposed to be expanded in-line. This is why even() was defined before main(). V d ny cng ch ra c im quan trng khc trong vic s dng hm inline . Mt hm ni tuyn cn phi c nh ngha trc ln gi u tin. Nu khng, Trnh bin dch s khng hiu. l l do ti sao hm even() c nh ngha trc hm main(). The advantage of using rather than parameterized macros is towfold. First, it provides amore structured way to expand short functions in line. For example, when you are creating a parameterized macro, its easy to forget that extra parentheses are often needed to ensure proper in-line expansion in every case. Using in-line functions prevents such problems. Vic s dng inline th tt hn l dng macro c tham s 2 im. Th nht, n cung cp nhiu cch thit lp cu trc m rng cc hm ngn.. V d nh, khi chng ta ang to mt macro c tham s, chng ta s d dng qun them du ngoc n m rng thng cn chc chn s khai trin ni tuyn dc xy ra trong mi trng hp. S dng hm ni tuyn s trnh c vn ny. Second, an in-line function might be able to be optimized more thoroughly by the compiler than a macro expansion. In any event, C++ programmers virtually never use parameterized macros, instead relying on inline to avoid the overhead of a function call associated with a short function. Th 2, mt hm ni tuyn s c trnh bin dch tt hn so vi macro.. Trong mi trng hp, ngi lp trnh C++ thng khng bao gi s dng hm macro c tham s, thay vo ngi ta s dng inline trnh trng hp qu ti khi khi hm kt hp vi hm ngn. It is important to understand that the inline spectifier is a request, not a command, to the compiler. If, for various reasons, the compiler is unable to fulfill the request, the function is compiled as a normal function and the inline

128

request is ignored Cn hiu r inline l mt yu cu ch khng phi l mt lnh i vi trnh bin dch. Vi nhiu l do khc nhau, trnh bin dch s khng th hon thnh yu cu, hm s c bin dch nh hm bnh thng , v inline yu cu s b hy b. Depending upon your compiler, several restrictions to in-line functions may apply. For example, some compilers will not in-line a function if it contains a static variable, a loop statement, a switch or a goto, or if the function is recursive. You should check your compilers user manual for specific restrictions to in-line functions that might affect you. Da vo trnh bin dch ca bn, c mt vi hn ch ca hm ni tuyn.v d nh, mt vi trnh bin dch s khng cho hm ni tuyn l mt hm nu nh trong hm c kiu d liu static, vng lp, lnh switch v lnh goto, hoc l cc hm quy. Bn nn kim tra kh nng s dng ca trnh bin dch ca bn cc hn ch ca hm ni tuyn khng nh hng ti bn.

CC V D
Any type of function can be in-line, including function that are members of classes. For example, here the member function divisible() is in-lined for fast execution. ( The function returns true if its argument can be evenly divided by its second.)

Mi loi hm u c th l hm ni tuyn, bao gm nhng hm m l thnh phn ca cc lp. V d, di y l hm divisible() c dng ni tuyn cho vic thao tc nhanh.( Hm tr v true nu nh biu thc u tin ca n xy ra) #include <iostream> using namespace std; class samp { int i,j; public: samp(int a,int b); 129

int divisible(); }; samp::samp(int a,int b) { i=a; j=b; } inline int samp::divisible() { return !(i%j); } int main() { samp ob1(10,2),ob2(10,3); if(ob1.divisible()) cout<<" 10 chia het cho 2\n"; if(ob2.divisible()) cout<<" 10 chia het cho 3 \n"; return 0; }

2. It is perfrctly permissible to in-line an overloaded function. For example, this program overloads min() three ways. Each way is also declared as inline. C l s hon ho hn khi s dng hm ni tuyn dng cho cc hm np chng. y l v d s dng hm np chng min() trong 3 cch. Mi cch l mt hm ni tuyn.

130

#include <iostream> using namespace std; inline int min(int a,int b) { return a<b?a:b; } inline long min(long a,long b) { return a<b?a:b; } inline double min(double a,double b) { return a<b?a:b; } int main() { cout<<min(-10,10)<<"\n"; cout<<min(-10.01,100.02)<<"\n"; cout<<min(-10L,12L)<<"\n"; return 0; }

BI TP (Exercises):

131

1. In Chapter 1 you overloaded the abs () functuion so that it could find the
absolute of integers, long integers, and doubles. Modify that program so that those functions are expanded. Trong chng 1, bn nh ngha chng hm abs() tm gi tr tuyt i ca s nguyn, s nguyn di v s thc di. Hy sa i chng trnh cc hm c m rng ni tuyn.

2.Why might the following function not be in-lineed by your compiler?


Ti sao hm sau khng th dch ni tuyn bi trnh bin dch? void f1() { int I; for(i=0; i<10; i++) } cout << i;

1.7. AUTOMATIC IN-LINING - HM NI TUYN T NG:


If a member functions definition is short enough, the definition can be included inside the class declaration. Doing so causes the function to automatically become an i-line function, if possible. When a function is defined within a class declaration, the inline keyword is no longer necessary.(However, it is not an error to use it in this situation.) For example, the divisible () function from the preceding section can be automatically in-line as shown here. Nu mt thnh phn ca hm c nh ngha qu ngn, th nh ngha hm c th nm bn trong ca s nh ngha 1 lp. Lm nh vy nhiu kh nng s t ng to ra mt hn ni tuyn. Khi mt hm c nh ngha trong khai bo mt lp, t kha inline th khng cn thit. (Tuy nhincng khng c li nu s dng

132

n trong trng hp ny). Chng hn nh hm divisible() phn trc c th t ng ni tuyn nh sau: #include <iostream> using namespace std; class samp { int i,j; public: samp(int a,int b); int divisible() { Return (!(i%j)); }; samp::samp(int a,int b) { i=a; j=b; } int main() { samp ob1(10,2),ob2(10,3);

//cau lenh dung if(ob1.divisible()) cout<<" 10 chia het cho 2\n";

//cau lenh sai 133

if(ob2.divisible()) cout<<" 10 chia het cho 3 \n";

return 0; } As you can see, the code associated with divisible() occurs inside the declaration for the class samp. Further notice that no other definition of divisible( ) is needed-or permitted. Defining divisible( ) inside samp cause it to be made into an in-line function automatically. Nh bn thy y, on m kt hp vi hm divisible() xut hin bn trong khai bo lp samp. Hn na, cng cn ch rng khng cho php cch nh ngha khc cho hm divisible(). nh ngha hm ny bn trong lp samp khin n t ng tr thnh mt hm ni tuyn. When a function define inside the class declaration cannot be made into an inline function (because a restriction has been violated), it is automatically made into a regular function. Khi mt hm c nh ngha bn trong mt khai bo lp th khng th c lm bn trong mt hm ni tuyn (do vi phm mt hn ch), n s t ng tr thnh mt hm bnh thng. Notice how divisible( ) is defined within samp, paying particular attention to the body. It occurs all on one line. This format is very common in C++ programs when a function is declared within a class declaration. It allows a declaration to be more compact. However, the samp class could have been written like this: Ch cch nh ngha hm divisible() trong lp samp, c bit ch n phn thn hm. N xut hin tt c trn mt dng. nh dng ny rt ph bin trong cc chng trnh C++ khi khai bo mt hm bn trong mt khai bo lp. N cho php vic khai bo cht ch hn. Tuy nhin, lp samp cng c th c vit nh sau:

class samp {

134

int i, j; public: samp(int a, int b);

//ham divisible() duoc dinh nghia o day va tu dong noi tuyen int invisible() { return !(i%j); } };

In this version, the layout of devisible( ) uses the more-or-less standard indentation style. From the compiles point of view, there is no defference between the compact style and the standard style. However, the compact style is commonly found in C++ programs when short functions are defined inside a class definition. Trong phin bn ny, cch b tr hm divisible() s dng nhiu hoc t hn kiu tht dng chun. T gc nhn ca trnh bin dch th khng c s khc bit no gia kiu kt hp ny vi kiu chun. Tuy nhin, kiu kt hp thng c tm thy hn trong cc chng trnh C++, khi cc hm ngn c nh ngha bn trong mt nh ngha lp. The same restrictions that apply to normal in-line functions apply to automatic in-line functions apply to automatic in-line functions within a class declaration. Cc hn ch nh nhau ny chp nhn cc hm ni tuyn bnh thng to thnh cc hm ni tuyn t ng trong mt khai bo lp.

CC V D:
135

1. Perhaps the most common use of in-line functions defined within a class is to define constructor and destructor functions. For example, the samp class can more efficiently be defined like this: C l, cch s dng ph bin nht ca cc hm ni tuyn c nh ngha bn trong mt lp l nh ngha cc hm dng v ph hy. V d nh lp samp c th c nh ngha mt cch hiu qu hn nh sau: #include <iostream.h> class samp { int i, j; public: samp(int a, int b) { i=a; j=b; } int divisible() { return !(i%j); } };

Vic nh ngha hm samp() bn trong lp samp l tt nht khng nn nh ngha theo cch khc.

2. Sometimes a short function will be included in a class declaration even 136

though the automatic in-lining feature is of little or no value. Consider this class declaration: i khi mt hm ngn cha trong mt khai bo lp d cho khng c gi tr no ni tuyn. Xem on khai bo lp sau: class myclass{ int i; public: myclass(int n) { i=n; } void show() { cout << i; } };

y, hm show() t ng tr thnh hm ni tuyn. Tuy nhin, nh bn bit y, cc ton t nhp xut kt ni n CPU hay cc ton t nh chm n mc nh hng loi tr n hm c gi chng c bn b mt. Mc d vy, trong cc chng trnh C++, ta vn thng thy cc hm nh ca loi ny c khai bo bn trong mt lp thun tin hn v khng gy sai xt.

BI TP:
1. Chuyn i lp stack v d 1, mc 2.1 n s dng hm ni tuyn t ng v tr thch hp. 2. Chuyn i lp strtype v d 3, mc 2.2 n s dng hm ni tuyn t ng.

137

CC K NNG KIM TRA:


im ny, c th bn nn lm cc bi tp v tr li cc cu hi sau: Phng thc thit lp l g? Phng thc ph hy l g? V chng c thc thi khi no? Khi to lp line v mt ng trn mn hnh. Lu di ca dng vo mt bin kiu nguyn ring l len. Ta c phng thc thit lp ca lp line vi mt tham s l di ca dng. Nu h thng ca bn khng h tr v ha th c th biu din dng bng cc du *. Chng trnh sau cho kt qu g? #include <iostream.h> int main() { int i = 10; long l = 1000000; double d = -0.0009;

cout << i << cout << \n;

<< l <<

<< d;

return 0; }

To mt lp con khc k tha t lp area_cl mc 2.3, bi tp 1. Gi lp ny l cylinder v tnh din tch xung quanh ca hnh tr. Gi : din tch xung quanh ca hnh tr l 2 * pi * R2 + pi * D * height. Th no l mt hm ni tuyn? u im v khuyt im ca n?

138

Chnh sa li chng trnh sau cho tt c hm thnh phn u t ng ni tuyn: #include < iostream.h> class myclass { int i, j; public: myclass(int x, int y); void show(); };

myclass :: myclass(int x, int y) { i= x; j= y; }

void myclass :: show() { cout << i << } << j << \n;

int main() { myclass count(2, 3); count.show(); 139

return 0; }

S khc bit gia mt lp v mt cu trc l g? on khai bo sau c hp l khng? union { float f; unsigned int bits; };

Cunulative Skiills Check


Phn ny kim tra bn tch ly nh th no trong chng ny v chng trc: 1. To mt lp tn l prompt. To hm dng mt chui ch dn vi cc la chn. Hm dng xut mt chui v sau nhp vo mt s nguyn. Lu li gi tr trong mt bin cc b gi l count. Khi mt i tng kiu prompt b ph hy th rung chung im cui cng nhiu bng s ln bn nhp vo. 2. Trong chng 1, bn vit mt chng trnh chuyn i t feet sang inches. By gi hy to mt lp lm cng vic ging nh vy. Lp ny lu tr gi tr feet v gi tr inch tng ng. Xy dng phng thc thit lp cho lp vi tham s feet v mt phng thc thit lp trnh by gi tr inch tng ng. 3. To mt lp tn l dice cha mt bin nguyn cc b. To hm roll() s dng b sinh s ngu nhin chun, l hm rand(), pht sinh mt s trong khong 1 n 6. Sau dng hm roll() trnh by gi tr .

140

CHAPTER 3 A CLOSER LOOK AT CLASSES - Xt K Hn V Lp


CHAPTER OJECT
3.1 ASSIGING OBJECT.

Gn i Tng.
3.2 PASSING OBJECT TO FUNCTION.

Truyn Cc i S Cho Cc Hm.


3.3 RETURNING OBJECT FROM FUNCTIONS.

Tr i Tng V T Hm.
3.4 AN INTRODUCTION TO FRIEND FUNCTIONS SKILLS CHECK.

Gii Thiu Cc Hm Friend.

In this chapter you continue to explore the class. You will learn about assigning objects, passing objects to functions, and return objects from functions. You will also learn about an important new type of function: the friend Trong chng ny bn s tip tc hc v lp. Bn s hc v vic gn cc i tng, chuyn cc i tng qua cc hm, v tr li cc i tng t cc hm. Bn s c hoc thm mt loi hm quan trng na l : Hm bn. Before proceeding, you should be able to correctly answer the following questions and do the exercises. Trc khi tip tc bn nn tr li chnh xc cc cu hi sau y v lm cc bi tp sau.

Given the following class, what are the names of its cuntructor and destructor function?

141

Trong lp sau, u l tn hm to v hm hy? Class widget { int x, y; public: //fill in constructor and destructor functions }; When is a constructor function called? When is a destructor function called? . Khi no hm to c gi? Khi no hm hy c gi? Given the following base class, show how it can be inherited by a deliverd class called Mars Cho lp c bn sau, hy ch ra bng cch no n c th k tha lp gc mang tn Sao Ha. class planet { int moons; double dist_from_sun; double dismeter; double mass; public: // };

There are two way to cause a function to be expanded in line. What are they? C hai cch lm cho mt hm c m rng ra trong ranh gii. Chng l hai cch no? Give two possible restrictions to in-line functions. Hy a ra hai cch hn ch kh thi i vi cc hm trong gii hn.

142

Given the following class, show how an object called ob that passes the value 100 to a and to c would be declared. Cho lp sau, hy cho bit bng cch no i tng mang tn ob gn gi tr 100 cho a v X cho c nh tuyn b. class sample { int a; char c; public: sample(int x, char ch) { a = x; c = ch;} };

1.1. Assigning Objects - Gn i tng:


One object can be assigned to another provided that both objects are of the same type. By default, when one object is assigned to another, a bitwise copy of all data members is made. For example, when an object called o1s is assigned to another object called o2, the contents of all of o1s data are copied into the equivalent members of o2. This is illustrated by the following program: Mt i tng c th gn gi tr cho mt i tng khc khi hai i tng ny c cng mt kiu. Mc nh, khi mt i tng c gn cho i tng khc, th mt bn copy tt c cc thnh phn ca i tng s c to ra. V d, khi gn i tng o1 cho o2, th ni dung bn trong d liu ca o1 s c copy sang cho d liu ca o2. Sau y l chng trnh minh ha: // An example of object assignment #include <iostream> Using namespace std;

Class myclass { int a, b; public: 143

void set(int i, int j) {a = i; b = j; } void show() {cout <<a << << b <<\n;} };

int main() { Myclass o1, o2 o1.set(10, 4); // assign o1 to o2 o2 = o1;

o1.show(); o2.show();

return 0; }

Here, object o1, has its member variables a and b set to the values 10 and 4, respectively. Next, o1 is assigned to o2. This causes the current value of o1.a to be assigned to o2.a and o1.b to be assigned to o2.b. Thus, when run, this program displays y, i tng o1, vi cc bin thnh vin l a v b ln lt c gn gi tr 10 v 4. Tip , o1 gn cho o2. Vic ny gy ra vic cc gi tr o1.a c gn cho o2.a v o1.b c gn cho o2.b. V vy, khi chy chng trnh, kt qu s l: 10 10 4 4

144

Keep in mind that an assignment between two objects simply makes the data in those objects identical. The two objects are still completely separate. For example, after the assignment, calling o1.set( ) to set the value of o1.a has no effect on o2 or its a value. Hy nh rng vic gn gi tr gia hai i tng n gin l nhn i d liu ra 1 i tng khc. Hai i tng ny l hon ton khc nhau. V d, sau khi gn, ta gi o1.set( ) thao tc trn gi tr o1.a th hon ton khng nh hng g n gi tr ca i tng o2.

V D (EXAMPLES):
Only objects of the same type can be used in an assignment statement. If the objects are not of the same type, a compile-time error is reported. Further, it is not sufficient that the types just be physically similar-their type names must be the same. For example, this is not a valid program: Ch c nhng i tng cng kiu mi s dng c lnh gn. Nu cc i tng khng cng mt kiu, th li trong qu trnh bin dch s xy ra. Hn na n khng quyn hn khi ch c phn thn l ging nhau m tn kiu cng phi ging nhau. V d chng trnh sau y l sai:

// This program has an error #include <iostream> using namespace std;

class myclass { int a, b; public: void set(int i, int j) { a = i; b = j; } void show(){cout << a << << b <<\n; } }; 145

/* This class is similar to myclass but uses a different class name and thus appears as a different type to the compiler. */

Class yourclass { int a, b; public: void set( int i, int j) { a = i; b = j; } void show( ) {cout<<a<< <<b<<\n;} };

int main() { myclass o1; yourclass o2;

o1.set(10, 4);

o2 = o1; //ERROR, objects not of same type

o1.show(); o2.show();

return 0;

146

In this case, even though myclass and yourclass are physically the same, because they have different type names, they are treated as differing types by the conmpiler. Trong trng hp ny, mc d myclass v yourclass ging nhau v phn thn, nhng chng c tn kiu khc nhau, v chng s c trnh bin dch coi nh hai kiu.

2. It is important to understand that all data members of one object are assigned to another when an assignment is performed. This includes compound data such as arrays. For example, in the following version of the stack example, only s1 has any characters actually pushed onto it. However, because of the assignment, s2s stck array will also contain the characters a, b and c. 2. Quan trng l bn phi hiu rng tt c d liu thnh vin ca mt i tng s dc gn cho cc thnh phn ca i tng khc khi vic gn c thc hin. Vic ny bao gm kiu d liu a hp nh mng. V d, trong chng trnh sau ca stack, ch s1 thc s c cc k t dc y vo. Tuy nhin bi v php gn, stck ca i tng s2 cng cha nhng k t nh a, b, c.

#include <iostream> using namespace std; #define SIZE 10 // Declare a stack class for characters Class stack { char stck[SIZE]; //holds the stack int tos; // index of the top stack public:

147

stack(); // constructor void push(char ch); // push character on stack char pop(); // pop character from stack };

// Initialize the stack stack::stack() { Cout << Constructor a stack\n; tos=0; } // Push a character void stack::push(char ch) { if(tos==SIZE) { cout << Stack is full\n; return; } stck[tos] = ch; tos++; }

// Pop a character char stack::pop()

148

{ if(tos==0) { cout << Stack is empty\n; return; //Return null on empty stack } tos--; return stck[tos]; }

int main() { /* Create two stacks that are automatically initialized */ stack s1, s2; int i;

s1.push(a); s1.push(b); s1.push(c);

//clone s1 s2 = s1; //now s1 and s2 are indentical

149

for(i=0; i<3; i++) cout << Pop s1: << s1.pop() << \n; for(i=0; i<3; i++) cout << Pop s2: << s2.pop() << \n;

return 0; }

3. You must exercise some case when assigning one object to another. For example, here is the strtype class developed in Chapter 2, along with a short main( ). See if you can find an error in this program. 3. Bn phi thc hin trong vi trng hp khi gn i tng ny cho i tng khc. V d, y c lp strtype c ni n trong chng hai, vi hm main( ) ngn gn. Bn hy tm li sai trong chng trnh sau

// This program contains an error #include <iostream> #include <cstring> #include <cstdlib> using namespace std;

class strtype { char *p; int len; public:

150

strtype(char *ptr); ~strtype(); void show(); };

strtype::strtype(char *ptr) { len = strlen(ptr); p = (char *) malloc(len+1); if(!p) { cout << Allocation error\n; exit(1); } strcpy(p, ptr); }

strtype::~strtype() { cout << Freeing p\n; free(p); }

void strtype::show() {

151

cout << p << length: << len; cout << \n; }

int main() { strtype s1(This is a test), s2(I like C++);

s1.show(); s2.show();

//assign s1 to s2 - - this generates an error s2 = s1;

s1.show(); s2.show();

return 0; }

The trouble with this program is quite insidious. When s1 and s2 are created, both allocate memory to hold their respective strings. A pointer to each objects allocated memory is stored in p. When a strtype object is destroyed, this memory is released. However, when s1 is assigned to s2, s2s p now points to he same memory as s1s p. Thus, when these objects are destroyed, the memory pointed to by s1s p is freed twice and the memory originally pointed to by s2s p is not freed at all.

152

Li ca chng trnh ny kh l kh thy. Khi s1 and s2 c to ra, c hai u c cp pht vng nh lu li chui ca chng. Mt con tr n nh cp pht cho i tng lu trong p. Khi i tng strtype b ph hy, vng nh ny cng c gii phng. Tuy nhin, khi s1 c gn cho s2, th con tr p ca s2 cng tr v ni m con tr p ca s1 ang nm gi. V vy, khi nhng i tng ny b ph hy, vng nh c tr bi con tr p ca s1 c gii phng n hai ln, v vng nh ban u c tr bi con tr ca s2 th khng c gii phng hon ton. While benign in this context, this sort of the problem occurring in a real program will cause the dynamic allocation system to fail, and possibly even cause a program crash. As you can see from the preceding example, when assigning one object to another, you must make sure you arent destroying information that may be needed later. Trong tnh hung tt, mt loi li s xy ra trong chng trnh thc khin cho s cp pht ng ca h thng b trc trc, v thm ch c th gy ra mt s h hi chng trnh. Nh bn thy v d trc, khi gn mt i tng ny cho i tng khc th bn cn m bo rng bn khng hy hoi thng tin m c th cn dng sau .

Exercises
What is wrong with the following fragment? C g sai trong on chng trnh sau //This program has an error #include <iostream> using namespace std;

class c11 { int i, j; public: c11(int a, int b) { i = a; j = b; } 153

// };

class c12 { int i, j; public: c12(int a, int b) { i = a; j = b; } // };

int main() { c11 x(10, 22); c12 y(0, 0); x = y;

// }

Using the queue class that you created for Chapter 2, Section 2.1, Exercise 1, show how one queue can be assigned to another. S dng lp queue m bn to ra trong chng hai, mc 2.1, bi tp 1, xem coi mt i tng queue c gn cho i tng khc nh th no. If the queue class from the preceding question dynamically allocates memory to hold the queue, why, in this situation, can one queue not be assigned to another?

154

Nu nh lp queue trong cu hi trc cp pht b nh ng gi queue, th ti sao trong tnh hung ny, mt i tng queue khng th c gn nh vy?

1.2. PASSING OBJECTS TO FUNCTIONS Truyn cc i tng cho hm:


Objects can be passed to functions as a arguments in just the same way that other types of data are passed. Simply declare the functions parameter as a class type and then use an object of that class as an argument when callings the function. As which other of data, by default all objects are passed by value to a function Cc i tng c th c chuyn tip qua hm nh mt i s theo cch m cc kiu d liu khc c chuyn qua. Ni mt cch n gin l cc tham s ca hm l mt kiu lp v sau dng mt i tng ca lp m c coi nh tham s khi gi hm. Nhng phn ca d liu mc nh s c chuyn qua hm bi gi tr.

Example
Here is a short example that passes an object to a function: y l mt v d ngn ca vic chuyn mt i tng n hm #include <iostream> using namespace std; class samp { int i; public: samp(int n) { i = n; } int get_i() { return i; }

155

}; //Return square of o.i int sqr_it(samp o) { return o.get_i() * o.get_i(); } int main() { samp a(10), b(2); cout << sqr_it(a) << \n; cout << sqr_it(b) << \n;

return 0; }

This program creates a class called samp that contains one integer variable called i. The function sqr_it( ) takes an argument of type samp and returns the square of that objects i value. The output from this program is 100 followed by 4. Chng trnh ny to ra mt lp mang tn samp cha mt bin nguyn mang tn i. Hm sqr_it( ) mang mt i s kiu samp v tr v gi tr i ca i tng . Gi tr xut ra ca chng trnh l 100 theo sau 4. As stated, the default method of parameter passing in C++, including objects, is by value. This means that a bitwise copy of the argument is made and it is this copy that is used by the function. Therefore, changes to the object inside the function do not effect the calling object. This is illustrated by the following example: Nh ni, phng php mc nh ca chuyn tip tham s trong C++, bao gm cc i tng, nh l gi tr. C ngha l mt bn copy ca i s s c to ra v bn copy ny c s dng nh hm. V vy, vic thay i hm bn trong i 156

tng th khng nh hng n vic gi i tng. iu ny c minh ha bi v d sau /* Remember, objects, like other parameters, are passed by value. Thus changes to the parameter inside a function have no effect on the object used in the call. */ #include <iostream> using namespace std;

class samp { int i; public: samp(int n) { i = n; } void set_i(int n) { i = n; } int get_i() {return i; } };

/* Set o.i to its square. This has no effect object used to call sqr_it(), however */ void sqr_it(samp o) { o.set_i(o.get_i() * o.get_i());

on the

cout << Copy of has I value of << o.get_i();

157

cout << \n; }

int main() { samp a(10);

sqr_it(a); //passed by value

cout << But, a.i is unchanged in main: ; cout << a.get_i(); //displays 10

return 0; }

The output displayed by this program is Kt qu hin th ca chng trnh l Copy of a has i value of 100 But, a.i is unchanged in main: 10 As with other types of variables, the address of an object can be passed to a function so that the argument used in the call can be modified by the function. For example, the following version of the program in the preceding example does, indeed, modify the value of the object whose address is used in the call to sqr_it( ). Nh cc kiu bin khc, a ch ca mt i tng c th c chuyn n mt hm nh mt i s c dng trong vic gi iu chnh hm. V d, trong chng trnh v d trc thc hin, thc vy, iu chnh gi tr ca i tng m a ch ca n c dng gi hm sqr_it( ).

158

/* Now that the address of an object is passed to sqr_it(), the function can modify the value of the argument whose address is used in the call */ #include <iostream> using namespace std;

class samp { int i; public: samp(int n) { i = n; } void set_i(int n) { i = n; } int get_i() {return i; } };

/* Set o.i its square. This affects the calling arhument */ void sqr_it(samp *o) { o->set_i(o->get_i() * o->get_i());

cout<<Copy of a has i value of << o->get_i(); cout << \n;

159

int main() { samp a(10); sqr_it(&a); // pass as address to sqr_it()

cout << Now, a in main() has been changed: ; cout << a.get_i(); // displays 100

return 0; }

This program now displays the following output Chng trnh lc ny xut ra: Copy of a has i value of 100 Now, a in main() has been changed: 100 When a copy of an object is made when being passed to a function, it means that a new object comes into existence. Also, when the function that the object was passed to terminates, the copy of the argument is destroyed. This raises two questions. First, is the objects constructor called when the copy is made? Second, is the objects destructor called when the copy is destroyed? The answer may, at first, seem surprising. Khi mt bn copy mt i tng c to ra th c chuyn tip n hm, ngha l mt i tng n trong s tn ti. Hn na,khi hm m i tng c chuyn qua th kt thc, bn copy ca i s b ph hy. Vic ny lm ny sinh ra 2 cu hi. Mt l, hm to c gi khi bn copy c to ra? Hai l, hm hy c gi khi bn copy b hy? Cu tr li c th l ngay lc u tin, dng nh

160

rt ngc nhin phi khng.

When a copy of an object is made to be used in a function call, the constructor function is not called. The reason for this is simple to understand if you think about it. Since a constructor function is generally used to initialize some aspect of an object, it must not be called when making a copy of an already existing object passed to a function. Doing so would alter the contents if the object. When passing an object to a function, you want the current state of the object, not its initial state. Khi mt bn copy ca mt i tng c to ra s dng trong hm, th hm i tng khng c gi. L do ca vic ny tht n gin hiu nu bn ngh v n. T lc hm to c dng khi to mt kha cnh ca i tng, n phi c gi khi to ra mt bn copy ca mt i tng tn ti ty bin qua hm. Vic lm ny s bin i ni dung ca mt i tng. Khi chuyn mt i tng n hm, bn mun trng thi hin ti ca i tng ch khng phi trng thi khi to ca i tng. However, when the function terminates and the copy is destroyed, the destructor function is called. This is because the object might perform some operation that must be undone when it goes out of scope. For example, the copy may allocate memory that must be released. Tuy nhin, khi hm kt thc v bn copy b hy, hm hy s c gi. V i tng thi hnh mt s thut ton m n cha lm khi n vt khi tm vc. V d, bn copy c th cp pht vng nh th phi c gii phng.

To summarize, when a copy of an object is created because it is used as an argument to a function, the constructor function is not called. However, when the copy is destroyed (usually by going out of scope when the function returns), the destructor function is called. Tm li, khi mt bn copy ca mt i tng c to ra bi v n c dng nh mt i s vi hm, hm to khng c gi. Tuy vy, khi bn copy b ph hy (Thng thng l khi hm tr v v vt qu tm vc), hm hy s c gi. The following program illustrates the preceding discussion: Chng trnh sau minh ha cho phn vit trn:

161

#include <iostream> using namespace std;

class samp { int i; public: samp(int n) { i = n; cout << Constructing\n; } ~samp() { cout << Destructing\n;} it get_i() { return i;} };

//Return square of oi int sqr_it(samp o) { return o.get_i() * o.get_i(); }

int main() { samp a(10);

162

cout << sqr_it(a) << \n;

return 0; }

This function displays the following Hm ny s hin th kt qu sau Constructing Destructing 100 Destructing

As you can see, only one call to the constructor function is made. This occurs when a is created. However, two calls to the destruction are made. One is for the copy created when a is passed to sqr_it( ). The other is for a itself. Nh bn thy, ch mt li gi hm to c to ra. Vic ny xy ra khi a c to ra. Tuy nhin, hai li gi hm hy c to ra. Mt cho vic to ra bn copy khi a c chuyn tip n hm sqr_it( ). Ci kia l cho chnh bn thn a. The fact that the destructor for the object that is the copy of the argument is executed when the function terminates can be a source of problems. For example, if the object used as the argument allocates dynamic memory and frees that memory when destroyed, its copy will free the same memory when its destructor is called. This will leave the original object damaged and effectively useless. (See exercise 2, just ahead in this section, for an example.) It is important to guard against this type of error and to make sure that the destructor function of the copy of an object used in an argument does not cause side effects that alter the original argument. S tht, hm hy cho mt i tng m bn copy c thc thi khi hm kt thc c th l ngun gc ca nhng rc ri ny. V d, nu i tng c dng nh l i s th cp pht b nh ng v gii phng b nh khi ph hy, bn copy ca 163

n cng s gii phng cng mt b nh khi hm hy ca n c gi. Vic ny s lm cho i tng ban u b h hi v nhng nh hng khng tt. (Hy nhn bi tp 2 ch phn u l mt v d.) N rt quan trng trong vic m bo kiu ny khi li v chc rng hm hy ca mt i tng copy c s dng trong mt i s khng gy ra nh hng n i tng ban u. As you might guess, one way around the problem of a parameters destructor function destroying data needed by the calling argumeny is to the address of the object and not the object itself. When an address is passed, no new object is created, and therefore, no destructor is called when the function returns. (As you will see in the next chapter, C++ provides a variation on this theme that offers a very elegant alternative.) Nh bn c chng, mt th xung quanh vn ca hm hy mt tham s m ph hy d liu cn thit bi vic gi i s l a ch ca i tng ch khng phi bn thn i tng. However, an even better solution exists, which you can use after you have learned about a special type of constructor called a copy constructor. A copy constructor lets you define precisely how copies of objects are made. (Copy constructors are discussed in Chapter 5.) Tuy nhin, mt bin php tt hn tn ti m bn c th dng sau khi hc v kiu hm to c bit mang tn hm to copy. Hm to copy cho php bn nh ngha chnh xc cc m bn copy i tng c to ra. (Chng ta s ni v hm tao copy ny trong chng 5.)

Exercises
1. Using the stack example from section 3.1, Example 2, add a function called showstack( ) that is passed an object of type stack. Have this function display the contents of a stack. Dng v d stack (ngn xp) trong phn 3.1, v d 2, thm hm c tn showstack m c chuyn tip mt i tng kiu stack. Hm ny biu th ni dung ca stack. As you know, when an object is passed to a function, a copy of that object is made. Further, when that function returns, the copys destructor function is called. Keeping this in mind, what is wrong with the following program? Nh bn bit, khi mt i tng c chuyn tip n hm, mt bn copy ca i tng s c to ra. Hn na, khi hm tr li, th hm hy ca bn copy c 164

gi. Hy nh iu ny, gi th xem trong on chng trnh sau sai ci gi

//This program contains an error #include <iostream> #incldue <cstdlib> using namespace std;

class dyna { int *p; public: dyna(int i); ~dyna() { free(p); cout << freeing \n; } int get() { return *p; } };

dyna::dyna(int i) { P = (int *) malloc(sizeof(int)); if(!p) { cout << Allocation failure\n; exit(1); }

165

*p = i; }

// Return negative value of *ob.p int neg(dyna ob) { return ob.get(); }

int main() { dyna o(10);

cout << o.get() << \n; cout << neg(o) << \n;

dyna o2(20); cout << o2.get() << \n; cout << neg(o2) << \n;

cout << o.get() << \n; cout << neg(o) << \n;

return 0;

166

1.3. RETURNING OBJECT FROM FUNCTIONS Tr v i tng cho hm:


Just as you can pass objects to functions, function can return objects. To do so, first declare the function as returning a class type. Second, return an object of that type using to understand about returning objects from function, however: when an object is returned by a function, a temporary object is automatically created which holds the return value. It is this object that is actually returned by the function. After the value has been returned, this object is destroyed. The destruction of this temporary object might cause unexpected side effects in some situation, as is illustrated in Example 2 below. Nh bn c th chuyn i tng qua hm, th hm cng c th tr v i tng. lm vy, trc tin phi khai bo hm c kiu tr v mt lp. Th hai, tr v mt i tng ca kiu dng khi hiu vic tr i tng v t hm, tuy nhin: khi mt i tng c tr v bi mt hm, mt i tng tm thi s c t ng to ra nm gi gi tr tr v. N s l i tng ny khi thc s c tr v t hm. Sau khi gi tr c tr v, i tng tm thi ny b ph hy. S ph hy i tng tm thi ny c th gy ra mt vi nh hng khng mong i trong mt s tnh hung, nh v d 2 minh ha di y

Example
Here is an example of a function that returns an object: Sau y l v d v vic hm tr v mt i tng //Returning an object #include <iostream> #incldue <cstring> using namespace std;

167

class samp { char sp[80]; public: void show() { cout << s << \n; } void set(char *str) { strcpy(s, str); } };

//Return an object of type samp samp input() { char s[80]; samp str;

cout << Enter a string: ; cin >> s;

str.set(s);

return str; }

int main() {

168

samp ob;

//assign returned objects to ob ob = input(); ob.show();

return 0; }

In this example, input( ) creates a local object called str and then reads a string from the keyboard. This string is copied into str.s, and then str is returned by the function. This object is then assigned to ob inside main( ) when it is returned by the call to input( ). Trong v d ny, hm input( ) to ra mt i tng cc b mang tn str v sau c 1 chui t bn phm. Chui ny c copy vo str.s v sau str c tr v bi hm. i tng ny sau c gn cho ob trong hm main( ) khi n c tr v bi vic gi thc hin hm input( ). You must be careful about returning objects from function if those objects contain destructor functions because the returned object goes out of scope as soon as the value is returned to the calling routine. For example, if the object returned by the function has a destructor that frees dynamically allocated memory, that memory will be freed even though the object that is assigned the return value is still using it. For example, consider this incorrect version of the preceding program: Bn phi cn thn vi vic tr i tng v t hm nu i tng cha hm hy bi v vic tr i tng lm cho vt khi tm vc ngay khi m gi tr c gi chng trnh con. V d, nu i tng tr v bi hm c phng thc hy th s gii phng vng nh cp pht ng, vng nh s b gii phng ngay c khi i tng m n c gn tr v gi tr vn cn s dng n. V d, xem xt on chng trnh li sau //An error generated by returning an object #include <iostream>

169

#include <cstring> #include <stdlib> using namespace std;

class samp { char *s; public: samp() { s = \0; } ~samp() { if(s) free(s); cout << Freeing s\n;} void show() { cout << s << \n; } void set(char *str); }; //Load a string void samp::set(char *str) { s = (char *) malloc(strlen(str)+1); if(!s) { cout << Allocation error\n; exit(1); }

strcpy(s, str); }

170

//Return an object of type samp samp input() { char s[80]; samp str;

cout << Enter a string: ; cin >> s;

str.set(s);

return str; } int main() { samp ob;

//assign returned objects to ob ob = input(); ob.show();

return 0;

171

The output from this program is shown here: Mn hnh hin th: Enter a string : Hello Freeing s Freeing s Hello Freeing s Null pointer assignment

Notice that samps destructor function is called three times. First, it is called when the local object str goes out of the scope when input( ) returns. The second time ~samp( ) is called is when the temporary object returned by input( ) is destroyed. Remember, when an object is returned from a function, an invisible (to you) temporary object is automatically generated which holds the returne value. In this case, this object is simply a copy of str, which is the return value of the function. Therefore, after the function has returned, the temporary objects destructor is executed. Finally, the destructor for object ob, inside main( ), is called when the program terminates. Ch rng hm hy ca samp c gi n 3 ln. Ln u, n c gi khi bin cc b str vt khi tm vc khi hm input() tr v. Ln hai hm ~samp( ) c gi khi i tng tm thi tr v bi hm input( ) b ph hy. Nh rng, khi mt i tng c tr v t hm, coi nh l mt i tng n (vi bn) th c t ng pht xut v gi gi tr tr v. Trong trng hp ny, i tng ny n gin l mt bn copy ca str l gi tr tr v ca hm. V vy, sau khi hm tr v, hm hy ca i tng tm thi ny c thc thi. Cui cng, hm hy ca i tng ob bn trong hm main( ) c gi khi chng trnh kt thc.

The trouble is that in this situation, the first time the destructor executes, the memory allocated to hold the string input by input( ) is freed. Thus, not only do the other two calls to samps destructor try to free an already released piece

172

of dynamic memory, but they destroy the dynamic allocation system in the process, as evidenced by the run-time message Null pointer assignment. (Depending upon your compiler, the memory model used for compilation, and the like, you may or may not see this message if you try this program.) Phin toi trong tnh hung ny, ln u tin hm hy thc thi, vng nh c cp pht lu chui nhp vo bi hm input( ) th b gii phng. V vy, khng nhng hai li gi hm hy khc ca samp c gng xa vng nh c gii phng khi b nh ng m chng cn ph hy h thng cp pht vng nh ng trong x l, bng chng l xut hin thng bo li trong x l Null pointer assignment. (Ty thuc vo trnh bin dch ca bn, loi vng nh dng cho bin dch v ging nh vy m bn c th thy hay khng thy thng bo li ny khi bn chy th chng trnh ny. The key point to be understood from this example is that when an object is returned form a function, the temporary object used to effect the return will have its destructor function called. Thus, you should avoid returning objects in which this situation is harmful. (As you will learn in Chapter 5, it is possible to use a copy constructor to manage this situation.) Mu cht cn hiu sau v d ny l khi mt i tng c tr v t hm, th i tng tm thi c dng tc ng s tr v s lm cho hm hy ca n c gi. V vy, bn nn trnh tr v i tng trong tnh hung c hi nh vy. (Bn s hc trong chng 5 cch x l nhng tnh hung nh vy.)

Exercises
To illustrate exactly when an object is constructed and destructed when returned from a function, create a class called who. Have whos constructor take one character argument that will be used to identify an object. Have the constructor display a message similar to this when constructing an object. minh ha xc thc khi mt i tng c to hay hy khi tr v t mt hm, to ra mt lp mang tn who. Hm to ly mt i s kiu k t m s dng nhn dng i tng. Hm hy hin th tin nhn ging nh trn khi to mt i tng Constructing who #x

173

Where x is the identifying character associated with each object. When an object is destroyed, have a message similar to this displayed: Khi x l k t nhn dng kt hp vi mi i tng. Khi mt i tng b hy, xut hin mt tin nhn ging nh sau: Destroying who #x Where, again, x is the indentifying character. Finally, create a function called make_who( ) that returns a who object. Give each object a unique name. Note the output displayed by the program. Ni, ln na, x l k t nhn dng. Cui cng, to ra hm mang tn make_who( ) tr v gi tr ca i tng who. Mi i tng mt tn c nht. Ghi chp nhng g hin ra bi chng trnh. Other than the incorrect freeing of dynamically allocated memory, think of a situation in which it would be improper to return an object from a function.

Thng qua vic sai lm trong gii phng b nh cp pht ng, hy ngh v tnh hung m n s khng ph hp tr mt i tung t hm.

1.4. AN INTRODUCTION TO FRIEND FUNCTIONS Tng quan v hm friend:


There will be times when you want a function to have access to the private members of a class without that function actually being a member of that class. Toward this end, C++ supports friend function. A friend is not a member of a class but still has access to its private elements. S c lc bn mun c mt hm truy cp vo cc thnh phn ring ca mt lp m hm khng phi l thnh vin ca lp . Nhm mc nh ny, C++ a ra cc hm friend. Mt hm friend khng phi l thnh vin ca mt lp nhng c th truy cp cc thnh vin ca lp . Two reasons that friend functions are useful have to do with operator overloading and the creation of certain types of I/O functions. You will have to wait until later to see these uses of a friend in action. However, a third reason for friend functions is that there will be times when you want one function to

174

have access to the private members of two or more different classes. It is this use that is examined here. C hai l do m cc hm friend rt hu ch l khi to ra cc kiu hm nhp/ xut no hoc khi lm vic vi s np chng ton t. Bn s thy r hn cng dng ca cc hm friend trong cc chng sau. Tuy nhin l do th ba i vi cc hm friend l s c lc bn cn mt hm truy cp n cc thnh vin ring t ca hai hay nhiu lp khc nhau. Vn ny s c trnh by y.

A friend function is define as a regular, nomember function. However, inside the class declaration for which it will be a friend, its prototype is also included, prefaced by the keyword friend. To understand how this works, examine this short program: Mt hm friend c nh ngha nh mt hm bnh thng, khng friend. Tuy nhin, bn trong khai bo ca lp, hm s l mt friend khi nguyn mu ca hm m u bng t kha friend. hiu phng thc hot ng hy xt chng trnh ngn sau: // An example of a friend function. #include <iostream> using namespace std ;

class myclass { int n, d ; public: myclass ( int i, int j) { n = i ; d = j ; }

175

// declare a friend of myclass friend int isfactor (myclass ob) ; };

/* Here is friend function definition. It returns true if d is a factor of n. Notice that the keyword friend is not used in the definition of isfactor( ). */ int isfactor (myclass ob) { if (!(ob.n % ob.d)) else } int main ( ) { myclass ob1 (10, 2), ob2 (13, 3) ; return 0; return 1;

if (isfactor (ob1) ) cout << 2 is a factor of 10\n ; else cout << 2 is not a factor of 10\n ;

if (isfactor (ob2) ) cout << 3 is a factor of 13\n ; else cout << 3 is not a factor of 13\n ;

176

return 0 ; }

In this example, myclass declares its constructor function and the friend isfactor( ) inside its class declaration. Because isfactor( ) is a friend of myclass, isfactor( ) has access to its private members. This is why, within isfactor( ), it is possible to directly refer to ob.n and ob.d. Trong v d ny, lp myclass khi bo hm dng v hm friend isfactor( ) ca n bn trong khai bo lp. V isfactor( ) l mt hm friend ca myclass nn isfactor( ) c th truy cp n cc thnh phn ring ca lp myclass. y l l do ti sao trong isfactor() c th tham chiu trc tip n ob.n v ob.d.

It is important to understand that a friend function, is not a member of the class for which it is a friend, Thus, it is not possible to call a friend function by using an object name and a class member access operator ( a dot or an arrow). For example, give the preceding example, this statement is wrong: iu quan trng hiu l mt hm friend khng phi l thnh vin ca lp cha hm . Do , khng th gi hm friend bng cch dng tn i tng v ton t truy cp thnh vin ( du chm hoc mi tn). Chng hn vi v d trn y, cu lnh sau l sai : ob1.isfactor(); //wrong; isfactor ( ) is not a member function Instead, friends are called just like regular functions. Cc hm friend c gi nh cc hm bnh thng. Although a friend function has knowledge of the private elements of the class for which it is a friend, it can only access them through an object of the class. That is, unlike a member function of myclass, which can refer to n or d directly, a friend can access these variables only in conjunction with an object that is declared within or passed to the friend function. Mc d, hm friend nhn bit cc phn t ring ca lp m n l friend, nhng hm friend ch c th truy cp cc phn t ring ny qua mt i tng ca lp. 177

Ngha l, khng ging nh hm thnh vin ca lp myclass, c th tham chiu trc tip n n v d, mt friend c th truy xut cc bin ny thng qua kt ni vi mt i tng c khai bo bn trong hoc c truyn n cho hm friend.

Note:
The preceding paragraph brings up an important side issue. When a member function refers to a private element, it does so directly because a member function is excuted only in conjunction with an object of that class. Thus, when a member function refer to a private element, the compiler knows which object that private element belongs to by the object that is linked to the function when that member function is called. However, a friend function is not linked to any object. It simly is granted access to the private elements of a class. Thus, inside the friend function, it is meaningless to refer to a private member without reference to a specific object.

CH :
on trn y a n mt vn quan trng. Khi mt hm thnh vin tham chiu n mt phn t ring, th n khng tham chiu trc tip v hm thnh vin ch c thi hnh khi lin kt vi mt i tng ca lp . Do , khi mt hm thnh vin tam chiu n mt phn t ring, trnh bin dch bit i tng no m phn t ring thuc v do i tng c kt ni vi hm khi hm thnh vin c gi. Tuy nhin mt hm friend khng c kt ni vi bt k i tng no. Ta ch c truy cp n cc thnh phn ring ca mt lp. Do . bn trong hm friend, s v ngha khi tham chiu n mt thnh vin ring m khng c tham chiu n mt i tng c th. Because friends are not members of a class, they will typically be passed one or more objects of the class for which they are friends. This is the case with isfactor( ). It is passed an object of myclass, it can access obs private elements. If isfactor( ) had not been made a friend of myclass, it would be able to access ob.d or ob.n since n and d are private members of myclass. Do cc hm friend khng phi l thnh vin ca lp, chng c th c truyn nhiu hay ch mt i tng ca lp m chng c nh ngha hot ng. y l trng hp ca hm isfactor( ). N c truyn n i tng ob ca lp myclass. Tuy nhin v isfactor( ) l mt hm friend ca lp myclass, nn n c th truy cp n cc phn t ring ca i tng ob. Nu isfactor( ) khng phi l hm friend ca lp myclass, th n khng th truy xut ob.n hoc ob.d v n v d l cc 178

thnh vin ring ca lp myclass.

Remember:
A friend function is not a member and can not be qualified by an object name. It must be called just like a normal function. A friend function is not inherited. That is, when a base class include a friend function, that friend function is not a friend of a derived class. One other important point about friend function is that a friend function can be friends with more than one class.

CN NH:
Mt hm friend khng phi l mt thnh vin ca mt lp v n khng th c xc nh bng tn ca i tng. N c gi ging nh cc hm bnh thng. Mt hm friend khng c k tha. Ngha l khi mt lp c s gm mt hm friend th hm friend ny khng phi l hm friend ca lp dn xut. Mt im quan trng khc v hm friend l mt hm friend c th l cc hm friend ca hn mt lp.

Example:
One common (and good) use of a friend function occurs when two different types of classes have some quantity is common that needs to be compared. For example, consider the following program, which creates a class called car and a class called truck, each containing, as a private variable, the speed of the vehicle it represents: Cch dng thng thng (v tt) ca hm friend xy ra khi hai kiu lp khc nhau c mt s i lng cn c so snh. V d, xt chng trnh sau y to ra lp car v lp truck. Mi lp c mt bin ring l tc ca xe: #include <iostream> using namespace std ;

class truck ; // a forward declaration

179

class car { int passengers ; int speed ; public : car (int p, int s) { passengers = p ; speed = s ; } friend int sp_greater ( car c, truck t) ; };

class truck { int weight ; int speed ; public : truck (int w, int s) { weight = w ; speed = s ; } friend int sp_greater (car c, truck t) ;

180

};

/* Return possitive if car speed faster than truck. Return 0 if speeds are the same. Return negative if car speed faster than truck. */ int sp_greater (car c, truck t) { return c.speed t.speed ; }

int main ( ) { int t ; car c1(6, 55), c2(2, 120) ; truck t1(10000, 55), t2(20000, 72) ;

cout << Comparing c1 and t1: \n ; t = sp_greater (c1, t1) ; if (t < 0) cout << Truck is faster. \n ; else if (t == 0) cout << Car and truck speed is the same. \n ; else

181

cout << Car is faster.\n ;

cout << Comparing c2 and t2: \n ; t = sp_greater (c2, t2) ; if (t < 0) cout << Truck is faster. \n ; else if (t == 0) cout << Car and truck speed is the same. \n ; else cout << Car is faster.\n ;

return 0 ; }

This program contain rhe function sp_greater( ), which is a friend function of both the car and truck classes. (As stated a function can be a friend of two or more classes). This function returns positive if the car object is going faster than the truck object, 0 if their speeds are the same, and negative if the truck is going faster. Chng trnh ny c hm sp_greater( ) l mt hm friend ca c hai lp car v truck. (Nh ni trn, mt hm c th l hm friend ca hai hoc nhiu lp). Hm ny tr v mt gi tr dng nu i tng thuc lp car chy nhanh hn i tng thuc lp truck, tr v 0 nu nu hai tc bng nhau v gi tr m nu i tng thuc lp truck nhanh hn. This program illustrates one important C++ syntax element: the forward declaration ( also called a forward reference). Because sp_greater( ) takes parameters of both the car and the the truck classes, it is logically impossible to declare both before including sp_greater ( ) in either. Therefore, there needs to be some way to tell the compiler about a class name without actually

182

declaring it. This is called a forward declaration. In C++, to tell the compiler that an identifier is the name of a class, use a line like this before the class name is first used: Chng trnh ny minh ha mt yu t c php quan trng trong C++: tham chiu hng ti, v hm sp_greater( ) nhn tham s ca c hai lp car v truck, v mt lun l khng th khai bo c hai trc khi a sp_greater( ) vo. Do cn c cch no bo cho trnh bin dch bit tn lp m thc s khng cn phi khai bo. iu ny c gi l tham chiu hng ti. Trong C++, bo cho trnh bin dch mt nh danh l tn ca lp, dng cch nh sau trc khi tn lp c s dng : class class-name ; For example, in the preceding program, the forward declaration is V d, trong chng trnh trc, tham chiu hng ti l: class truck ; Now truck can be used in the friend declaration of sp_greater( ) without generating a compile-time error. By gi truck c th c dng trong khai bo hm friend sp_greater( ) m khng gy ra li thi gian bin dch.

A function can be a member of one class and a friend of another. For example, here is the proceding example rewritten so that sp_greater() is a member of car and a friend of truck: Mt hm c th l thnh vin ca mt lp v l mt hm friend ca lp khc. Sau y l chng trnh trc c vit li hm sp_greater( ) l thnh vin ca lp car v l hm friend ca lp truck. #include <iostream> using namespace std ; class truck ; // a forward declaration class car { 183

int passengers ; int speed ; public : car (int p, int s) { passengers = p ; speed = s ; } int sp_greater (truck t) ; };

class truck { int weight ; int speed ; public : truck (int w, int s) { weight = w ; speed = s ; } // not new use of the scope resolution operator friend int car :: sp_greater (truck t) ; };

184

/* Return possitive if car speed faster than truck. Return 0 if speeds are the same. Return negative if car speed faster than truck. */ int car :: sp_greater (car c, truck t) { return speed t.speed ; }

int main ( ) { int t ; car c1(6, 55), c2(2, 120) ; truck t1(10000, 55), t2(20000, 72) ;

cout << Comparing c1 and t1: \n ; t = sp_greater (c1, t1) ; if (t < 0) cout << Truck is faster. \n ; else if (t == 0) cout << Car and truck speed is the same. \n ; else cout << Car is faster.\n ;

185

cout << Comparing c2 and t2: \n ; t = sp_greater (c2, t2) ; if (t < 0) cout << Truck is faster. \n ; else if (t == 0) cout << Car and truck speed is the same. \n ; else cout << Car is faster.\n ;

return 0 ; }

Notice the new use of the scope resolution operator as it occurs in the friend declaration within the truck class declaration. In this case, it is used to tell the compiler that the function sp_greater( ) is a member of the car class. Ch n cch dng mi ca ton t phn gii phm vi (Scope resolution operator) xy ra trong khai bo friend bn trong khai bo ca lp truck. Trong trng hp ny, n c dng bo cho trnh bin dch hm sp_greater( ) l mt thnh vin ca lp car. One easy way to remember how to use the scope resolution operator is that the class name followed by the scope resolution operator followed by the member name fully specifies a class member. Mt cch d dng nh cch s dng ton t phn gii phm vi l tn lp theo sau bi ton t phn gii phm vi ri n tn thnh vin ch r thnh vin ca lp. In fact, when refering to a member of a class, it is never wrong to fully specify its name. However, when an object is used to call a member function or access a member variable, the full name is redundant and seldom used. For example: Thc t, khi tham chiu n mt thnh vin ca lp s khng bao gi sai khi ch 186

r tn y . Tuy nhin, khi mt i tng c dng gi mt thnh vin hay truy cp mt bin thnh vin, tn y s tha v t c dng. V d: t = cl.sp_greater (t1) ; can be written using the (redundant) scope resolution operator and the class name car like this: c th c vit bng cch dng ton t phn gii v tn lp car nh sau: t = c1.car :: sp_greater (t1) ; However, since c1 is an object of type car, the compiler already knows that sp_greater( ) is a member of the car class, making the full class specification unnecessary. Tuy nhin, v c1 l mt i tng kiu car, trnh bin dch bit sp_greater( ) l mt thnh vin ca lp car nn vic ch r tn y l khng cn thit.

Exercise:
Imagine a situation in which two classes, called pr1 and pr2, show here, share one printer. Further, imagine that other parts of your program need to know when the printer is in use by an object of either of these two classes. Create a function called inuse( ) that return true when the printer is being used by either and false otherwise. Make this function a friend of both pr1 and pr2. Hy tng tng mt tnh hung trong c 2 lp pr1 v pr2 c trnh by di y s dng chung mt my in. Xa hn, hy tng tng cc phn ca chng trnh ca bn cn bit khi no th my in c dng ca mt i tng ca mt trong hai lp. Hy to hm inuse( ) tr v gi tr ng (true) khi my in ang c dng v gi tr sai (false) trong trng hp ngc li. Lm cho n tr thnh hm friend ca c hai lp pr1 v pr2. class pr1 { int printing ; // public:

187

pr1( ) { printing = 0 ; } void setprint(int status) { printing = status ; } //.. };

class pr2 { int printing ; public: pr2( ) { printing = 0 ; } void setprint(int status) { printing = status ; } //.. };

188

MASTERY SKILLS CHECK:


Before proceding, you should be able to answer the following question and perform the exercises. KIM TRA K NNG TIP THU: Trc khi tip tc, bn hy tr li cc cu hi v lm cc bi tp sau:

What single prerequisite must be met in order for one object to be assigned to another? Give the classfragment. cho mt i tng c gn vo mt i tng khc th trc ht cn c iu kin g? Cho on chng trnh sau: class samp { double *p ; public: samp(double d) { p = (double *) malloc( sizeof (double)) ; if (!p) *p = d ; } ~ samp ( ) { free (p) ; } exit(1) ; // allocation error

189

//. };

// samp ob1 (123, 09), ob2(0, 0) ; //... ob2 = ob1 ;

what problem is caused by the assignment of ob1 to ob2? iu g xy ra sau khi gn ob1 vo ob2? Give the class : 3. Cho lp sau : class planet { int moons ; double dist_from_sun ; double diameter ; double mass ; public: //. double get_miles ( ) { return dist_from_sun ; } }; // in miles

190

create a function called light( ) that takes as an argument an object of type planet and returns the number of seconds that it takes light from the sun to reach the planet. (assume that light travels at 186,000 miles per second and that dist_from_sun is specified in miles ). hy to hm light( ) nhn mt i tng thuc lp planet lm i s tr v mt s giy cn thit nh sng i t mt tri n hnh tinh. (Gi thit nh sng di chuyn 186.000 dm/giy v dis_from_sun c tnh bng dm). Can the address of an object be passed to a function as an argument? a ch mt i tng c th c truyn cho hm nh mt i s khng? Using the stack class, write a function called loadstack( ) that returns a stack that is already loaded with the letters of the alphabet (a-z). Assign this stack to another object in the calling routine and prove that it contains the alphabet. Be sure to change the stack size so it is large enough to hold the alphabet. Dng lp stack, hy vit hm loadstack( ) tr v mt ngn xp c np vo cc ch ci (a-z). Hy gn ngn xp ny cho mt i tng khc khi gi mt chng trnh ph v chng t rng n cha cc ch ci alphabet. Explain why you must be careful when passing objects to a function or returning objects from a function. Hy gii thch ti sao bn phi cn thn khi truyn cc i tng cho hm hoc tr v cc i tng t mt hm. What is a friend function? Hm friend l g?

COMMULATIVE SKILLS CHECK:


This section checks how well you have integrated the material in this chapter with that from earlier chapters.

KIM TRA K NNG TNG HP:


Phn ny kim tra xem bn c th kt hp chng ny vi cc chng trc nh th no.

191

Functions can be overloaded as long as the number oer type of their parameters differs. Overload loadstack( ) from Exercise 5 of the mastery Skills check so that it takes an integer, calledpper, as a parameter. In the overloaded version, if upper is 1, load the stack with the uppercase alphabet. Otherwise, load it with the lowercase alphabet. Cc hm c th c np chng. Hy np chng hm loadstack( ) trong bi tp 5 ca phn kim tra k nng tip thu cho n nhn mt s nguyn, gi l upper, lm tham s. Trong phin bn nh ngha chng, nu upper l 1, hy a vo ngn xp cc mu t in hoa. Ngc li a vo ngn xp cc mu t thng.

Using the strtype class shown in section 3.1. Example 3, add a friend function that takes as an argument a pointer to an object of type strtype and returns a pointer to the string pointed to by that object. (That is,have the function return p). Call this function get_string( ). Dng lp strype trong phn 3.1, v d 3, hy b sung mt hm friend nhn con tr v mt i tng kiu strype lm tham s v tr v con tr cho mt chui c tr bi i tng . (Ngha l cho hm tr v P). Gi hm ny l get_string( ). Experiment: When an object of a derived class, is the data associated with the base class also copied? To find out, use the following two class and write a program that demonstrates what happens. Thc hnh :Khi mt i tng ca lp dn xut c gn cho mt i tng khc ca cng lp dn xut th d liu kt hp vi lp c s c c sao chp khng? bit c, hy dng hai lp sau y v vit chng trnh cho bit nhng g xy ra. class base { int a ; public: void load_a( int n) { a = n ; } int get_a( ) { 192

return a ; }

};

class derived : public base { int b ; public : void load_b int n) { b = n ; } int get_b ) { return a ; } } ;

193

CHNG 4 ARRAYS, POITERS, AND REFERENCES - Mng, con tr v tham chiu


Chapter objectives
Mc lc 4.1 ARRAYS OF OBJECTS Mng cc i tng 4.2 USING POINTERS TO OBJECTS S dng con tr n i tng 4.3 THE THIS POINTER Con tr this 4.4 USING NEW AND DELETE S dng new vdelete 4.5 MORE ABOUT NEW AND DELETE M rng v new v delete 4.6 REFERENCES Tham chiu 4.7 PASSING REFERENCES TO OBJECTS Truyn tham chiu cho i tng 4.8 RETURING REFERENCES Tr v cc tham chiu 4.9 INDEPENDENT REFERENCE AND RESTRICTIONS Cc tham chiu c lp v hn ch.

194

This chapter examines several important issues involving arrays of objects and pointers to objects. It concludes with a discussion of one of C++s most important innovations: the reference.The reference is crucial to many C++ features, so a careful reading is advised. Chng ny xt n nhiu vn quan trng gm mng, cc i tng v con tr ti i tng. Phn kt trnh by v mt trong nhng im mi quan trng nht ca C++: Tham chiu. Tham chiu l rt quan trng i vi nhiu c im ca C++, v th bn nn c k.

C++ Review Skill check

C++ Kim tra k nng n


Before proceeding, you should be able to correctly answer the following questions and do the exercises. Trc khi bt u, bn nn tr li ng cc cu hi v lm cc bi tp sau: When one object is assigned to another, what precisely takes place? 1. Khi mt i tng c gn cho mt i tng khc, iu g xy ra? Can any troubles or side effects occur when one object is assigned to another> (Give an example). 2. C rc ri hoc tc dng ph no xy ra khi gn mt i tng cho mt i tng khc? (cho mt v d). When an object is passed as an argument to a function, a copy of that object is made. Is the copys constructor function called? Is its destructor called? 3. Khi mt i tng c truyn nh i s cho mt hm, bn sao ca i tng c thc hin. Hm to ca bn sao c c gi khng? Hm hy ca bn sao c c gi khng? By default, objects are passed to functions by value, which means that what occurs to the copy inside the function is not supposed to affect the argument used in the call. Can there be a violation of this principle? If so, give an example. 4. Theo mc nh, cc i tng truyn cho hm bng gi tr, ngha l nhng g xy ra cho bn sao bn trong hm c gi thit l khng c nh hng n i s c dng trong li gi C th c s vi phm nguyn l ny khng? Cho v d.

195

Given the following class, create a function called make_sum() that returns an object of type summation. Have this function prompt the user for a number and then construct an object having this value and return it to the calling procedure. Demonstrate that the function works. 5. Cho lp sau y, hy to hm make_sum() tr v mt i tng kiu summation. Hy lm cho hm ny nhc ngi s dng nhp mt s v sau to mt i tng c gi tr ny v tr n v cho th tc. Hy chng t hm hot ng. class summation { int num; long sum; // summation of num public: void set_sum(int n); void show_sum() { cout<<num<<" summed is "<< sum<<"\n"; } };

void summation::set_sum(int n) { int i; num = n;

sum = 0; for(i=1; i<=n;i++) sum+=i;

196

In the preceding question, the function set_num() was not defined in line withing the summation class declaration. Give a reason why this might be necessary for some compilers. 6. Trong bi trn, hm set_num() khng c nh ngha ni tuyn trong khai bo lp summation. Hy gii thch ti sao iu ny l cn thit cho mt s trnh bin dch. Given the following class, show how to add a friend function called isneg() that takes one parameter of type myclass and returns true if num is negative and false otherwisse. 7. Cho lp sau y, hy b sung mt hm friend isneg() nhn mt tham s loi myclass v tr v gi tr ng nu num l m v gi tr sai nu ngc li. class myclass { int num; public: myclass(int x) { num = x;} };

Can a friend funtion be friends with more than one class? 8. Mt hm friend c th l nhng hm friend ca nhiu hn mt lp c khng?

1.1. ARRAYS OF OBJECTS - MNG CC I TNG


As has been stated several times, object are varialbes and have the same capabilities and attributes as any other type of variable. Therefore, it is perfectly acceptable for objects to be arrayed. The syntax for declaring an array of objects is exactly like that used to declare an array of any other type of variable. Further, arrays of objects are accessed just like arrays of other

197

types of variables. Nh c ni nhiu ln, cc i tng l cc bin v c cng kh nng v thuc tnh nh bt k loi bin no. Do , cc i tng hon ton c th c xp thnh mng. C php khai bo mt mng cc i tng l hon ton ging nh c php c dng khai bo mng cc loi bin bt k. Hn na, cc mng ca cc i tng c truy cp nh cc mng ca cc loi bin khc.

V D(EXAMPLES):
Here is an example of an array of objects: y l mt v d v mng cc i tng #include <stdafx.h> #include <iostream> using namespace std;

class samp { int a; public: void set_a(int n) {a = n;} int get_a() {return a;} };

int main() { samp ob[4]; int i;

198

for(i=0;i<4;i++) ob[i].set_a(i);

for(i=0;i<4;i++) cout<<ob[i].get_a();

cout<<"\n";

return 0; } This program creates a four-element array of objects of type samp and then loads each elements a with a value between 0 and 3. Notice how member functions are called relative to each array element. The array name, in this case ob, is indexed; then the member access operator is applied, followed by the name of the member function to be called. Chng trnh ny to ra mng i tng c 4 phn t kiu samp v sau np vo a ca mi phn t mt gi tr gia 0 v 3. Ch cch gi cc hm thnh vin i vi mi phn t mng. Tn mng, trong trng hp ny l ob, c nh ch s: Sau mi ton t truy cp thnh vin c p dng, theo sau bi tn ca hm thnh vin c gi. If a class type includes a constructor, an array of objects can be initialized. For example, here ob is an initialized array: 1. Nu mt kiu lp gm mt hm to, th mi mng i tng c th c khi u. V d, y ob l mng c khi u // Inintialize an array #include <stdafx.h> #include <iostream> using namespace std;

class samp {

199

int a; public: samp(int n) { a = n;} int get_a() {return a;} }; int main() { samp ob[4]= {-1,-2,-3,-4}; int i;

for(i=0;i<4;i++) cout<<ob[i].get_a()<<" ";

cout<<"\n";

return 0; }

This program diplays -1 -2 -3 -4 on the screen. In this example, the values -1 through -4 are passed to the ob constructor function. Chng trnh ny hin th -1, -2, -3, -4 ra mn hnh. Trong v d ny, cc gi tr t -1 n -4 c truyn cho hm to ob. Actually, the syntax shown in the initialization list is shortthand for this longer form (first shown in Chapter 2): Thc s, c php trong danh sch khi u l dng ngn ca dn di hn nh sau (c trnh by trong chng hai): samp ob[4] ={ samp(-1),samp(-2),samp(-3),samp(-4) };

200

However, the form used in the program is more common (although, as you will see, this form will work only with arrays whose constructors take only one argument). Tuy nhin, khi khi u mng mt chiu, dng c trnh by trong chng trnh trn l thng dng (mc d, nh bn s thy, dng ny ch lm vic vi cc mng c hm to ch nhn mt i s). You can also have multidimensional arrays of objects. For example, here is a program that creates a two-dimensional array of objects and initializes them: 2. Bn cng c mng i tng nhiu chiu. V d, y l chng trnh to mng i tng hai chiu v khi u chng: // creat a two-dimensional array of objects. #include "stdafx.h" #include <iostream> using namespace std; class samp { int a; public: samp(int n) { a = n;} int get_a() { return a; } }; int main() { samp ob[4][2] = { 1, 2, 3, 4, 5, 6, 7, 7}; int i;

for(i=0;i<4;i++) { 201

cout<<ob[i][0].get_a()<<" "; cout<<ob[i][1].get_a()<<"\n"; }

cout<<"\n"; return 0; }

This program displays Chng trnh ny hin th 1 3 5 7 2 4 6 8

As you know, a constructor can take more than one argument. When initializing an array of objects whose constructor takes more than one argument, you must use the alternative form of initialization mentioned earlier. Lets begin with an example: 3. Nh bn bit, mt hm to c th nhn nhiu i s. Khi khi u mt mng i tng c hm to nhn nhiu i s, bn phi dng dng khi u khc ni trc y. Hy bt u vi mt v d: #include <stdafx.h> #include <iostream> using namespace std; class samp { int a,b;

202

public: samp( int n,int m) { a = n; b = m;} int get_a() { return a;} int get_b() { return b;} }; int main() { samp ob[4][2] ={ samp(1,2),samp(3,4), samp(5,6), samp(7,8), samp(9,10), samp(11,12), samp(15,16) }; int i;

samp(13,14),

for(i=0;i<4;i++) { cout<<ob[i][0].get_a()<<" "; cout<<ob[i][0].get_b()<<"\n"; cout<<ob[i][1].get_a()<<" "; cout<<ob[i][1].get_b()<<"\n"; } cout<<"\n"; return 0; } In this example, samps constructor takes two arguments. Here, the array ob is declared and initialized in main() by using direct calls to samps constructor. This is necessary because the formal C++ syntax allows only one argument at a time in a comma-separated list. There is no way, for example, to specify two (or more) arguments per entry in the list. Therefore, when you initialize arrays

203

of objects that have constructors that take more than one argument, you must use the long form initialization syntax rather than the shorthand form. Trong v d ny, ham to ca samp nhn hai i s. y, mng ob c khai bo v c khi u trong main() bng cch gi trc tip hm to ca samp. iu ny l cn thit do c php chnh thc ca C++ cho php ch mt i s mt ln trong danh sch phn cch nhau bng du phy. Chng hn, khng c cch no ch r hai (hay nhiu) i s cho mi mc nhp trong danh sch. Do d khi bn khi u cc mng i tng c cc hm to nhn hn mt i s, bn phi dng c php khi u dng di hn l dng ngn. Note: you can always use the long form of initialization even if the object takes only one argument. Its just that the short form is more convenient in this case. Ch :Bn c th lun lun s dng dng di khi u ngay c khi i tng ch nhn mt i s. Nhng chnh dng ngn li thun tin hn trong trng hp ny. The preceding program displays Chng trnh ny hin th: 1 3 5 7 9 11 13 15 2 4 6 8 10 12 14 16

EXERCISES

BI TP
Using the following class declaration, create a ten-element array and initialize the bchb element with the values A through J. Demonstrate that the array does, 204

indeed, contain these values. Dng khai bo lp sau y to mng 10 phn t v khi u phn t ch vi cc gi tr t A n J. Hy chng t mng cha cc gi tr ny. #include <iostream> using namespace std;

class letters { char ch; public: letters (char c) { ch = c;} char get_ch() { return ch; } };

Using the following class declaration, create a ten-element array, initialize num to the values 1 through 10, and initialize sqr to nums square 2. Dng khai bo lp sau y to mt mng 10 phn t, hy khi u num vi cc gi tr t 1 n 10 v hy khi u spr i vi bnh phng ca num. #include <iostream> using namespace std;

class squares { int num, sqr; public:

205

squares (int a, int b) { num = a; sqr = b;} void show() { cout<< num<<' '<<sqr<<"\n";} };

Change the initialization in Exercise 1 so it uses the long form. (That is, invoke letters constructor explicitly in the initialization list). 3. Hy thay i cch khi u ob trong bi tp 1 bng cch dng dng di. (Ngha l nh n hm to ca ob trong danh sch khi u.

1.2. USING POINTERS TO OBJECTS S dng con tr


i tng:
As discussed in Chapter 2, objects can be accessed via pointers. As you know, when a pointer to an object is used, the objects members are referenced using the arrow (->) operator instead of the dot (.) operator. Nh tho lun trong chng 2, cc i tng c th c truy cp thng qua con tr. Nh bn bit khi s dng con tr ti mt i tng, cc thnh vin ca i tng c tham chiu bng cch dng ton t mi tn (->) thay v dng ton t chm (.). Pointer arithmetic using an object pointer is the same as it is for any other data type: it is performed relative to the type of the object. For example, when an object pointer is incremented, it points to the next object. When an object pointer is decremented, it points to the previous object. S hc v con tr i vi mt i tng ging nh i vi mt kiu d liu khc: n c thc hin i vi i tng. Khi mt con tr i tng gim xung, n tr ti i tng ng trc.

EXAMPLE:
1. Here is an example of object pointer arithmetic:

206

y l v d v s hc con tr i tng: // pointers to objects #include <iostream> using namespace std ; class samp { int a, b ; public: samp (int n, int m) { a = n ; int get_a( ) int get_b( ) }; int main ( ) { samp ob [4] = {samp(1, 2), samp(3, 4), samp(5, 6), samp(7, 8)}; int i ; samp *p; p = ob; // get starting address of array {return a; {return b; } } b = m; }

for (I = 0; i < 4; i++) { cout << p -> get_a( ) << ;

cout << p -> get_b( ) << \n ; p++ ; } cout << \n ; 207 //advance to next object

return 0; }

This is program displays: Chng trnh hin th nh sau: 1 2 3 4 5 6 7 8 As evidenced by the output, each time p is incremented, it points to the next object in the array. Nh c ch r trong d liu xut, mi ln p tng, n tr ti i tng k tip trong mng.

EXERCISE:
1. Rewrite Example 1 so it displays the contents of the ob array in reverse order. Hy vit li v d 1 n hin th ni dung ca mng ob theo th t ngc li. 2. Change Section 4.1, Example 3 so the two-dimensional array is accessed via a pointer. Hint: In C++, as in C, all arrays are stored contiguously, left to right, low to high. Hy thay i phn 4.1, v d cho mng hai chiu c truy cp thng qua con tr. Hng dn: Trong C++, cng ging nh trong C, tt c cc mng u c lu tr k nhau, t tri qua phi, t thp ln cao.

1.3. THE THIS POINTER Con tr This:


C++ contains a special pointer that is called this. this is a pointer that is automatically passed to any member function when it is called, and it is a 208

pointer to the object that generates the call. For example , given this statement, C++ c mt con tr c bit l this. y l mt con tr t ng chuyn n bt k hm thnh phn no m n c gi, v n l con tr cho i tng sinh ra li gi. V d, cho cu lnh sau, Ob.f1(); // assume that ob is an object

The function f1() is automatically passed a pointer to ob-which is the object that invokes the call. This pointer is referred to as this. It is important to understand that only member functions are passed a this pointer . For example, a friend does not have a this pointer. Hm f1() t ng a con tr ti ob-ni tng gi li gi. Con tr ny c dng l this. hiu ch c mt hm thnh phn c chuyn n con tr this l quan trng. V d, a firend khng c con tr this.

EXAMPLE
As you have seen, when a member function refers to another member of a class, it does so directly, without qualifying the member with either a class or an objiect specification. For example, examine this short program , which creates a simple inventory class:

V D:
1. Nh bn thy, khi m mt hm thnh phn dng cho nhng thnh phn khc ca lp, n lm trc tip, khng ni r thnh phn vi c lp v i tng xc nh. V d, xem xt on chng trnh ngn ny, ci no tao nn 1 lp kim k n gin: // Demonstrate the this pointer . #include <iostream> #include <cstring> using namespace std;

209

class inventory { char item[20]; double cost; int on_hand; public: inventory(char *I, double c, int o) { strcpy(item,i); cost=c; on_hand=o; } void show(); };

void inventory::show() { cout<<item; cout<<:S<<cost; cout<<On hand:<<on_hand<<\n; } int main() { inventory ob(wrench,4.95,4);

210

ob.show(); return 0; }

As you can see , within the constructor inventory() and the member function show(), the member variables item, cost, and on_hand are referred to directly. This is because a member function can be called only in conjunction with an objectdata is being referred to. However, there is an even more subtle explanation. When a member function is called , it is automatically passed a this pointer to the object that invoked the call. Thus, the preceding program could be rewritten as shown here. Nh bn thy, trong hm thit lp inventory() v hm thnh phn show(), thnh phn c gi tr item(), cost, v on_hand c dng trc tip. iu ny bi v mt hm thnh phn c th c gi cng chung vi thng tin ca i tng ang c dng. Tuy nhin, c nhiu hn s gii thch m h. Khi mt hm thnh phn c gi, n t ng chuyn con tr this n i tng gi li gi. V vy, chng trnh trc c th c vit li nh sau: // Demonstrate the this pointer #include <iostream> #include <cstring> using namespace std;

class inventory { char item[20]; double cost; int on_hand;

211

public: inventory(char*i, double c, int o) { strcpy(this->item,i); members this->cost=c; //through the this //pointer //accesss

this->on_hand=o; } void show(); };

void inventory::show() { cout<<this->item; //use this to access members

cout<<:S<<this->cost; cout<<On hand:<<this->on_hand<<\n; } int main() { inventory ob(wrench,4.95,4); ob.show(); return 0; }

Here the member variable are accessed explicitly through the this pointer. Thus, within show(), these two statements are equivalent:

212

y l thnh phn c gi tr c thng qua r rang con tr this. V vy, trong show(), hai cu lnh di y l tng ng: cost=123.23; this->cost=123.23; In fact, the first form is, loosely speaking, a shorthand for the second. While no C++ programmer would use the this pointer to access a class member as just shown, because the shorthand form is much easier, it is important to understand what the shorthand implies. The this pointer has several uses, including aiding in overloading operators. This use will be detailed in chapter . For now, the important thing to understand is that by default, all member functions are automatically passed a pointer to the invoking object. Tht s, dng u tin, hin th di dng, cch vit nhanh cho dng th hai. Trong khi khng c lp trnh vin C++ no dng con tr this truy cp mt thnh phn ca lp nh c trnh by, bi v dng vit nhanh d dng hn nhiu, tht l quan trng hiu cch vit ngn gi iu g. Con tr this c vi cch dng, bao gm tr gip ton t qu ti. Cch dng ny s chi tit hn trong chng ny. V lc ny, iu quan trng hiu l do mc nh, tt c hm thnh phn t ng chuyn con tr n li gi i tng.

EXERCISE
Given the following program, convert all appropriate references to class members to explicit this pointer references.

BI TP
1. Cho on chng trnh di y, chuyn i tt c s tham kho thch hp qua lp thnh phn lm r rng s tham kho con tr. #include <iostream> using namespace std;

213

class myclass { int a,b; public: myclass(int n, int m) { a=m; b=m; } int add() return a+b; void show(); };

void myclass::show() { int t; t=add(); //call member function

cout<<t<<\n; } int main() { myclass ob(10,14); ob.show(); return 0;

214

1.4. USING NEW AND DELETE new v delete:

Cch dng ton t

Up to now, when memory needed to be allocated, you have been using malloc(), and you have been freeing allocated memory by using free(). These are, of course, the standard C dynamic allocation functions. While these functions are available in C++, C++ provides a safer and more convenient way to allocate and free memory . In C++, you can allocate memory using new and release it using delete. These operators take these general forms: Cho n by gi, khi b nh cn c cpp pht, bn phi dng malloc(), v bn phi gii phng b nh c cp pht bng cch s dng free(). Nhng iu ny, d nhin, hm cp pht ng trong chun C. Trong khi nhng hm c gi tr trong C++, C++ cung cp cch an ton hn v tin li hn. cp pht v gii phng b nh. Trong C++, bn c th cp pht b nh s dng new v gii phng n bng delete. Nhng ton t ny mang nhng dng chung:

p-var=new type; delete p-var; Here type is the type specifier of the obj for which you want to allocate memory and p-var is a pointer to that type. new is an operator that returns a pointer to dynamically allocated memory that is large enough to hold an obj of type type. delete releases that memory when it is no longer needed. delete can be called only with a pointer previously allocated with new. If you call delete with an invalid pointer, the allocation system will be destroyed , possibly crashing your program. Loi ny l loai r rng ca i tng m bn mun cp pht b nh v p-var l mt con tr dng . new l mt ton t m tr v mt con tr cp pht b nh ng ln gi mt i tng ca loi type. delete gii phng b nh khi n khng cn thit na. delete c th c gi ch vi mt con tr cp pht trc y vi new. Nu bn gi delete vi mt con tr khng c gi tr, h thng cp pht s c ph hy, c th ph hng chng trnh ca bn.

215

If there is insufficient available memory to fill an allocation request, one of two actions will occur. Either new will return a null pointer or it will generate an exception. (Exceptions and exception handling are described later in this book; loosely, an exception is a run-time error that can be managed in a structured fashion.) In Standard C++, the default behavior of new is to generate an exception when it can not satisfy an allocation request. If this exception is not handled by your program, your program will be terminated. The trouble is that the precise action that new takes on failure has been changed several times over the past few years. Thus, it is possible that your compiler will not implement new as defined by Standar C++. Nu b nh c gi tr khng in vo yu cu cp pht, mt hay hai hnh ng s xy ra. Ngay c new s tr v mt con tr null hay l n s to ra mt ngoi l. (Nhng s ngoi l v s iu khin ngoi l c m t sau ny; mt cch di dng, mt s ngoi l l li run-time m c th qun l trong cu trc hnh dng.) Trong chun C++, cch hnh ng mc nh ca new l to mt ngoai l khi n khng th tha mn yu cu cp pht. Nu s ngoi l ny khng c iu khin bi chng trnh ca bn, chng trnh s b chm dt. Vn l hnh ng chnh xc m new gnh ly tht bi s c thay i vi ln trong vi nm qua. V vy, tht kh thi trnh bin dch s khng thc thi new nh c nh ngha bi chun C++. When C++ was first invented, new returned null on failure. Later this was changed such that new caused an exception on failure. Finally, it was decided that a new failure will generate an exception by default, but that a null pointer could be returned instead, as an option. Thus, new has been implemented differently at different times by compiler manufacturers. For example, at the time of this writting, Microsofts Visual C++ returns a null pointer when new fails. Borland C++ generates an exception. Although all compiler will eventually implement new in compliance with Standard C++, currently the only way to know the precise action of new on failure is to check your compilers documentation. Khi C++ ln u tin c pht minh, new tr v null trn tht bi. Sau ny n c thay i nh l new gy ra s ngoi l trn tht bi. Cui cng, n c quyt nh rng s tht bi new s sinh ra mt s ngoi l bi mc nh, nhng con tr null c th c tr v thay v, nh l mt ty chn. V vy, new thi hnh khc nhau ti thi im khc nhau bi nh sn xut trnh bin dch. V d, ti thi im ca bi vit ny, Microsofts Visual C++ tr v mt con tr null

216

when new tht bi. Borland C++ sinh ra s ngoi l. Tuy nhin tt c trnh bin dch s thm ch thi hnh new theo yu cu vi chun C++, hin ti cch duy nht bit hnh ng r rng ca new trn tht bi l kim tra ti liu ca trnh bin dch. Since there are two possible ways that new can indicate allocation failure, and since different compilers might do so differently, the code in this book will be in such a way that both contingencies are accommodated. All code in this book will test the pointer returned by new for null. This handles compilers that implement new by returning null on failure, while causing no harm for those compilers for which new throws an exception. If your compiler generates an exception when new fails, the program will simply be terminated. Later, when exception handling is described, new will be re-examined and you will learn how to better handle an allocation failure. You will also learn about an alternative form of new that always returns a null pointer when an error occurs. T khi c hai cch kh thi th new c th ch ra s cp pht tht bi, v t nhng trnh bin dch khc c th lm khc nhau, code trong cun sch ny s nh l mt cch m s kin ngu nhin c gip . Tt c code trong cun sch ny s th con tr tr v bi new cho null. S iu khin trnh bin dch ny thc thi new bi tr v null trn tht bi, trong khi khng gy ra s tn hi no cho nhng trnh bin dch m new a ra mt s ngoi l. Nu trnh bin dch ca bn sinh ra mt ngoi l th new tht bi, chng trnh s b dng li. Sau nay, khi s iu khin ngoi l c din t, new s c xem xt li v bn s hc cch lm th no iu khin tt hn s cp pht tht bi. Bn cng s hc v dng thay th ca new m lun lun tr v mt con tr null khi m mt li xut hin. One last point: none of the examples in this book should cause new to fail, since only a handful of bytes are being allocated by any single program. Althought new anh delete perform function similar to malloc() and free(), they have several advantages. First, new automatically allocates enough memory to hold an obj of the specified type. You do not need to use sizeof, for example, to compute the number of bytes required. This reduces possibility for error. Second, new automatically returns a pointer of the specified type. You do not need to use an explicit type cast the way you did when you allocated memory by using malloc() (see the following note). Third, both new and delete can be overloaded, enabling you to easily implement your own custom allocation system. Fourth, it is possible to initialize a dynamically allocated obj. Finally, you no longer need to include <cstdlib> with your programs.

217

Mt im cui cng: khng c v d no trong cun sch ny gy nn new tht bi, t khi ch c mt t bytes c cp pht bi mt chng trnh c lp no . Mc d new v delete cho thy hm ging nh malloc() v free(), n c vi thun li. Th nht, new t ng cp pht b nh gi mt i tng ca loi l thuyt. Bn khng cn phi dng sizeof, v d , tnh ton s bytes cn thit. iu ny gim bt kh nng c li. Th hai, new t ng tr v mt con tr ca loi l thuyt. Bn khng cn dng loi r rng to ra cch bn lm khi bn cp pht b nh bng cch s dng malloc() ( xem lu bn di ). Th ba , c new v delete c th b qu ti, cho php bn c th d dng thc thi h thng cp pht ty ca chnh bn. Th t, khi to mt i tng cp pht ng. Cui cng, bn c th khng cn cha <cstdlib> vi chng trnh ca bn na.

Note: In C, no type cast is required when you are assigning the return value of
malloc() to a pointer because the void* returned by malloc() is automatically converted into a pointer compatible with the type of pointer on the left side of the assignment. However, this is not the case in C++, which requires an explicit type cast when you use malloc(). The reason for this different is that it allows C++ to enforce more rigorous type checking. Now that new and delete have been introduced, they will be used instead of malloc() and free().

Lu : Trong C, khng c loi b cc no c cn khi m bn gn gi tr tr v


ca malloc() cho mt bin con tr bi v void* tr v bi malloc() c t ng chuyn i thnh con tr tng thch vi loi con tr bn tri ca s ch nh. Tuy nhin, iu ny khng phi l trng hp trong C++, m cn mt loi b cc r rng khi bn dng malloc(). L do cho s khc nhau ny l n cho php C++ lm cho loi kim tra nghim ngt hn. By gi new v delete c gii thiu, n s dng thay th cho malloc() v free().

EXAMPLES
As a short first example, this program allocates memory to hold an integer:

V D:
Nh l mt v d ngn, chng trnh ny cp pht b nh gi mt s nguyn: 218

// A simple example of new and delete. #include <iostream> using namespace std;

int main() { int *p; p=new int; if(!p) { cout<<Allocation error\n; return 1; } *p=1000; cout<<Here is integer at p:<<*p<<\n; delete p; //release memory return 0; } //allocate room for an integer

Create a class that contains a persons name and telephone number. Using new, dynamically allocate an obj of this class and put your name and phone number into these fields within this obj. What are the two ways that new might indicate an allocation failure?

To mt lp m cha tn v s in thoi ca mt ngi. Dng new, cp pht ng mt i tng ca lp ny v t tn v s in thoi ca n bn trong nhng vng ny trong i tng ny. 219

Cho bit hai cch m new c th biu th mt cp pht tht bi?

1.5.

MORE ABOUT NEW AND DELETE - M Rng ca new v delete:

This section discusses two additional features of new and delete. First, dynamically allocated objects can be given initial values. Second, dyanmically allocated arrays can be created. Phn ny tho lun thm 2 c im ca ton t new v delete. Trc ht, cc i tng c chia phn linh ng c th c khi to gi tr ban u.Th hai l, cc mng ng c th c to ra. You can give a dynamically allocated object an intial value by using this form of the new statement: Bn c th khi to cho i trng cp pht linh ng mt gi tr ban u bng cch s dng khai bo new nh sau: p-var =new type (intial-value); To dynamically allocate a one-dimensional array, use this form of new: cp pht linh ng cho 1 mng ng, s dng hnh thc sau ca new: p-var= new type[size]; After this statement has excuted, pvar will point to the start of an array of size element of the type specified. For various technical reasons, it is not possible to initialize an array that is dynamically allocated. Sau khi cu lnh c thi hnh, pvar s ch vo v tr phn t u tin ca mng theo nh ngha. V nhiu l do ca cng ngh, s khng ph hp khi khi to mt mng m c cp pht ng. To delete a dynamically allocated array, use this from of delete : xa mt mng cp pht ng, s dng ton t delete: Delete [] p-var;

220

This syntax causes the compiler to call the destructor function for each element in the array. It does not cause p-var to be free multiple times. P-var is still freed only once. Cu trc ny l nguyn nhn m trnh bin dch gi phng thc ph hy cho mi phn t trong mng. khng phi l nguyn nhn p-var c gii phng nhiu ln. p-var vn ch c gii phng 1 ln.

Note : For older compilers, you might need to spectify the size of the array that
you are deleting between the squre brackets of the delete statement. This was required by the original definition of C++. However, the size specification is not needed by modern compilers.

Ch : i vi cc trnh bin dch c, bn c th cn phi xc nh kch thc ca


mng m bn ang xa gia du ngoc vung ca cu lnh delete. l mt yu cu bi s nh ngha gc trong C++. Tuy nhin, i vi trnh bin dch mi th s c t kch thc khng cn phi c.

Examples:
This program allocates memory for an integer and initializes that memory: y l chng trnh cp pht b nh cho mt s integer v khi to gi tr cho a ch .

#include iostream.h using namespace std; int main() { int *p; p=new int(9); if(!p) { cout<< Allocation error\n; return 1;

221

} cout<< Here is integer at p :<<*<<\n; delete p; return 0; } As you should expect, this program displays the value 9, which is the initial value given to the memory point to by p. Nh bn mong i, chng trnh s hin th gi tr 9, l gi tr ca nh m c tr ti bi p. The following program passes initial values to a dynamically allocate object: Chng trnh di b qua gi tr khi to cp pht linh ng cho i tng #include iostream.h using namespace std; class samp { int i,j; public : samp(int a,int b) { i=a;j=b;} int get_product () {return i*j;} }; int main() { samp *p; p=new sam(6,5); if(!p) {

222

cout<< Allocation error\n; return 1; } cout<<Product is <<p->get_product()<<\n; delete p; return 0; } When the samp object is allocated, its constructor is automatically called and is passed the value 6 and 5. Khi m i tng samp c cp pht, phng thc khi to ca n t ng c gi v nhn c gi tr l 6 v 5. The following program allocates an array of integer. Chng trnh di cp pht cho mt mng s nguyn. #include iostream.h using namespace std; int main() { samp *p; p=new int[5]; if(!p) { cout<< Allocation error\n; return 1; } int I; for(i=0;i<5;i++) p[i]=i;

223

for(i=0;i<5;i++){ cout<< Here is integer at p[<<i<<] :; cout<<p[i]<<\n; } delete []p; return 0; } The program displays the following: Here is integer at p[0] : 0 Here is integer at p[0] : 1 Here is integer at p[0] : 2 Here is integer at p[0] : 3 Here is integer at p[0] : 4

The following program creates a dynamic array of objects: Chng trnh khi to mt mng ng cc i tng. #include iostream.h using namespace std; class samp { int i,j; public : void set_if(int a,int b) { i=a;j=b;} int get_product () {return i*j;} };

224

int main() { samp *p; p=new sam[10]; int i; if(!p) { cout<< Allocation error\n; return 1; } for(i=0;i<10;i++) p[i].set_if(i,i); for(i=0;i<10;i++) { cout<<Product [ <<i<<]is ; cout<<p[i].get_product()<<\n; } delete []p; return 0; }

This program displays the following : Product [0] is :0 Product [0] is :1 Product [0] is :4 225

Product [0] is :9 Product [0] is :16 Product [0] is :25 Product [0] is :36 Product [0] is :49 Product [0] is :64 Product [0] is :81

The following version of the preceding program gives samp a destructor, and now when p is freed. Each elements destructor is called: Phin bn di ca chng trnh trn cung cp thm cho lp samp phng thc ph hy, v khi p c gii phng l mi ln thnh phn ph hy c gi. #include iostream.h using namespace std; class samp { int i,j; public : void set_if(int a,int b) { i=a;j=b;} ~samp(){ cout << destroying \n;} int get_product () {return i*j;} }; int main() { samp *p; p=new sam[10];

226

int i; if(!p) { cout<< Allocation error\n; return 1; } for(i=0;i<10;i++) p[i].set_if(i,i); for(i=0;i<10;i++) { cout<<Product <<i<<is ; cout<<p[i].get_product()<<\n; } delete []p; return 0; }

This program displays the following : Kt qu chng trnh nh sau: Product [0] is :0 Product [0] is :1 Product [0] is :4 Product [0] is :9 Product [0] is :16

227

Product [0] is :25 Product [0] is :36 Product [0] is :49 Product [0] is :64 Product [0] is :81 Destroying Destroying Destroying Destroying Destroying Destroying Destroying Destroying Destroying Destroying

As you can see, samps destructor is called ten times=once for each element in the array. Nh cc bn thy, phng thc ph hy ca samp c gi 10 ln cho mi mt phn t trong mng.

Exercises
Show how to convert the following code into its equivalent that uses new. Hy ch ra cch chuyn on code sau s dng ton t new. char *p; p=(char*)malloc(100); 228

// strcpy(p,this is the test);

Hint: A string is simply an array of characters. Ghi ch: Mt chui n gin l mt mng cc k t.

Using new, show how to allocate a double and give it an initial value of -123.0987 S dng ton t new, trnh by cch cp pht b nh cho mt s nguyn di v cho n mt gi tr ban u l -123.0987.

1.6. REFERENCES - Tham chiu:


C++ contains a feature that is related to the pointer: the reference. A referance is an implicit pointer that for all intents and purposes acts like another name for a variable. There are three ways that a reference can be used. First, a reference can pass to a function. Second, a reference can be returned by a function. Finally, an independent reference can be created, Each of these application of the reference is examined, beginning with reference parameters. C++ bao gm mt c im m lin quan n con tr : l tham chiu. Tham chiu l mt loi con tr n dng vi mc ch biu din mt tn khc cho bin. C 3 cch s dng tham chiu. Mt l, tham chiu co th l mt hm chc nng.Hai l, tham chiu c th c tr v bi hm. Cui cng, mt tham chiu c lp co th c tao. Mi ng dng ca tham chiu u c kim tra, bt u vi thng s ca tham bin. Without a doubt, the most important use if a refernce is as a parameter to a function. To help you understand what a reference parameter is and how it works, lets first start with a program that uses a pointer ( not a reference )as a parameter: Khng nghi ng, iu quan trng nht l s dng tham chiu nh l mt tham bin trong hm. gip bn hiu r tham chiu l nh th no v chng hot ng ra sao, chc ta bt u vi 1 chng trnh s dng con tr(khng phai l

229

tham chiu) nh l mt tham bin. #include iostream.h using namespace std; void f(int *n) int main() { int i=0; f(&i); cout<<Here is is new value :<<i<<\n; return 0; } void f(int*n) { *n=100; }

Here f() loads the value 100 into the integer pointed to by n. In this program, f() is called with the address of i in main(). Thus, after f() return, I contains the value 100. Hm f() y np gi tr 100 vo con tr s nguyn bi n. Trong chng trnh, f() c gi vi a ch ca i trong hm main(). This program demonstrates how a pointer is used as a parameter to manually create a call-by-reference parameter-passing mechanism. In a C program, this is the only way to achieve a call-by-reference. l chng trnh m t con tr hot ng nh l 1 tham bin nh th no to mt bin c gi bi tham chiu. Trong chng trnh C, l cch duy nht t c s gi theo tham kho.

230

However, IN C++, you can completely automate this process by using a reference parameter.To see how, lets rework the previous program. Here is a version that uses a reference parameter: Tuy nhin, trong C++, bn c th t hon thnh qu trnh ny bng cch s dng cc tham s truyn theo a ch. Lm th no thy r iu , hy tr li chng trnh pha trc. y l phin bn ca chng trnh m s dng tham chiu: #include <iostream> Using namespace std; void f(int &n) int main() { int i=0; f(i); cout<<Here is is new value :<<i<<\n; return 0; } void f(int &n) { n=100; }

Examine this program carefully. First, to declare a reference variable or parameter, you precede the variables name with the &. This is how n is declared as parameter to f(). Now that n is a reference. It is no longer necessary- or even legal- to apply the operator. Instead, each time n is used within f(), it is automatically treated as a pointer to the argument used to call f(). This means that the statement n=100; actually puts the value 100 into the variable used to call f(), which, in this case, is i. Further, when f() is called,

231

there is no need to precede the argument with the &. Instead, because f() is declared as taking a reference parameter, the address to the argument is automatically passed to f(). Kim tra chng trnh tht k. Trc tin, biu th mt tham chiu l bin s hay tham s, bn in pha trc tn ca bin s &. iu c ngha l n c nh ngha l mt tham s ca f(). By gi n l mt tham chiu. Khng cn thit hoc thm ch ng lut cp mt ton t. Thay vo , mi ln n c s dng trong f(), n s c t ng xem l mt con tr- i s gi f(). Cu lnh n=100 c ngha tht s l t gi tr 100 vo bin s s dng gi f(), trong trng hp ny l i. Hn na, khi hm f() c gi, khng cn c pha trc i s &. Thay vo , v hm f() c cng b nh l s dng cc tham s truyn theo a ch, a ch ca i s t ng c gi vo trong f(). To review, when you use a reference parameter, the compiler automatically passed the address of the variable used as the argument. There is no need no manually generate the address of the argument by preceding it with an & (in fact,it is not allowed). Further, within the function, the compiler automatically uses the variable pointer to by the reference parameter. There is no need to employ the * ( and again, it is not allowed). Thus, a reference parameter fully automates the call-by-reference parameter-passing mechanism. It is important to understand that you cannot change what a reference is pointing to . For example, if the statement n++: were put inside f() in the preceding program, n would still be pointing to I in main(). Instead of incrementing n, this statement increments the value of the variable being referenced(in this case, i). nhn li, khi bn s dng mt tham s truyn theo a ch, trnh bin dch s t ng in a ch ca bin s vo nh l i s. Khng cn phi to ra 1 a ch cho i s bng cch thm vo trc n &( s tht, n khng c cho php). Hn na, trong hm, trnh bin dch t ng s dng cc bin con tr nh l cc tham s truyn theo a ch. Cng khng cn thit s dng ton t *( n khng c php). Do , tham s truyn theo a ch t ng gi call-by-reference parameter-passing mechanism. Tht quan trng hiu rng bn khng th thay i ci g m tham chiu tr ti. V d nh cu lnh sau : n++; 232

c t vo trong hm f() trong chng trnh trc, n vn s ch vo i trong hm main(). Thay v tng mt tr s ca n, cu lnh ny tng gi tr ca bin s c tham chiu ti ( trong trng hp ny l i).

Reference parameters offer several advantages over their (more or less ) equivalent pointer alternatives. First, from a practical point of view, you no longer need to remember to pass the address of an argument. When a reference parameter is used, the address is automatically passed. Second, in the opinion of many programmers, reference parameters offer a cleaner, more elegant interface than the rather clumsy explicit pointer mechanism. Third, as you will see in the next section, when an object is passed to a function as a reference, no copy is made. This is one way to eliminate the troubles associated with the copy of argument damaging something needed elsewhere in the program when its destructor function is called. Tham s truyn theo a ch c mt vi im thun tin hn con tr. Trc ht, trn quan im thc hnh, bn khng cn phi nh a ch ca i s. Khi m tham s truyn theo a ch c dng, a ch s t ng c thng qua. Th hai, theo kin ca nhiu lp trnh vin, tham s truyn theo a ch th c giao din tao nh r rng hn. l mt cch loi b nhng phc tp kt hp vi nhng i s ging nhau lm hi nhng th cn khi m trong chng trnh gi phng thc ph hy c gi.

Example
The classic example of passing arguments by reference is a function that exchanges the value of the two argument with which it is called. Here is an example called swapargs() that uses references to swap its two integer arguments. Mt v d in hnh ca vic b qua i s bng cch s dng tham chiu l hm hon v 2 gi tr ca i s mi khi n c gi. y l v d gi hm swaparg() m s dng tham chiu hon v 2 gi tr s nguyn.

#include <iostream> using namespace std; void swapargs( int &x, int &y); 233

int main() { int i,j; i=10; j=19; cout<<"i: cout<<"j: "<<i<<";"; "<<j<<"\n";

swapargs(i,j); cout<<"After swap :"; cout<<"i: cout<<"j: return 0; } void swapargs(int &x,int &y) { int t; t=x; x=y; y=t; } "<<i<<";"; "<<"\n";

If swapargs() had been written using pointer instead of references, it would have looked like this: Nu hm swapargs() c vit bng con tr thay cho tham chiu, th n s nh th ny:

234

void swapargs(int *x,int *y) { int t; t=*x; *x=*y; *y=t; }

As you can see, by using the reference version of swapargs(), the need for the *operator is eliminated. Nh bn thy, bng cch s dng tham chiu, loi b c ton t *. 2. Here is program that uses the round() function to round a double value. The value to kbe rounded is passed by reference. y l chng trnh s dng hm round() lm trn gi tr double. Gi tr c lm trn c b qua bi tham chiu #include <iostream> #include <cmath> using namespace std; void round(double &num); int main() { double i=100.4; cout<<i<<"rounded is "; round(i); cout<<i<<"\n"; i=10.9;

235

cout<<i<<"rounded is "; round(i); cout<<i<<"\n"; return 0; } void round(double&num) { double frac; double val; frac=modf(num,&val); if(frac<0.5) num=val; else num=val+1.0; } round() uses a relatively obscure standard library function called modf() to decompose a number into its whole number and fractional parts. The fractional part is returned; the whole number is put into the variable pointed to by modf()s second parameter. Hm round() s dng th vin chun ca hm gi lnh modf() phn tch s thnh phn nguyn v phn thp phn. Phn thp phn c tr v, phn nguyn c t vo bin s tr vo bi tham s th 2 ca hm modf().

Exercises
Write a function called neg() that reverses the sign of its integer parameter. Write the function two ways-first by using a pointer parameter and then by using a reference parameter. Include a short program to demonstrate their operation. Vit hm neg() o du ca tham s nguyn. Vit hm bng hai cch- s dng con tr v s dng tham s truyn theo a ch.Bao gm c chng trnh nh m t php thc hin . 236

What is wrong with the following program? Ch ra im sai? #include <iostream> using namespace std; void triple(double &num); int main() { Double d=7.0; triple(&d); cout<<i; return 0; } void triple(double &num) { num=3*num; } 3. Give some advantages of reference parameters. im thun li ca tham s truyn theo a ch.

1.7. PASSING REFERENCES TO OBJECTS Truyn tham chiu cho i tng:


As you learned in Chapter 3, when an object is passed to a function by use of the default call-by-value parameter-pasing mechnism, a copy of that object is made although the parameters constructor function is not called. Its destructor function is called when the function returns. As you should recall, this can cause serious problems in some instances-when the destructor frees dynamic memory, for example.

237

Khi bn tm hiu chng 3, khi m mt i tng c chuyn thnh hm bng cch s dng phng thc gi hm tham s truyn theo a ch mc nh, mt bn sao ca i tng c to ra mc d phng thc thit lp ca tham s khng c gi. Phng thc ph hy ca n c gi khi hm tr v. Khi m bn gi li hm, l nguyn nhn gy ra li nghim trng trong mt vi trng hp- khi m phng thc ph hy gii phng b nh ng. One solution to this problem is to pass an object by reference.(The other solution involves the use of copy constructors, which are discussed in Chapter 5).When you pass the object by reference, no copy is made, and therefore its destructor function is not called when the function returns. Remember, however, that changes made to the object inside the function affect the object used as the argument. Mt gii php gii quyt vn l chuyn i tng thnh tham chiu.( Mt gii php khc gii quyt vn l s dng bn sao ca phng thc ph hy, m chng ta tho lun trong chng 5.Khi bn chuyn 1 i tng bng tham chiu, vic to bn sao s khng c thc hin, thm ch phng thc ph hy ca n cng khng c gi khi hm tr v.Nn nh rng, vic thay i nh th lm i tng bn trong hm b nh hng bi i tng dng nh l mt i s. Note: It is critical to understand that a reference is not a pointer. Therefore, when an object is passed by reference, the member access operator remains the dot (.) not the arrow (->). Ch : Tht sai lm khi hiu rng tham chiu khng phi l con tr. Do , khi m i tng c chuyn sang tham chiu, cc thnh phn c truy cp bi ton t .. Ch khng phi l ton t ->.

Example
The following is an example that demonstrates the usefulness of passing an object by reference. First, here is a version of a program that passes an object of myclass by value to a function called f(): V d di y m t s hu ca vic chuyn i tng thnh tham chiu. Trc ht, y l phin bn ca chng trnh chuyn i tng ca lp myclass bi gi tr ca hm f() #include <iostream>

238

using namespace std; class myclass { int who; public: myclass(int n){ who=n; cout<<"Constructing "<<who<<"\n"; } ~myclass() { cout<<"Destructing "<<who<<"\n"; } int id(){return who;} }; void f(myclass o) { cout<<"Receive "<<o.id<<"\n"; } int main() { myclass x(1); f(x); return 0; }

239

This function displays the following: Kt qu hin th: Contructing 1 Receive 1 Destructing 1 Destructing 1

As you can see, the destructor function is called twice-first when the copy of object 1 is destroyed when f() terminates and again when the program finishes. Nh bn thy, phng thc ph hy c gi 2 ln trc khi bn sao ca i tng 1 c ph hy khi m hm f() kt thc v khi chng trnh kt thc. However, if the program is changed so that f() uses a reference parameter, no copy is made and, therefore, no destructor is called when f() return: Tuy nhin, nu chng trnh c thay i hm f() dng tham s truyn theo a ch, s khng c bn sao c tao ra, v do , khng c phng thc ph hy c gi khi hm f() tr v.

#include <iostream> using namespace std; class myclass { int who; public: myclass(int n){ who=n; cout<<"Constructing "<<who<<"\n";

240

} ~myclass() { cout<<"Destructing "<<who<<"\n"; } int id(){return who;} }; void f(myclass &o) { cout<<"Receive "<<o.id()<<"\n"; } int main() { myclass x(1); f(x); return 0; }

This function displays the following: Kt qu hin th. Contructing 1 Receive 1 Destructing 1

Remember

241

when accessing members of an object by using a reference, use the dot operator; not the arrow. Nh rng khi m truy cp cc thnh phn ca i tng s dng tham chiu, s dng ton t . Ch khng phi l ton t mi tn->

Exercise
1. What is wrong with the following program ? Show how it can be fixed by using a reference parameter. Tm ch sai trong chng trnh? Ch ra cch khc phc s dng tham s truyn theo a ch. #include <iostream> #include <cstring> #include <cstdlib> using namespace std; class strtype{ char*p; public: strtype(char*s); ~strtype(){delete []p;} char *get(){return p;} }; strtype::strtype(char*s) { int l; l=strlen(s)-1; p=new char[1]; if(!p)

242

{ cout<<"Allocation is error\n"; exit(1); } strcpy(p,s); } void show(strtype x) { char*s; s=x.get(); cout<<s<<"\n"; } int main() { strtype a("Hello"), b("There"); show(a); show(b); return 0;
}

1.8. RETURNING REFERENCES - Tr v tham chiu:

243

A function can return a reference. As you will see in Chapter 6, returning a reference can be very useful when you are overloading certain types of operators. However, it also can be employed to allow a function to be used on the left side of an assignment statement. The effect of this is both powerful and startling. Mt hm c th tr v mt tham chiu. Nh bn s thy chng 6. vic tr v mt tham chiu c th rt hu dng khi m bn np chng vo mt s loi ton ton t no . Tuy nhin, n cng c th c tn dng cho php mt hm c s dng bn tri ca mt cu lnh gn. Tc ng ca n th rt ln v ng ch .

EXAMPLES: V D:
To begin, here is very simple program that contains a function that returns a reference: bt u, y l mt chng trnh n gin bao gm mt hm tr v mt tham chiu: // A simple example of a function returning a reference #include iostream.h Using namespace std; int &f(); //return a reference int x;

int main() { f()=100; //assign 100 to reference to returned by f() cout<<x<<\n;

244

return 0; } //return an int reference

int &f() { return x; //return a reference to x; } Here function f() is declared as returning a reference to an integer. Inside the body of function, the statement. y, hm f() c khai bo c chc nng l tr v mt tham chiu cho mt s nguyn. Bn trong thn hm c cu lnh: return x; does not return the value of the global variable x, but rather, it automatically return xs address (in the form of a reference). Thus, inside main(), the statement. m khng tr v mt gi tr ca bin ton cc x, nhng d nhin n s t ng tr v a ch ca x (di hnh thc l mt tham chiu). V vy, bn trong thn hm main(), cu lnh f()=100; To review, function f() returns a reference. Thus, when f() is used on the left side f the assignment statement, it is this reference, returned by f(), that is being assigned to. Since f() returns a reference to x (in this example), it is x that reviews the value 100. xem li, hm f() tr v mt tham chiu. Do , khi hm f() c s dng trong cu lnh gn, th tham chiu c tr v bi hm f() s c gn. K t khi hm f() tr v mt tham chiu cho x (trong v d ny), th x s nhn gi tr 100. 2. You must be careful when returning a reference that the object you refer to does not go out of scope. For example, consider this slight reworking of function f():

245

Bn phi thn trng khi tr v mt tham chiu rng i tng m bn ang cp n khng vt ra tm vc. V d, xem li cch hot ng ca hm f(): //return an int reference int &f() { int x; //x is now a local variable //returns a reference to x

return x; }

In this case, x is now local to f() and will do out of scope when f() returns. This effectively means that the reference returned by f() is useless. Trong trng hp ny, x by gi thuc v hm f() v s hot ng bn ngoi tm vc khi m f() tr v. Trn thc t, iu ny c ngha l tham chiu tr v bi f() l v ngha.

NOTE: Some C++ compilers will not allow you to return a reference to a local variable. However, this type of problem can manifest itself in the other ways. Such as when objects are allocated dynamically. One very good use of returning a reference is found when a bounded array type is created. As you know, in C and C++, no array boundary checking occurs. It is therefore possible to overflow or underflow an array. However, in C++, you can create an array class that performs automatic bounds checking. An array class contains two core functions-one that stores information into the array and one that retrieves information. These function can check, at run time, that the array boundaries are not overrun. Mt trong nhng li ch ca vic tr v mt tham chiu c pht hin khi m mt loi mng c gii hn c to. Nh bn bit, trong C v C++, khng c s gii hn bi ng bin ca mng. V th c th trn hoc l thiu ht mng. Tuy nhin, trong C++, bn c th to mt lp mng m c th t ng thc thi vic kim tra gii hn. Mt lp mng bao gm 2 hm li mt l lu tr thng tin bn trong mng, v mt l dng ly thng. Khi chy, nhng hm ny c th kim tra, l khng vt qua cc ng bin ca mng.

246

The following program implements a bounds-checking array for characters: Chng trnh sau y thc hin vic kim tra ng gii hn ca mng nhng k t: #include "stdafx.h" #include <iostream> #include<cstdlib> using namespace std; class array { private: int size; char *p; public: array(int num); ~array() {delete []p;} char &put(int i); char get (int i); };

array ::array(int num) { p=new char[num]; if(!p) { cout<<"Allocation error";

247

exit(1); } size=num; }

//put something into the array char &array::put (int i) { if(i < 0 || i<=size) { cout<<"Bonus error!!!\n"; exit (1); } return p[i];//return reference to p[i] }

//Get something from the array char array ::get(int i) { if(i<0 || i>=size) { cout <<"Bonus error !!!\n"; exit(1); } return p[i];//return character

248

int main() { array a(10); a.put(3)='X'; a.put(2)='R';

cout <<a.get(3)<<a.get(2); cout<<"\n";

//now genrate run-timr boundary error a.put(11) = '!';

return 0; } This example is a practical use of functions returning references, and you should exam it closely. Notice that the put() function returns a reference to the array element specified by parameter i. This reference can then be used on the left side of an assignment statement to store something in the array-if the index specified by i is not out of bounds. The reverse is get(), which returns the value stored at the specified index if that index is within arrange. This approach to maintaining an array is sometimes referred to as a safe array. (You will see a better way to create a safe array later on, in Chapter 6.) V d ny l mt tin ch thc dng ca hm tr v tham chiu, v bn nn xem xt n k lng. Ch rng hm put() tr v mt tham chiu cho phn t ca mng c ch nh bi i. Sau tham chiu ny c th c s dng bn tri ca mt lnh gn lu tr vi th trong mng Nu index c ch nh bi i th khng nm ngoi ng ranh gii. Tri li l hm get(), n tr v mt gi tr c. 249

One other thing to notice about the preceding program is that the array is allocated dynamically by the use of new. This allows arrays of differing lengths to be declared. As mentioned, the way that bounds checking is performed in this program is a practical application of C++. If you need to have array boundaries verified at run time, this is one way to do it. However, remember that bounds checking slows access to the array. Therefore, it is best to include bounds checking only when there is a real likelihood that an array boundary will be violated. Write a program that creates a two-by-three-dimensional safe array of integers. Demonstrate that works. Vit mt chng trnh to mt mng s nguyn an ton 2 chiu ? Chng t n hot ng. Is the following fragment valid? If not, why not? on chng trnh sau c hp l khng? Nu khng, ti sao khng? int &f() { } int *x; x=f();

1.9.

INDEPENDENT REFERENCES AND

RESTRICTIONS - THAM CHIU C LP V NHNG HN CH :


Although not commonly used, the independent reference is another type of reference that is available in C++. An independent reference is a reference variable that is all effects is simply another name for another variable. Because references cannot be assigned new values, an independent reference must be initialized when it is declared.

250

Mc d khng c thng xuyn s dng, nhng tham chiu c lp l mt loi khc ca tham chiu m c th c s dng trong C++. Mt tham chiu c lp l mt bin tham chiu m c nhng tc ng n gin i vi nhng tn bin khc nhau th cho bin s khc nhau. Bi v nhng tham chiu khng th c gn gi tr mi, mt tham chiu c lp phi c khi to ngay khi c khai bo. NOTE: because independent references are sometimes used, it is important that you know about them. However, most programmers feel that there is no need for them and that they can add confusion to a program. Further, independent references exist in C++ largely because there was no compelling reason to disallow them. But for the most part, their use should be avoided. Ch : Thnh thong tham chiu c lp c s dng, v vy bn cn phi bit v chng. Tuy nhin, hu ht lp trnh vin cm thy rng khng cn n chng v chng ch em li rc ri cho chng trnh. Hn th na, mt tham chiu c lp tn ti trong C++ nhiu hn bi v khng c bt c mt l do thuyt phc no khng tha nhn chng. Nhng thng thng nn trnh vic s dng chng. There are a number of restrictions that apply to all types of references. You can not reference another reference. You cannot obtain the address of a reference. You cannot create an arrays of references, and you cannot reference a bit-field. References must be initialized unless they are members of class, are return values, or are function parameters. C mt s cc hn ch khi p dng n tt c cc loi tham chiu. Bn khng th bit c tham chiu ny vi tham chiu khc. Bn khng th ly c a ch ca tham chiu c lp. Bn khng th to mt mng tham chiu, v bn khng th tham chiu n mt trng bit. Nhng tham chiu phi c khi to nu n khng l thnh phn ca mt lp, v tr v nhng gi tr, hoc l nhng hm tham s.

EXAMPLES: V d:
Here is a program that contains an independent reference: y l mt chng trnh cha mt tham chiu c lp:

#include "stdafx.h"

251

#include <iostream> using namespace std;

int main() { int j, k; int &i = j; // independent reference

j = 10;

cout << j << " " << i; // outputs 10 10

k = 121; i = k; address // copies k's value into j, not k's

cout << "\n" << j;

// outputs 121

return 0; } In this program, the independent reference ref serves as a different name for x. Form a practical point of view, x and ref are equivalent. Trong chng trnh ny, tham chiu c lp ref c i x nh l mt ci tn khc ca x. Hnh thnh nn mt quan im thc t tng quan l x v ref th tng ng nhau. An independent reference can refer to a constant. For example, this is valid:

252

Mt tham chiu c lp c th tham chiu n mt hng s. V d, y l hp l: const int & ref=10;

EXERCISE:
V d: One your own, try to think of a good use for an independent reference Thm na, tham chiu c lp cn c mt li ch nh, m bn c th thy t gi trong nhng chng trnh khc.

Skills check:
Nhng k nng kim tra
At this point, you should be able to perform the following exercises and answer the questions. Vo thi im ny, bn nn thc hnh mt s bi tp bn di v tr li cc cu hi sau.

Given the following class, create a two-by-five two-dimensional array and give each object in the array an initial value of your own choosing. Then display the contents of the array. Hy thay i phng php ca bn gii quyt vn trn, sao cho truy cp mng bng cch s dng mt con tr. class a_type { double a,b; public: a_type(double x, double y) { a=x;

253

b=y; } void show() (cout <<a<< <<b<<\n;}; };

Modify your solution to the preceding problem so it accesses the array by using a pointer. Hy thay i phng php ca bn gii quyt vn trn, sao cho truy cp mng bng cch s dng mt con tr. What is the this pointer? Con tr this l g? Show the general forms for new and delete. What are some advantages of using them instead of malloc() and free()? Hy ch ra nhng dng thng thng ca new v delete. Vi tin ch ca vic s dng chng thay v s dng malloc() v free() l g? What is a reference? What is one advantage of using a reference parameter? Tham chiu l g? Mt tin ch ca vic s dng tham chiu l g? Create a function called recip() that takes one double reference parameter. Have the function change the value of that parameter into its reciprocal. Write a program to demonstrate that it works. To mt hm c gi l recip() n gi mt tham chiu kiu double. Hy dng hm ny thay i gi tr ca tham chiu thnh s nghch o ca n. Vit mt chng trnh chng minh n hot ng.

CUMULATIVE SKILLS CHECK: K NNG TCH LY:


This section checks how well you have integrated material in this chapter with that from the preceding chapters. Phn ny kim tra cch m bn kt hp ti liu trong chng ny vi nhng chng trc.

254

Give a pointer to an object, what operator is used to access a member of that object? Cho mt con tr tr vo mt i tng, ton t no c s dng truy cp mt thnh phn ca i tng ? In chapter 2, a strtype class was created that dynamically allocated space for a string. Rework the strtype class (shown here for your convenience) so it uses new and delete. Trong chng 2, mt lp strtype c to cp ch trng cho mt chui. Hy xem xt li lp strype ( c ch ra y to cho ban s thun li ) v vy n s dng new v delete. #include<cstring> #include<iostream> #include<cstdlib> using namespace std;

class strtype { char *p; int len; public: strtype (char *ptr); ~strtype(); void show(); };

strtype::strtype(char*ptr) { len=strlen(ptr);

255

p=(char*)malloc(len+1); if(!p) { cout<<"Allocation error"; exit(1); } strcpy(p,ptr); }

strtype::~strtype() { cout<<"freeing"; free(p); }

void strtype::show() { cout <<p<<len; cout <<"\n"; } int main() { strtype s1("This is the first string"); strtype s2("This is the second string");

256

s1.show(); s2.show(); return 0; } On your own, ,reword so that is uses a refer Theo bn,..khi no cp n

CHAPTER 5 FUNCTION OVERLOADING Np chng hm


Chapter objectives 5.1 OVERLOADING CONSTRUCTOR FUNCTION Np chng Cc Hm To. 5.2 CREATING AND USING A COPY CONSTRUCTOR To V S Dng Hm Bn Sao. 5.3 THE OVERLOAD ANACHRONISM S Li Thi Ca T Kha overload.

257

5.4 USING DEFAULT ARGUMENTS S Dng Cc i S Mc nh. 5.5 OVERLOADING AND AMBIGUIT Np chng v Tnh Khng Chnh Xc nh. 5.6 FINDING THE ADDREES OF AN OVERLOADED FUNCTION SKILLS CHECK Tm a Ch Ca Mt Hm np chng.

In this chapter you will learn more about overloading functions. Although this topic was introduced early in this book, there are several further aspects of it that need to be covered. Among the topics included are how to overload constructor funcitons, how to create a copy constructor, how to give functions default arguments, and how to avoid ambiguity when overloading. Trong chng ny bn s hc v qu ti cc hm. Mc du ch ny c gii thiu trc trong cun sch ny, nhng vn cn nhiu kha cnh cn c trnh by. Trong s cc ch gm c cch qu ti cc hm to, cch to hm to bn sao (conpy constructor), cch cho mt hm cc i s mc nh v cch trnh tnh khng xc nh khi qu ti.

Review skills check (kim tra k nng n)


Before proceeding, you should be able to correctly answer the following questions and do the exercises. Trc khi bt u, bn nn tr li ng cc cu hi v lm cc bi tp sau y: What is a reference? Give two important uses. Tham chiu l g? Hy trnh by hai cch dng quan trng. Show how to allocate a float and an int by using new. Also, show how to free them by using delete. Trnh by cch cp pht mt float v mt int bng cch dng new. Cng vy, trnh by cch gii phng chng bng cch dng delete.

258

What is the general form of new that is used to initialize a dynamic variable? Give a concrete example. Trnh by dng tng qut ca new dng khi u mt bin ng. Cho v d c th. Given the following class, show how to initialize a ten-element array so that x has the valuses 1 through 10. Cho lp sau y, trnh by cch khi u mt mng 10 phn t cho x c gi tr t 1 n 10. class samp { int x; public: samp(int n) { x = n; } int getx() { return x; } };

Give one advantage of reference parameters. Give one disadvantage. Trnh by mt u im ca cc tham s tham chiu. Trnh by mt nhc im. Can dynamically allocated arrays be initialized? C th khi u mt mng c cp pht ng khng? Create a function called mag() using the following prototype that raises numb to the order of magnitude specified by order: Hy to hm c tn mag() bng cch dng nguyn mu sau y nng num ln cp c ln l order: void mag(long &num, long order); For example, if num is 4 and order is 2, when mag() returns, num will be 400. Demonstrate in a program that the function works.

259

V d nu num l 4v order l 2 th khi mag() tr v, num s l 400. Hy vit mt chng trnh hm hot ng.

5.1. OVERLOADING CONSTRUCTOR FUNCTIONS - QA TI CC HM TO:


It is possible-indeed, common-to overload a classs constructor function. (It is not possible to overload a destructor, however). There are three main reasons why you will want to overload a constructor function: to gain flexibility, to support arrays, and to creat copy constructors. The first two of these are discussed in this section. Copy constructors are discussed in the next section. Thng thng c th qu ti hm to ca mt lp. (Tuy nhin, khng th qu ti hm hy). C 3 l do chnh ti sao bn cn qu ti hm to: c tnh linh hot, h tr mng v to cc hm bn sao. Hai l do u tin c tho lun trong phn ny. Cc hm to bn sao c tho lun trong phn tip theo. One thing to keep in mind as you study the examples is that there must be a constructor function for each way that an object of a class will be created. If a program attempts to create an object for which no matching constructor is found, a compile-time error occurs. This is why overloaded constructor functions are so common to C++ programs. Mt iu cn nh khi bn nghing cu cc v d l hm to ca lp phi ph hp vi cch m i tng ca lp c khai bo. Nu khng c s ph hp, li thi gian bin dch s xy ra. y l l do ti sao cc hm to c qu ti l rt thng dng trong cc chng trnh C++.

EXAMPLES
V D
Perhaps the most frequent use of overloaded constructor functions is to provide the option of either giving an object an initialization or not giving it one. For example, in the following program, o1 is given an initial value, but o2 is not. If you remove the constructor that has the empty argument list, the program will not compile because there is no constructor that matches a noninitialized object of type samp. The reverse is also true: If you remove the parameterized constructor, the program will not compile because there is no match for an 260

initialized object. Both are needed for this program to compile correctly.

1. C l cch s dng thng xuyn nht i vi cc hm to c qu ti l hoc khi u mt i tng hoc khng khi u mt i tng. V d, trong chng trnh ny, o1 c cho mt gi tr u cn o2 th khng. Nu bn loi b hm to c danh sch i s rng th chng trnh s khng bin dch v khng c hm to ph hp vi i tng kiu samp khng c khi u. iu ngc li cng ng: Nu bn loi b hm to c tham s ha th chng trnh ss khng bin dch bi v khng c s ph hp vi i tng c khi u, c hai u cn cho chng trnh ny bin dich ng. #include <iostream> using namespace std;

class myclass { int x; public: // overload constructor two ways myclass () { x = 0; } //no initializer myclass (int n) { x = n ;} // initializer int getx () { return x; } };

int main() { myclass o1(10); // declare with initial value myclass o2; // declare without initializer

261

cout<<" o1: "<< o1.getx()<<'\n'; cout<<" o2: "<< o2.getx()<<'\n';

return 0; }

Another common reason constructor functions are overloaded is to allow both individual objects and arrays of objects to occur within a program. As you porbably know from your own programming experience, it is fairly common to initialize a single variable, but it is not as common to initialize an array. (Quite often array values are assigned using information known only when the program is executing). Thus, to allow noninitialized arrays of objects along with initialized objects, you must include a constructor that supports initialization and one that does not. 2. Mt l do thng dng khc i vi cc hm to c qu ti l cho cc i tng ring l ln cc mng i tng xy ra trong chng trnh. C l t kinh nghim lp trnh bn bit, rt thng dng khi u mt bin n, nhng khonog thng dng khi u mt mng. (Thng cc gi tr mng c gn bng cch dng thng tin c bit ch khi chng trnh ang thi hnh). Do , cho cc mng i tng cha c khi u i vi cc i tng c khi u, bn phi dng n hm to h tr s khi u v mt hm to khng h tr s khi u. For instance, assuming the class myclass from Example 1, both of these declarations are valid: V d, gi s vi lp myclass trong v d 1, c hai khai bo sau u ng:

myclass ob(10); myclass ob[10]; By providing both a parameterized and a parameterless constructor, your program allows the creation of objects that are either initialized or not as needed.

262

Bng cch dng hm to cho c s khi u v s khonog khi u, cc bin c th c khi u hoc khng khi u khi cn. V d, chng trnh ny khai bo hai mng c kiu myclass, mt mng c khi u v mt mng khng. Of course, once you have defined both parameterized and parameterless constructors you can use them to create initialized and noninitialized arrays. For example, the following program declares two arrays of myclass; one initialized and the other is not:

#include <iostream> using namespace std;

class myclass { int x; public: //overload constructor two days myclass() { x = 0; } //no initializer // initializer

myclass( int n ) { x = n; } int getx() { return x; } };

int main() { myclass initialized o1[10]; //declare array without

// declare with initializers

263

myclass o2[10] = {1,2,3,4,5,6,7,8,9,10};

int i;

for(i=0; i<10; i++) { cout<<" o1["<< i << "]: "<< o1[i].getx(); cout<<"\n"; cout<<" o2["<< i << "]: "<< o2[i].getx(); cout<<"\n"; } return 0; }

In this example, all elements of o1 are set to 0 by the constructor function. The elements of o2 are initialized as shown in the program. Trong v d ny, mi phn t ca o1 c hm to t zero. Cc phn t ca o2 c khi u nh trong chng trnh. Another reason for overloading constructor functions is to allow the programmer to select the most convenient method of initializing an object. To see how, first examine the next example, which creates a class that holds a calendar date. It overloads the date() constructor two ways. One form accepts the date as a character string. In the other form, the date is passed as three integers Mt l do khc qu ti cc hm to l cho php ngi lp trnh chn phng php thun li nht khi u cho mt i tng. thy r iu ny nh th no, trc ht chng ta hy xt v d tip theo to ra mt lp gi ngy thng theo lch. N qu ti hm to date() theo hai cch. Mt cch n nhn ngy thng nh mt chui k t. Cch khc, ngy thng c truyn nh 3 s nguyn. #include <cstdio> // included for sscanf()

264

using namespace std;

class date { int day, month, year; public: date( char *str); date( int m, int d, int y) { day = d; month = m; year = y; } void show() { cout<< month<<'/'<<day<<'/'; cout<< year<<'\n'; } };

date::date( char *str) { sscanf(str, &year); } "%d%*c%d%*c%d",&month, &day,

265

int main() { // construct date object using string date sdate(" 12/31/99");

// construct date object using integers date idate (12,31,99);

sdate.show(); idate.show(); return 0; }

The advantage of overloading the date() constructor, as shown in this program, is that you are free to use whichever version most conveniently fits the situation in which it is being used. For example, if a date object is being created from user input, the string version is the easiest to use. However, if the date object is being constructed through some sort of internal computation, the three-integer parameter version probably makes more sense. u im ca vic qu ti hm to date(), nh c trnh by trong chng trnh, l bn t do s dng phin bn no thun li nht ph hp vi tnh hung m n c s dng. V d, nu i tng date c to ra t d liu nhp ca ngi s dng th s dng phin bn chui l thun li nht. Tuy nhin nu i tng date c to thng qua mt loi tnh ton bn trong th s dng phin bn tham s 3 s nguyn c l s hay hn. Although it is possible to overload a constructor as many times as you want, doing so excessively has a destructuring effect on the class. From a stylistic point of view, it is best to overload a constructor to accommodate only those situations that are likely to occur frequently. For example, overloading date() a third time so the date can be entered interms of accept an object of type time_t (a type that stores the system date and time) could be very valuable. (See the 266

mastery Skills Check exercises at the end of this chapter for an example that does just this). Mc d c th qu ti hm to nhiu ln nh ban mun, nhng nu thc hin qu ti qu nhiu ln c th to ra tc dng hy hoi trn lp. T quan im tu t tt nht l nn qu ti mt hm to ph hp vinhng tnh hung thng xy ra. V d, vic qu ti hm date() ln th ba cho ngy thng c a vo nh ba s nguyn bt phn th t c ngha hn. Tuy nhin, vic qu ti hm nhn mt i tng c kiu time_t (la kiu lu tr ngy gi h thng) c th rt c gi tr. (Xem cc bi tp trong phn kim tra k nng lnh hi cui chng ny). There is one other situation in which you will need to overload a classs constructor function: when a dynamic array of that class will be allocated. As you should recall from the preceding chapter, a dynamic array cannot be initialized. Thus, if the class contains a constructor that takes an initializer, you must include an overloaded version that takes no initializer. For example, here is a program that allocates an object array dynamically: C mt trng hp m bn cn qu ti mt hm to ca lp: khi mng ng ca lp c cp. Bn nh li trong chng trc, mt mng ng khng th c khi u. Do , nu lp c hm to nhn mt b khi u, bn phi a vo phin bn c qu ti khng nhn b khi u. V d, y l chng trnh cp pht ng mt mng i tng. #include <iostream> // included for sscanf() using namespace std;

class myclass { int x; public: // overload constructor two ways myclass() { x = 0;} //no initializer myclass( int n) { x = n;} //initializer int getx () { return x;} 267

void setx(int n) {x = n; } };

int main() { myclass *p; myclass ob(10); // initialize single variable

p here

new

myclass[10];//can't

use

initializers

if(!p) { cout<<" Allocation error\n"; return 1; }

int i; // intialize all elements to ob for( i=0; i<10; i++) { cout<<" p[ "<<i<<"]"<<p[i].getx(); cout<<'\n'; }

return 0; } 268

Without the overloaded version of myclass() that has no initializer, the new statement would have generated a compile-time error and the program would not have been compiled. Khng c phin bn c qu ti ca myclass() v myclass khng c b khi u th cu lnh new s sinh ra li thi gian bin dch v chng trnh s khng c bin dch.

EXERCISES BI TP
1. Given this partially defined class 1 Cho lp c xc nh ring phn nh sau: class strtype { char*p; int len; public: char *getstring() { return p;} int getlength() { return len;} }; Add two constructor functions. Have the first one take no parameters. Have this one allocate 255 bytes of memory (using new), initialize that memory as a null string, and give len a value of 255. Have the other constructor take two parameters. The first is the string to use for initialization and the other is the number of bytes to allocate. Have this version allocate the specifiex amount of memory and copy the string to that memory. Perform all necessary boundary checks and demonstrate that your constructors work by including a short program.

269

Hy b sung hai hm to. Hy cho hm th nht khng nhn tham s. Hy cho hm ny cp pht 255 byte b nh (bng cch dng newb), hy khi u b nh nh mt chui rng (null) v cho len gi tr 255. Hy cho hm to khc nhn hai tham s. Tham s th nht l chui dng khi u v tham s kia l s byte cp pht. Cho phin bn ny cp pht lng b nh c ch r v chp chui vo b nh. Thc hin vic kim tra gii hn bin cn thit v chng t hm to ca bn hot ng bng cch a n vo trong mt chng trnh ngn.

In Exercise 2 of Chapter 2, Section 2.1, you created a stopwatch emulation. Expand your solution so that the stopwatch class provides both a paramenterless constructor (as it does already) and an overloaded version that accepts the system time in the form returned by the standard function clock(). Demonstrate that your improvement works. . Trong bi tp 2, chng 2, phn 1, bn to ra s thi ua ca ng h bm gi. Hy m rng gii p ca bn cho lp stopwatch bao gm mt hm to khng tham s v mt phin bn c qu ti nhn gi h thng di dng c tr v bi hm chun clock(). Chng t rng chng trnh cn ci tin ca bn hot ng

On your own, think about ways in which an overloaded constructor function can be beneficial to your own programming tasks. Hy tm cch trong mt hm to c qu ti c th c li ch cho cc cng vic lp trnh ca bn.

5.2. CREATING AND USING A COPY CONSTRUCTOR TO V S DNG HM TO BN SAO:


One of the more important forms of an overloaded constructor is the copy constructor. As numerous examples from the preceding chapters have shown, problems can occur when an object is passed to or returned from a function. As you will learn in this section, one way to avoid these problems is to define a copy constructor. Mt trong nhng dng quan trng hn ca hm to qu ti l hm to bn sao (copy constructor). c nhiu v d c trnh by trong nhng chng trc, vn xy ra khi c mt i tng c truyn ti hay c tr v t mt hm. Nh bn s bit trong phn ny, cch trnh nhng vn ny l nh ngha mt 270

hm to bn sao, l kiu c bit ca hm to c qu ti. To begin, lets restate the problem that a copy constructor is designed to solve. When an object is passed to a function, a bitwise (i.e.,exact) copy of that object is made and given to the function parameter that receives the object. However, there are cases in which this identical copy is not desirable. For example, if the object contains a pointer to allocated memory, the copy will point to the same memory as does the original object. Therefore, if the copy makes a change to the contents of this memory, it will be changed for the original object too! Also, when the function terminates, the copy will be destroyed, causing its destructor to be called. This minght lead to undesired side effects that further affect the original object. bt u chng ta hy xt li vn m mt hm to bn sao c thit k gii quyt. Khi mt i tng c truyn cho mt hm, mt bn sao tng bit ca i tng c to ra v c truyn cho tham s ca hm nhn i tng. Tuy nhin, c nhng trng hp trong bn sao ng nht l khng nh mong mun. V d, nu i tng c con tr ti b nh c cp pht, th bn sao s tr ti cng b nh nh i tng gc lm. Do , nu bn sao to ra s thay i cho ni dung b nh th n cng s c thay i i vi i tng gc. Cng vy, khi mt hm kt thc, bn sao s b hy v hm hy ca n c gi. iu ny dn n nhng tc dng khng mong mun lm nh hng n i tng gc. A similar situation occurs when an object is returned by a function. The compiler will commonly generate a temporary object that holds a copy of the value returned by the function. (This is done automatically and is beyond you control). This temporary object goes out of scope temporary objects destructor to be called. However, if the destructor frees dynamically allocated memory), trouble will follow. Tnh trng tng t ny cng xy ra khi mt i tng c tr v t mt hm. Thng thng, trnh bin dch s to ra mt i tng tm gi bn sao ca gi tr do hm tr v. (Vic ny c thc hin mt cch t ng v bn khng iu khin c). i tng tm ny s ra khi phm vi mt khi gi tr c tr v cho th tc gi, khin hm hy ca i tng tm c gi. Tuy nhin, nu hm hy hy b th g cn cho th tc gi (v d, n gii phng b nh cp pht ng), th rc ri s xy ra. At the core of these problems is the fact that a bitwise copy of the object is being made. To prevent these problems, you, the programmer, need to define

271

precisely what occurs when a copy of an object is made so that you can avoid undesired side effects. The way you accomplish this is by creating a copy constructor. By defining a copy constructor, you can fully specify exactly what occurs when a copy of an object is made. Ct li ca vn ny l ch bn sao tng bit cu i tng c thc hin. ngn chn nhng vn ny, l ngi lp trnh, bn cn xc nh chnh xc nhng g xy ra khi bn sao ca mt i tng c thc hin cho bn trnh c nhng tc dng khng mong mun. Thc hin iu ny bng cch to ra mt hm to bn sao. Bng cch nh ngha hm to bn sao, bn c th hon ton ch r chnh xc nhng g xy ra khi bn sao ca mt i tng c thc hin.

It is important for you to understand that C++ defines two distinct types of situations in which the value of one object is given to another. The first situation is assignment. The second situation is initialization, which can occur three ways: iu quan trng l bn phi hiu rng C++ xc nh hai trng hp phn bit trong gi tr ca mt i tng c truyn cho i tng khc. Trng hp th nht l php gn. Trng hp th hai l s khi u c th xy ra theo 3 cch:

When an object is used to initialize another in a declaration statement. When an object is passed as a parameter to a function and When a temporary object is created for use as a return value by a function. Khi mt i tng c dng khi u mt i tng khc trong cu lnh khai bo. Khi mt i tng c truyn nh tham s cho hm v Khi mt i tng c to ra dng lm gi tr tr v bi mt hm.

The copy constructor only applies to initializations. It does not apply to assignments. Hm to ch p dng cho s khi u. N khng p dng cho php gn. 272

By default, when an initialization occurs, the compiler will automatically provide a bitwise copy. (That is, C++ automatically provides a default copy constructor that simply duplicates the object). However, it is possible to specify precisely how one object will initialize another by defining a copy constructor. Once defined, the copy constructor is called whenever an object is used to initialize another. Theo mc nh, khi mt s khi u xy ra, trnh bin dich s t ng cung cp bn sao tng bit. (Ngha l, C++ t ng cung cp mt hm to bn sao mc nh). Tuy nhin, c th ch r chnh xc cch m mt i tng s khi u mt i tng khc bng cch nh ngha hm to bn sao. Khi c nh ngha, hm to bn sao c gi bt c lc no m mt i tng c dng khi u mt i tng khc.

Remember: Copy constructor do not affect assignment operations


The most common form of copy constructor is shown here:

Cn nh: cc hm to bn sao khng c nh hng n cc php gn


Mi hm to bn sao c dng tng qut sau: classname(const classname&obj) { //body of constructor }

Here obj is a reference to an object that is being used to initialize another object. For example, assuming a class called myclass, and that y is an object of type myclass, the following statements would invoke the myclass copy constructor: y, obj l mt tham chiu ti mt i tng c dng khi u mt i

273

tng khc. V d, gi s lp c gi l myclass, v y l i tng cu myclass th cc cu lnh sau y s dng n hm to bn sao ca myclass.

myclass x = y; // y explicitly initializing x func1(y); y = func2(); // passed as a parameter

// receiving a returned object

In the first two case, a reference to y would be passed to the copy constructor.In the third, a reference to the object returned by func2() is passed to the copy constructor.

Trong hai trng hp u, mt tham chiu ti y s c truyn cho hm to bn sao. Trng hp th ba, mt tham chiu ti i tng c tr v bi func2() s c truyn cho hm to bn sao.

EXAMPLES CC V D
Here is an example that illustrates why an explicit copy constructor function is needed. This program creates a very limited sage integer array type thay prevents array boundaries from being overrun. Storage for each array is allocated using new, and a pointer to the memory is maintained withing each array object.

y l v d minh ha ti sao cn n mt hm to bn sao c th. Chng trnh ny to ra mt mng nguyn an ton c gii hn ngn chn s trn qua gii hn bin ca mng. Dng new cp pht b nh lu tr mi mng, v mt con tr ti b nh c duy tr trong mi i tng mng. /* This program creates a "safe" array class, since space for the array is dynamically allocated, a copy constructor is provided to allocate memory when one 274

array object is used to initialize another. */

#include <stdafx.h> #include <iostream> #include <cstdlib> using namespace std;

class array { int *p; int size; public: array( int sz) { //constructor p = new int[sz]; if(!p) exit(1); size = sz; cout<<" Using 'normal' constructor\n"; } array () {delete [] p;}

// copy constructor array(const array &a);

275

void put(int i, int j) { if(i>=0&& i<size) p[i]=j; } int get (int i) { return p[i]; } };

/* Copy constructor In the following, memory is allocated specifically for the copy, and the address of this memory is assigned to p, therefore, p is not pointing to th same dynamically allocated memory as the original object */

array::array (const array &a) { int i;

size = a.size; p = new int [ a, size]; // allocate memory for copy if(!p) exit(1); for(i=0;i<a.size;i++) p[i] = a.p[i]; // copy

276

contents cout<<" Using copy constructor\n"; }

int main() { array num(10); // this calls "normal" constructor int i;

//put some values into the array for(i=0;i<10;i++) num.put(i,i); // display num for(i=9;i>=0;i--) cout<<num.get(i); cout<<"\n";

// create another array and initialize with num array x = num; // this invokes copy constructor

// display x for(i=0;i<10;i++) cout<<x.get(i);

return 0; }

277

When num is used to initialize x, the copy constructor is called, memory for the new array is allocated and stored in x.p, and the contents of num are copied to xs array. In this way, x and num have arrays that have the same values, but each array is separate and distinct. (That is, num.p and x.p do no point to the same piece of memory). If the copy constructor had not been created, the bitwise initialization array x = num would have resulted in x and num sharing the same memory for their arrays! (That is, num.p and x.p would have, indeed, pointed to the same location). Khi num c dng khi u x, hm to bn sao c gi, b nh c cp pht cho mng mi v c lu tr trong x.p, v ni dung cu num c sao chp vo mng ca x. Trong cch ny, x v num c cc mng c cgn nhng gi tr, nhng mi mng l hon ton phn bit (num.p v x.p khng tr v cng mt phn b nh). Nu hm to khng c to ra th s khi ng tng bit array=num s dn n kt qu l x v num cng s dng chung b nh ging nhau i vi cc mng! (Ngha l num.p v x.p cng tr v mt v tr). The copy constructor is called only for initializations. For example, the following sequence does not call the copy constructor defined in the preceding program: Hm to bn sao c gi ch i vi s khi u. V d, nhng cu lnh sau y khng gi hm to c nh ngha trong chng trnh trc:

array a(10); array b(10); b=a; // does not call copy constructor

In this case, b=a performs the assignment operation. Trong trng hp ny, b=a thc hin php gn. To see how the copy constructor helps prevent some of the poblems associated with passing certain types of objects to functions, consider this (incorrect) program: thy r cch hm to bn sao gip ngn nga mt s vn lin quan vi vic

278

truyn cc kiu i tng no cho hm, hy xt chng trnh (sai) sau y: // This program has an error #include <stdafx.h> #include <iostream> #include <cstring> #include <cstdlib> using namespace std;

class strtype { char *p; public: strtype(char *s); ~strtype() {delete [] p;} char *get() {return p;} };

strtype::strtype( char *s) { int l;

l = strlen(s)+1;

p = new char[1]; if(!p)

279

{ cout<<" Allocation error\n"; exit(1); }

strcpy(p,s); }

void show(strtype x) { char *s;

s = x.get(); cout<<s<<"\n"; }

int main() { strtype a("Hello"), b("There");

show(a); show(b);

return 0; }

280

In this program, when a strtype object is passed to show(), a bitwise copy is made (since no copy constructor has been defined) and put into parameter x. Thus, when the function returns x, goes out of scope and is destroyed. This, of course, causes xs destructor to be called, which frees x.p. However, the memory being freed is the same memory that is still being used by the object used to call the function. This results in an error. Trong chng trnh ny khi i tng strtype c truyn cho show(), mt bn sao tng bit c thc hin (do khng c hm to bn sao c nh ngha) v c a vo tham s x. Do , khi hm tr v, x ra khi phm vi v b hu. D nhin, iu ny khin cho hm hy ca x c gi gii phng x.p. Tuy nhin, b nh c gii phng cng l b nh ang c s dng bi i tng dng gi hm. iu ny gy ra li. The solution to the preceding problem is to define a copy constructor for the strtype class that allocates memory for the copy when the copy is created. This approach is used by the following, corrected, program: Gii php gii quyt vn ny l nh ngha mt hm to bn sao cho lp strtype cp pht b nh cho bn sao khi bn sao c to ra. Phng php ny c dng trong chng trnh c hiu chnh sau y:

/* This program uses a copy constructor strtype objects to be passed to functions */ #include <stdafx.h> #include <iostream> #include <cstring> #include <cstdlib> using namespace std;

to

allow

class strtype

281

{ char *p; public: strtype( char*s); //constructor strtype(const strtype &c); //copy constructor ~strtype() { delete [] p;} // destructor char *get() {return p;} };

// "Normal" constructor strtype::strtype(char *s) { int l;

l = strlen(s)+1;

p = new char [1]; if(!p) { cout<<" Allocation error\n"; exit(1); }

strcpy(p,s); }

282

// copy constructor strtype::strtype(const strtype &o) { int l;

l = strlen(o.p)+1;

p = new char [1]; // allocate memory for new copy if(!p) { cout<<" Allocation error\n"; exit(1); }

strcpy(p,o.p); // copy string into copy }

void show(strtype x) { char *s;

s = x.get(); cout<<s<< "\n"; }

283

int main() { strtype a("Hello"), b("There");

show(a); show(b);

return 0; }

Now when show() terminates and x goes out of scope, the memory pointed to by x.p( which will be freed) is not the same as the memory still in use by the object passed to the function. By gi, khi show() kt thc v x ra khi phm vi, b nh c tr ti bi x.p (s c gii phng) khng ging nh b nh ang c s dng bi i tng c truyn cho hm.

EXERCISES BI TP
The copy constructor is also invoked when a function generates the temporary object that is used as the functions return value (for those functions that return objects). With this in mind, consider the following output: Hm to bn sao cng c dng n khi mt hm sinh ra i tng tm thi c dng nh gi tr tr v ca hm (i vi nhng hm tr v i tng). Vi ngha , xt d liu xut sau: Constructing normally Constructing normally

284

Constructing copy

This output was created by the following program. Explain why, and describe precisely what is occuring. D liu xut ny c to ra bi chng trnh sau y. Hy gii thch ti sao v m t chnh xc iu g xy ra.

#include <stdafx.h> #include <iostream> using namespace std;

class myclass { public: myclass(); myclass (const myclass &o); myclass f(); };

// Normal constructor myclass::myclass() { cout<<" Constructing normally\n"; }

// Copy constructor 285

myclass::myclass( const myclass &o) { cout<<" Constructing copy\n"; }

// Return an object. myclass myclass::f() { myclass temp;

return temp; }

int main() { myclass obj; obj = obj.f(); return 0; }

Explain what is wrong with the following program and then fix it. Hy gii thch iu g sai trong chng trnh sau v sau sa chng trnh li cho ng. // This program contains an error. #include <stdafx.h>

286

#include <iostream> #include <cstdlib> using namespace std;

class myclass { int *p; public: myclass (int i); ~myclass () {delete p;} friend int getval (myclass o); };

myclass::myclass(int i) { p = new int;

if(!p) { cout<<" Allocation error\n"; exit(1); } *p = i; }

287

int getval(myclass o) { return *o.p; //get value }

int main() { myclass a(1), b(2);

cout<<getval(a)<<" "<<getval(b); cout<<"\n"; cout<<getval(a)<<" "<<getval(b);

return 0; }

In your words, explain the purpose of a copy constructor and how it differs from a normal constructor Theo cch ca bn, hy gii thch mc ch ca hm to bn sao v n khc vi hm to thng thng th no.

5.3. THE OVERLOAD ANACHRONISM - S Li Thi Ca T kha Overload:


When C++ was first invented, the keywork overload was required to create an overloaded function. Although overload is now obsolete and no longer supported

288

by modern C++ compliers, youmay still see overload used in old program, so it is a good idea to understand how it was applied. Khi C++ mi c pht minh, t kha overload dng ta mt hm qu ti. Mc du overload khng cn thit na, nhng duy tr tnh tng thch ca C++ v c php ca s qu ti theo kiu c vn cn trnh bin dch C++ chp nhn. Khi bn trnh dng t kha ny,bn vn thy overload c dng trong chng trnh hin nay, hiu n c ng dng nh th no cng l mt kin hay. The general grom of overload is shown here. Dng tng qut ca overload nh sau: overload func-name; Where func-name is the name of the function to be overloaded. This statement must precede the overloaded function declarations. For example, this tells the compiler that you will be overloading a function called timer(): func-name l tn hm c qu ti. Cu lnh ny phi khai bo trc cc hm c qu ti. V d, cu lnh sau bo cho chng trnh bin dch bit l bn qu ti mt hm c tn timer() overload timer;

Remmber: overload is obsolete and no longer supported by modern C++ compilers. Cn Nh: v overload tr thnh li thi trong cc chng trnh C++ hin nay nn ngi ta thng trnh dng n.

5.4. USING DEFAULT ARGUMENTS - S dng cc i s mc nh:


There is feature of the C++ that is related to function overloading. This featureis called the defaukt argument, and it allows you to give a parameter a default value when no corresponding argurrment is specified when the function is called. As you will see, using default arguments is essentially a shorthand from

289

of function overloading. C mt c im ca C++ lin quan n s qu ti hm. c im ny ny gi l i s mc nh v cho php bn gn mt gi tr mc nh cho hm s khi khng c i s tng ng c ch r khi hm c gi. Nh bn s thy, s dng cc i s mc nh ch yu l dng ngn ca s qu ti hm. To give a parameter a default arguments, simply follow that parameter with an equal sign and the value you want it to defualt to if no corresponding argument is present when the function is called. For example, this function gives its two paramentes default values of 0: gn mt i s mc nh cho mt tham s th sau tham s c du bng v mt gi tr m bn mun mc nh nu khng c i tng ng khi hm c gi. V d, hm sau y cho hai gi tr mc nh 0 i vi cc tham s. void f(int a=0, int b=0) Notice that this syntax is smilar to variable initializantion. This function can now be called there difference ways. First, it can be called with both arguments specifief. Second, it can be called with only the first argument specified. In this case, b will default to 0. Finally , f() can be called with no arguments, causing both a and b to default to 0. That is, the following invoctions of f() are all valid: Ch rng c php ny tng t nh khi u mt bin.By gii hm c th gi theo 3 cch khac nhau. Th nht, n c gi vi hai i s c ch r. Th hai, n c gi vi i s th nht c ch r. Trong trng hp ny, b s mc nh v zero. Ngha l cc cu lnh sau u ng:

f(); f(10); f(10,99);

// a and b default to 0 // a is 10, b defaults to 0 // a is 10, b defaults to 99

In this example, it should be clear that there is no way to default a and specify b

290

Trong v d ny, r rng khng l khng c cch mc nh a v ch r b When you create a function that has one or more default arguments, those arguments must be specfied noly once: either in the functions prototype or in its definition if the definition precedes the functions first use. The defaults cannot be specified in both the prototype and the definition. This rule applies even if you simply duplicate the same defaults. Khi bn to hm c mt hay nhiu i s mc nh th nhng i s c ch r mt ln : hoc trong nh ngha hm hoc trong nguyn mu ca n ch khng c c hai. Qui tc ny c dng nay trong c khi bn nhn i cc ngm nh ging nhau. As you can probably guess, all default parameters must be to the right of any parameters that dont have defaults. Further, once you begin to define default parameters, you cannot specify any parameters that have the same defaults. C l bn cng on c,tt c cc tham s mc nh phi bn trong thn hm s khng c mc nh. Hn na, mt khi bn bt u nh ngha cc tham s mc nh, bn khng th ch nh cc tham s mc nh cc tham s khng c m nh. One other point about default arguments: they must be constants or global variables. They cannot be local variables or other paramenters. Mt im khc v i s mc nh: chng phi l cc hng hoc l cc bin ton cc. Chng khng th l cc bin a phng hoc cc tham s khc.

Examples
Cc V D 1. Here is a program that illustrates the example described in the preceding discussion: y l chng trnh c minh ha v d m t trong phn tho lun trc: // A simple firdt example of default arguments. #include "stdafx.h"

291

#include <iostream> using namespace std;

void f(int a=0, int b=0) { cout<< "a: " << a << ", b:" << b; cout<< \n; }

int main() { f(); f(10); f(10,99); return 0; } As you should expect, this program display the following output: Nh bn ch t, chng trnh s hin th kt qu nh sau: a: 0, b: 0 a: 10, b: 0 a: 10, b: 99 Remember that once the first default argument is specfied, all following paraments must have defaults as well. For example, this slightly different version of f() causes a compile-time error: Nh rng khi i s mc nh u tin c ch r, tt c tham s theo sau cng phi mc nh. V d, phin bn ni khc ca f() sau y to ra li thi gian bin

292

dch: void f(int a=0, int b) too { cout<< "a: " << a << ", b:" << b; cout<< \n; } 2. To understand how default arguments are ralated to function overloading, first consider the next program, which overloads the function called rect_area(). This function returns the area of a rectangle. hiu cc i s mc nh lin quan vi s qu tair hm, trc ht hy xt chng trnh tip y. Chng trnh ny qu ti hm rect_area(). Hm ny tr v din tch hnh ch nht. //computer functions. area of a rectangle using overloading //wrong! B must have default,

#include "stdafx.h" #include <iostream> using namespace std;

//return area of a non-square rectangle double rect_area (double length, double width) { return length * width; }

//return area of a square double rect_area (double length)

293

{ return length * length; }

int main() { cout<< "10 x 5.8 rectangle has area "; cout<< rect_area(10.0, 5.8) << '\n';

cout<< "10 x 10 square has area : "; cout<< rect_area(10.0) << '\n'; return 0; } In this program, rect_area() is overloaded two ways. In the first way, both dimensions of a rectangle are passed to the function. This version is used when the rectangleis not a square. However, when the rectangle is a aquare, only one rect_area() is called Trong chng trnh ny, rect_area() c qu ti theo hai cch trong cch th nht, ca hai chiu ca hnh ch nht c truyn cho hm. Phin bn ny c s dng khi hnh ch nht khng phi l hnh vung. Tuy nhin, khi hnh ch nht l hnh vung, ch cn mt i s c ch r v phin bn th hai cua rect_area() c gi. If you think about it, it is clear than in this situation there is really no need to have two difference function. Instead, the second paramenter can be defaulted to some value that acts as a flag to rect_area(). When this value is seen by the function, is uses the length paramenter twice. Here is an example of this approach : R rng trong trng hp ny thc s khng cn c hai hm khc nhau. Thay vo , tham s th hai c th c mc nh v gi tr no tc ng nh ci hiu cho box_area(). Khi hm thy gi tr ny, n dng tham s length hai ln. y l v 294

d tip cn ny. //computer area of a rectangle using defualt functions. #include "stdafx.h" #include <iostream> using namespace std;

//return area of a

rectangle

double rect_area (double length, double width = 0) { if(!width) width = length ; return length * width; }

int main() { cout<< "10 x 5.8 rectangle has area "; cout<< rect_area(10.0, 5.8) << '\n';

cout<< "10 x 10 square has area : "; cout<< rect_area(10.0) << '\n'; return 0; } Here 0 is the default of width.This value was chosen because no rectangle will have a width of 0. (Actually, a rectangle width of 0 is a line.) Thus, if default value is senn, rect_area() automatically uses the value in length for the value of

295

width. y, zore l gi tr mc nh ca with. Gi tr ny c chn v khng c hnh hp no c cnh la zero c. (Tht s hnh ch nht c rng zero l ng thng). Do , nu nhn thy gi tr mc nh ny box-area() t ng dng gi tr trong length cho gi tr ca with As this example shows, default agruments often provide a simple alternative to function overloading. ( Of cuorse, there are many situations in which function overloading is still required.) Nh v d ny ch r, cc i s ngm nh thng da ra cch n gin qu ti hm. (D nhin, c nhiu trng hp trong cn cn n s qu ti hm)

3.

It is not only legal to give constructor functions default arguments, it is also common. As you saw earlier in this chapter, many times a constructor is overloaded simply to allow both intialized and uninitialized object to be create. In many cases ,you can avoid overloading a constructor by giving it one or more default arguments. For example, examine this program : Vic cho cc hm to cc i s mc nh l ng n v thng dng. Nh bn thy trc y chng trnh ny, mt hm ch to c qu ti ch cho php to ra cc tng c khi u vn khng c khi u. Trong nhiu trng hp, bn c th trnh qu ti ca mt hm to bng cch cho n mt hay nhiu hm nh. V d, xt chng trnh sau: #include "stdafx.h" #include <iostream> using namespace std;

class myclass { int x; public: /* Use default argument instead of overloading 296

myclass' constructor */ myclass (int n = 0) { x = n; } int getx() { return x; } }; int main() { myclass o1(10); myclass o2; //declare with initial value //declare without initializer

cout<< "o1: " << o1.getx() << '\n'; cout<< "o2: " << o2.getx() << '\n'; return 0; } As this example shows, by giving n the default value of 0, it is possible to create objects that have explicit initial values and those for which the default value is sufficient. Nh v d ny ch r, bng cch cho n gi tr mc nh zero, c th to ra cc mc i tng c cc gi tr u r rng v cc i tng c gi tr mc nh l . Another good application for a default argument is found when a parameter is used to select an option. It is possible to give that parameter a default value that is used as a flag that tells the function to continue to use the previously selected option. For example, in the following program, the function printf() display a string on the screen. If its how parameter is set to ignore, the text is display as is. If how is upper, the text is diaplayed in uppercase. If how is lower, the text is displayed in lower case. When how is not specified, it defaults to -1, which tells the function to rease the last how value. Mt cch p dng khc i vi mt i mc nh c tm thy khi mt hm s c dng chn mt ty chn. C th cho hm mt gi tr mc nh c dng nh mt c hiu dng bo cho hm tip tc s dng ty chn c cho trc. V d, trong chng trnh di y, hm printf hin th mt chi ln mn hnh.Nu tham s how c t cho ignore, vn bn c hin th nh chnh

297

n.Nu how l upper, vn bn ch hin th di ch hoa. Nu how l lower th vn bn c hin th di y l ch thng. Khi khng ch r h, gi tr mc nh ca n l -1, bo cho hm dng li gi tr how sau cng #include "stdafx.h" #include <iostream> #include <cctype> using namespace std;

const int ignore = 0; const int upper = 1; const int lower = 2;

void prinft( char *s, int how = -1); int main() { printf("Hello There \n",ignore ); printf("Hello There \n",upper); printf("Hello There \n"); //continue in upper printf("Hello there \n",lower); printf("That's all \n"); //continua in lower return 0; }

/* printf a string in the specified case. Use last case specified if none is given.*/ void printf( char * s, int how)

298

{ static int oldcase = ignore; //reuse old case if none specified if(how < 0) how = oldcase; while(*s) { switch(how) { case upper: cout<< (char) toupper (*s); break; case lower : cout<< (char) tolower(*s); break; default : cout<< *s; } s--; } oldcase = how; } This function display the following output: Hm ny hin th kt qu Hello There HELLO THERE HELLO THERE hello there

299

thats all Earlier in this charpter you saw the general from of a copy constructor. This general from was shown with only one parameter. However, it is possible to create copy constructors that take additional values. For example, the following is also an acceptable from a copy constructor: Phn u trong chng ny bn thy dng tng qut ca hm to bn sao. Dng tng qut ny c trnh by ch vi mt tham s. Tuy nhin c th to cc hm bn sao c cc i s thm vo min l cc i s thm vo c cc gi tr mc nh. V d, dng sau y cng chp nhp l dng ca hm to ca hm to bn sao: myclass( { //body of constructor } As long as the first argument is a raference to the object being copied, and all other arguments default, the function qualifies as a copy constructors.This flexibiliry you to create copy also contructors that have other uses. Ch cn i s th nht l tham chiu ti i tng c sao chp, v cc i tng khc c mc nh, th hm s l hm to bn sao. Tnh linh hot ny cho php bn bn to tra cc hm bn sao c nhng cng c khc. 6. Although default arguments are powerful and convenient, they can be misused. There is no question that, when used correctly, default arguments allow a function to perfrom its job in an efficient and easy-to-use mamner. However, this is only the case when the default default value given to a parameter makes sense. For example, if the argument is the value wanted nine times out ten, giving a function a default argument to this effect is obviously a good idea. However, in cases in which no one value is more likely to be used than anotherm, or when there is no one benefit to using a default value. Actually, providing a default argument when one is not called for destructures your program and tends to mislead anyone else who has to use that function. Mc du cc i s mc nh l mnh v thun li, chng c th khng dng ng cch. Khng c vn m khi c dng ng, cc i s mc nh cho php thc hin cng vic mt cch d dng v c hiu qu. Tuy nhin, y ch l 300 const mayclass *obj, int x = 0

trng hp khi gi tr mc nh (c cho vo tham s) c ngha. V d. nu i s l gi tr cn c 9 ln ngoi th vic cho hm mt i s mc nh hiu nhin l mt tng tt. Tuy nhin, trong nhng trng hp khi khng c ch li g dng i s mc nh nh mt gi tr c hiu th s dng gi tr mc nh khng c ngha hn. Thc s, vic cung cp i s mc nh t c ngha hn. Thc s, vic cung cp i s mc nh khi khng c gi s lm hng chng trnh ca bn v s hng dn sai cho ngi s dng hm . As with function overloading, part of becoming an excellent C++ programmer is knowing when to use a default argument and when not to. Vi s qu ti hm, mt ngi lp trnh C++ thnh tho s bit khi no s dng i s mc nh v khi no khng.

EXERCISES:
Cc V D: 1. In the C++ standard library is the function strtol(), which has this prototype: Trong th vin chun C++ c hm strtol(), nguyn dng sau: long strtol(const char *start, const base ); **end, int

The function converts the numeric string pointed to by start into a long integer. The munber base of the numeric string is specified by base. Upon return, end pionts ti the character in the string immediately following the end of the number. The long integer equivalent of the numeric string is returned base must be in the range 2 to 38. However, most commonly, base 10 is used Hm chuyn i chui s c con tr ti strat thnh s nguyn di. C s ca chui s c ch r bi base/ Khi tra v, end tr ti k t trong chui theo sau s cui. Tng dng s nguyn di ca chui tr v base phi trong khong t 2 n 38. Tuy nhin, rt thng thng c s 10 c dng. Create a function called myclass() that works the same as strtol() except that base the default argument of 10.( Feel free to use strtol() to actually perfrom the conversion. It requires the hearder < cstdlib>.) Demonstrate that your version Hy to hm mystrtol() hot ng ging nh strtol() vi ngoi l l base c

301

cho mt i s mc nh la 10. (Bn c th dng hm strtol() thc hin s chuyn i. Cn phi c file stdlib.h)Hy chng t phin bn ca bn hot ng ng. What is wrong with the following function prototype ? C g sai trong nguyn mu hm sau: char *f( char *p, int x = 0, char *q) Most C++ compiles supply nonstandard function that allow cursor positioning and the like. If you compiler supplies such function, create s function called myclreol() that clears the line from the current cursor position to the end of the line. However, give ths this function a parameter that specifies the number of character positions to clear. If the parameter is not specified, automatically clear the entire line. Otherwise, clear only the number of character positions specified by the parameter. Hu ht cc trinh bin dch C++ cung cp cc hm khng chun cho php con chy (cusor) nh. Nu trnh bin dch ca bn c chc nng nh th, hy to hm myclreol() xa dng t v tr con chy n cui dng. Tuy nhin, hy cho hm ny ny mt tham s v tr ca v tr c xa. Ngc li, ch xa nhng v tr k t no c ch r bi tham s. 4. What is wrong with the following prototype, which uses a default argument? C g sai trong nguyn mu dng i s mc nh sau y? int f(int count , int max = count);

5.5.

OVERLOADING AND AMBIGUITY - S QU TI V TNH KHNG XC NH:


When you are overloading functions, it is possible to introduce ambiguity into your program. Overloading-caused ambiguity can be introduced through type conversions, reference parameters, and default arguments. Further, some types of ambiguity are caused by the overloaded function themselves. Other types occur in the manner in which an overloaded function is called. Ambiguity must be removed before your program will compile without error.

302

Khi qu ti cc hm, c th dn n tnh khng xc nh (ambiguity) trong chng trnh ca ban. Tnh khng xc nh do qu ti gy ra c th c a vo thng qua cc chuyn i kiu, cc tham s tham chiu v cc i s mc nh. Hn na, mt s loi khng xc nh gy ra do chnh cc hm c qu ti. Cc loi khc xy ra do cch gi cc hm c qu ti. Tnh khng xc nh phi c loi b trc khi chng trnh ca bn bin dch khng c li.

EXAMPLES
CC V D One of the most common types of ambiguity is caused by C++s automatic type conversion rules. As you know, when a function is called with an argument that is of a compatible (but not the same) type as the parameter to which it is being passed, the type of the argument is automatically converted to the target type. In fact, it is this sort of type conversion that allows a function such as putchar() to be called with a character even though its argument is specified as an int. However, in some cases, this automatic type conversion will cause an ambiguous situation when a function is overloaded. To see how, examine this program:

Mt trong nhng loi thng dng nht ca tnh khng xc nh gy ra bi cc quy tc chuyn i kiu t ng ca C++. Nh bn bit, khi mt hm c gi c mt i s phi c kiu tng thch (nhng khng ging) nh i s c truyn cho hm, kiu ca i s t ng c chuyn i thnh kiu ch. iu ny i khi c xem l s xc tin kiu (type promotion), v hon ton ng. Thc t, chnh loi chuyn i kiu ny cho php mt hm nh putchar() c gi vi mt k t mc d i s ca n c ch r kiu int. Tuy nhin, trong mt s trng hp, loi chuyn i kiu t ng ny s gy ra tnh trng khng xc nh khi mt hm c qu ti. thy r nh th no, hy xt chng trnh sau: // This program contains an ambiguity error. #include <iostream> using namespace std; float f(float i) {

303

return i/2.0; } double f(double i) { return i/3.0; } int main() { float x = 10.09; double y = 10.09; cout << f(x); // unambiguous use f(float) cout << f(y); // unambiguous use f(double) cout << f(10); // ambiguous, convert 10 to double or float?? return 0; } As the comments in main() indicate, the compiler is able to select the correct version of f() when it is called with either a float or a double variable. However, what happens when it is called with an integer? Does the compiler call f(float) or f(double)? (Both are valid conversions!). In either case, it is valid to promote an integer into either a float or a double. Thus, the ambiguous situation is created. Nh nhng li ch gii trong main(), trnh bin dch c th la chn phin bn ng ca f() khi n c gi vi hoc mt bin float hoc mt bin double. Tuy nhin, iu g xy ra khi n c gi vi mt s nguyn? Trnh bin dch gi f(float) hoc f(double)? (C hai l nhng chuyn i c hiu lc!). Trong mi trng hp, xc tin mt s nguyn thnh hoc float hoc double u ng. Do , tnh trng khng xc nh xy ra. This example also points out that ambiguity can be introduced by the way an

304

overloaded function is called. The fact is that there is no inherent ambiguity in the overloaded versions of f() as long as each is called with an unambiguous argument. V d ny cng ch ra rng tnh khng xc nh c th xy ra khi mt hm qu ti c gi. Tnh khng xc nh khng phi l c sn trong cc phin bn qu ti ca f() min l mi ln c gi vi i s xc nh. Here is another example of function overloading that is not ambiguous in and of itself. However, when this function is called with the wrong type of argument, C++s automatic conversion rules cause an ambiguous situation.

y l mt v d khc v s qu ti hm m t n khng c tnh khng xc nh. Tuy nhin, khi c gi vi kiu i s sai, cc quy tc chuyn i t ng ca C++ gy ra tnh trng khng xc nh. // This program is ambiguous. #include <iostream> using namespace std; void f(unsigned char c) { cout << c; } void f(char c) { cout << c; } int main() { f(c); f(86); // which f() is called??? 305

return 0; } Here, when f() is called with the numeric constant 86, the compiler can not know whether to call f(unsigned char) or f(char). Either conversion is equally valid, thus leading to ambiguity. y, khi f() c gi vi hng s 86, trnh bin dch khng th bit l gi f(unsigned char) hay gi f(char). Mi chuyn i u ng nn dn n tnh khng xc nh. One type of ambiguity is caused when you try to overload functions in which the only difference is the fact that one uses a reference parameter and the other uses the default call- by-value parameter. Given C++s formal syntax, there is no way for the compiler to know which function to call. Remember, there is no value parameter and calling a function that takes a reference parameter. For example:

Mt loi tnh khng xc nh xy ra do bn qu ti cc hm trong c s khc bit duy nht l mt hm s dng mt tham s tham chiu v hm kia s dng tham s mc nh gi bng gi tr. Cho c php chnh thc ca C++, khng c cch cho trnh bin dch bit hm no c gi. Cn nh, khng c s khc bit v mt c php gia cch gi hm nhn tham s gi tr vi cch gi hm nhn tham s tham chiu. V d:

// An ambiguous program. #include <iostream> using namespace std;

int f(int a, int b) { return a+b; }

306

// this is inherently ambiguous int f(int a, int &b) { return a-b; }

int main() { int x=1, y=2; cout called??? << f(x,y); // which version of f() is

return 0; }

Here, f(x,y) is ambiguous because it could be calling either version of the function. In fact, the compiler will flag an error before this statement is even specified because the overloading of the two functions is inherently ambiguous and no reference to them could be resolved.

y, f(x,y) l khng xc nh v c th gi mi phin bn ca hm. Thc t, trnh bin dch s bo hiu li khi cu lnh ny c ch nh v s qu ti hai hm l hon ton khng xc nh v khng c tham chiu no ti chng c gii quyt.

Another type of ambiguity is caused when you are overloading a function in which one or more overloaded functions use a default argument. Consider this program:

307

Mt loi tnh khng xc nh khc xy ra khi qu ti hm trong c mt hay nhiu hm c qu ti dng mt i s mc nh; hy xt chng trnh sau.

// Ambiguity overloading.

based

on

default

arguments

plus

#include <iostream> using namespace std;

int f(int a) { return a*a; }

int f(int a, int b = 0) { return a*b; }

int main() { cout << f(10, 2); // calls f(int, int) cout << f(10); // ambiguous call f(int) or f(int, int)??? return 0; }

308

Here the call f(10,2) is perfectly acceptable and unambiguous. However, the compiler has no way of knowing whether the call f(10) is calling the first version of f() or the second version with b defaulting.

Li gi f(10,2) hon ton c chp nhn v xc nh. Tuy nhin, trnh bin dch khng c cch bit f(10) gi phin bn th nht ca f() hay phin bn th hai vi b l mc nh.

EXERCISE
BI TP

Try to compile each of the preceding ambiguous programs. Make a mental note of the types of error messages they generate. This will help you recognize ambiguity errors when they creep into your own programs.

Hy th bin dch mt chng trnh trong cc chng trnh khng xc nh trn y. Hy ch cc loi thng bo li. iu ny s gip bn nhn ra cc li ca tnh khng xc nh khi cc li ny c trong chng trnh ring ca bn.

5.6. FINDING THE ADDRESS OF AN OVERLOADED FUNCTION - TM A CH CA MT HM QU TI:


To conclude this chapter, you will learn how to find the address of an overloaded function. Just as in C, you can assign the address of a function (that is, its entry point) to a pointer and access that function via that pointer. A functions address is obtained by putting its name on the right side of an assignment statement without any parentheses or arguments. For example, it zap() is a function, assuming proper declarations, this is a valid way to assign p the address of zap() :

309

kt thc chng ny, bn s hc cch tm a ch ca mt hm qu ti. Nh trong C, bn c th gn a ch ca mt hm (ngha l im nhp ca n) cho mt con tr v truy cp hm thng qua con tr. a ch ca mt hm c c bng cch t tn hm bn phi ca cu lnh gn m khng c du ng m ngoc hoc i s. V d, nu zap() l hm, gi s cc khai bo u ng, th cch hp l gn a ch ca zap() cho p l: p = zap; In C, any type of pointer can be used to point to a function because there is only one function that it can point to. However, in C++ the situation is a bit more complex because a function can be overloaded. Thus, there must be some mechanism that determines which functions address is obtained. Trong C, mt kiu con tr bt k c dng tr ti mt hm bi v ch c mt hm m n c th tr ti. Tuy nhin, trong C++, tnh trng hi phc tp hn bi v hm c th c qu ti. Do , phi c mt c ch no xc nh cn c a ch ca hm no. The solution is both elegant and effective. When obtaining the address of an overloaded function, it is the way the pointer is declared that determines which overloaded functions address will be obtained. In essence, the pointers declaration is matched against those of the overloaded functions. The function whose declaration matches is the one whose address is used. Gii php l rt p v c hiu qu. Khi thu c a ch ca mt hm qu ti, chnh cch khai bo con tr xc nh a ch ca hm qu ti no s thu c. Thc cht, s khai bo con tr ph hp vi cch khai bo ca cc hm qu ti. Hm c s ph hp khai bo l hm c a ch c s dng.

EXAMPLE
V D
Here is a program that contains two versions of a function called space(). The first version outputs count number of spaces to the screen. The second version outputs count number of whatever type of character is passed to ch. In main(), two function pointers are declared. The first one is specified as a pointer to a function having only one integer parameter. The second is declared as a pointer to a function taking two parameters.

310

y l chng trnh cha hai phin bn ca hm space. Phin bn th nht xut count l s khong trng trn mn hnh. Phin bn th hai xut count l s bt k kiu k t no c truyn cho ch. Trong main(), hai con tr hm c khai bo. Con tr th nht c ch r khi tr ti hm ch c mt tham s nguyn: Con tr th hai c khai bo nh con tr ti hm nhn hai tham s:

/* Illustrate assigning overloaded functions. */ #include <iostream> using namespace std;

function

pointers

to

// Output count number of spaces. void space (int count) { for( ; count; count--) cout << ; } // Output count number of chs. void space (int count, char ch) { for( ; count; count--) cout << ch; } int main() { /* Create a pointer to void function with one int parameter.*/

311

void (*fp1)(int); /* Create a pointer to void function with One int parameter and one character parameter.*/ void (*fp2)(int, char); fp1 = space; // gets address of space (int) fp2 = space; // gets address of space (int, char) fp1(22); // output 22 spaces cout << |\n; fp2(30, x); // output 30 xs cout << |\n; return 0; } As the comments illustrate, the compiler is able to determine which overloaded function to obtain the address of based upon how fp1 and fp2 are declared. Nh cc li ch gii minh ha, trnh bin dch c th xc nh hm qu ti no nh thu c a ch cn c vo cch khai bo ca fp1 v fp2. To review: When you assign the address of an overloaded function to a function pointer, it is the declaration of the pointer that determines which functions address is assigned. Further, the declaration of the function pointer must exactly match one and only one of the overloaded functions. If it does not, ambiguity will be introduced, causing a compile-time error. Tng quan: Khi bn gn a ch ca mt hm qu ti cho mt con tr hm, chnh s khai bo con tr xc nh a ch ca hm no c gn. Hn na, khai bo con tr hm phi ph hp chnh xc vi mt v ch mt hm qu ti. Nu khng, tnh khng xc nh s xy ra v gy ra li thi gian bin dch.

EXERCISE BI TP
312

Following are two overloaded functions. Show how to obtain the address of each.

Sau y l hai hm qu ti. Hy trnh by cch c c a ch ca mi hm.

int dif(int a, int b) { return a-b; } float dif (float a, float b) { return a-b; }

SKILLS CHECK (KIM TRA K NNG):

C
++ Mastery
Skills Check

313

C
++ Kim tra k nng lnh hi
At this point you should be able to perform the following exercises and answer the questions. y bn c th lm cc bi tp v tr li cc cu hi sau: Overload the date() constructor from Section 5.1, Example 3, so that it accepts a parameter of type time_t. (Remember time_t is a type defined by the standard time and date functions found in your C++ compilers library.) Hy qu ti hm to date() trong phn 5.1, v d 3 cho n nhn mt tham s kiu time_t. (Nn nh, time_t l mt kiu c nh ngha bi cc hm v thi gian v ngy thng chun trong th vin ca trnh bin dch C++ ca bn).

What is wrong with the following fragment? iu g sai trong on chng trnh sau?

class samp { int a; public: samp (int i) {

314

a = i; } // }; // int main() { samp x, y(10); // }

Give two reasons why you might want (or need) to overload a classs constructor. Hy trnh by hai l do ti sao bn mun (hoc cn) qu ti hm to ca mt lp.

What is the most common general form of a copy constructor? Dng tng qut ca hm to bn sao l g?

What type of operations will cause the copy constructor to be invoked? Kiu php ton no s lm cho hm to bn sao c dng n?

Briefly explain what the overload keyword does and why it is no longer needed? Gii thch ngn gn t kha overload l g v ti sao khng cn cn dng na?

Briefly describe a default argument.

315

Trnh by ngn gn i s mc nh l g?

Create a function called reverse() that takes two parameters. The first parameter, called str, is a pointer to a string that will be reversed upon return from the function. The second parameter is called count, and it specifies how many characters of str to reverse. Give count a default value that, when present, tells reverse() to reverse the entire string.

Hy to hm reverse nhn hai tham s. Tham s th nht, gi l str, l mt con tr tr ti chui m chui ny c o ngc khi tr v t hm. Tham s th hai gi l count v n ch r c bao nhiu k t ca chui o ngc. Hy cho count mt gi tr mc nh bo cho reverse() o ngc ton b chui.

What wrong with the following prototype? iu g sai trong nguyn mu sau?

char *wordwrap (char *str, int size=0, char ch);

Explain some ways that ambiguity can be introduced when you are overloading functions. Hy gii thch theo nhiu cch xy ra tnh khng xc nh khi qu ti cc hm.

What is wrong with the following fragment? iu g sai trong on chng trnh sau?

void compute (double *num, int divisor=1); void compute (double *num);

316

// compute(&x);

When you are assigning the address of an overloaded function to a pointer, what is it that determines which version of the function is used?

Khi gn a ch ca mt hm qu ti cho mt con tr, chnh iu g xc nh phin bn no ca hm c dng?

C
++Cumulative
Skills Check

Kim tra k nng tng hp

This section checks how well you have integrated material in this chapter with that from the preceding chapters. Phn ny kim tra xem bn kt hp chng ny vi nhng chng trc nh th no. Create a function called order() that takes two integer reference parameters. If the first argument is greater than the second argument, reverse the two arguments. Otherwise, take no action. That is, order the two arguments used to call order() so that, upon return, the first argument will be less than the second. For example, given

317

Hy to hm order() nhn hai tham s tham chiu nguyn. Nu i s th nht ln hn i s th hai, hy o ngc hai s i. Ngc li, khng c tc ng no. Ngha l, th t ca hai i s c dng gi order() sao cho, khi tr v, i s th nht s nh hn i s th hai. V d, cho:

int x=1, y=0; order (x,y);

following the call, x will be 0 and y will be 1. sau khi gi, x s l 0 v y s l 1.

Why are the following two overloaded functions inherently ambiguous? Ti sao hai hm qu ti sau y vn khng xc nh?

int f(int a); int f(int &a);

Explain why using a default argument is related to function overloading. Hy gii thch ti sao dng i s mc nh c lin quan n s qu ti hm.

Given the following partial class, add the necessary constructor functions so that both declarations within main() are valid. (Hint: You need to overload samp() twice.)

Cho lp ring phn sau, hy b sung cc hm to cn thit cho c hai khai bo trong main() u ng. (Hng dn: Bn cn qu ti samp() hai ln).

318

class samp { int a; public: // add constructor functions int get_a() { return a; } }; int main() { samp ob(88); // int obs a to 88 samp array // } obarray[10]; // noninitialized 10-element

Briefly explain why copy constructor. Hy gii thch ngn gn l ti sao cn cc hm to bn sao?

_________________________________________________ _________

319

CHNG 6 INTRODUCING OPERATOR OVERLOADING GII THIU V NP CHNG TON T


Chapter objectives:
6.1. THE BASICS OF OPERATOR OVERLOADING C s ca s np chng ton t 6.2. OVERLOADING BINARY OPERATORS Np chng ton t nh nguyn 6.3. OVERLOADING THE RELATIONAL AND LOGICAL OPERATORS Np chng cc ton t quan h v lun l 6.4. OVERLOADING A NUARY OPERATOR Np chng ton t n nguyn 6.5. USING FRIEND OPERATOR FUNCTIONS S dng hm ton t friend 6.6. A CLOSER LOOK AT THE ASSIGNMENT OPERATOR Ton t gn 6.7. OVERLOADING THE [ ] SUBSCRIPT OPERATOR Np chng ton t ch s di []

This chapter introduces another important C++ feature: operator overloading. This feature allows you to define the meaning of the C++ operators relative to classes that you define. By overloading operators, you can seamlessly add new data types to your

320

program. Chng ny gii thiu mt c im quan trng khc ca C++: s Np chng ton t. c im ny cho php bn xc nh ngha ca cc ton t C++ lin quan vi cc lp m bn nh ngha. Bng cch thc hin Np chng cc ton t i vi lp, bn c th b sung cc loi d liu mi cho chng trnh ca bn

REVIEW SKILLS CHECK (Kim tra k nng n) Before proceeding, you should be able to correctly answer the following questions and do the exercises. Trc khi bt u, bn nn tr li ng cc cu hi v lm cc bi tp sau: Show how to overload the constructor for the following class to that uninitialized objects can also be created. (When creating uninitialized objects, give x and y the value 0). Hy thc hin Np chng cho hm to i vi lp sau y sao cho cc i tng khng c khi u cng c to ra. (Khi to ra cc i tng cha c khi u, cho x va y gi tri 0). class myclass { int x,y; public: myclass( int i, int j) { x=i; y=j;} //... }; Using the class from Question 1, show how you can avoid overloading myclass() by using default arguments. S dng lp trong cu hi 1, hy chng t c th khng np chng myclass() bng cch dng cc i s mc nh. What is wrong with the following declaration? iu g sai trong khai bo sau y:

321

int f(int a=0, double balance); What is wrong with these two overloaded functions? iu g sai trong hai hm np chng sau y? void f (int a); void f (int &a); When is it appropriate to use default arguments? When is it probably a bad idea? Dng cc i s mc nh khi no l thch hp? Khi no n l mt tng khng hay? Given the following class definition, is it possible to dynamically allocate an array of these objects? Cho nh ngha lp di y, c th cp pht ng mt mng cc i tng ny? class test { char *p; int *q; int count; public: test(char *x, int *y, int c) { p = x; q = y; count = c; } //... };

322

What is a copy constructor and under what circumstances is it calles? Hm to sao chp l g? V n c gi trong trng hp no?

THE BASICS OF OPERATOR OVERLOADING - C S CA QU TI TON T


Operator overloading resembles function overloading. In fact, operator overloading is really just a type of function overloading. However, some additional rules apply. For example, an operator is always overloaded relative to a user-defined type, such as a class. Other differences will be discussed as needed. S Np chng ton t ging nh s Np chng hm. Tht vy, s Np chng ton t ch l mt loi Np chng hm. Tuy nhin, n c mt s quy tc b sung. V d, mt ton t thng c Np chng i vi mt lp. Nhng im khc bit khc s c trnh by khi cn thit. When an operator is overloaded, that operator loses none of its orginal meaning. Instead, it gains additional meaning relative to the class for which it is defined. Khi mt ton t c Np chng, ton t khng mt ngha gc ca n. Thm na, ton t cn c thm ngha b sung i vi lp m ton t c nh ngha. To overload an operator, you create an operator function. Most often an operator function is a member or a friend of the class for which it is defined. However, there is a slights difference between a member operator function and a friend operator function. The first part of this chapter discusses the creation of member operator functions. Then friend operator functions are discussed. Np chng mt ton t, bn to ra mt hm ton t (operator function). Thng mt hm ton t l mt thnh vin (member) hoc bn (friend) ca lp m ton t c nh ngha. Tuy nhin c s khc bit nh gia mt hm ton t thnh vin v mt hm ton t friend. Phn u ca chng trnh by cch to ra cc hm ton t thnh vin. Cc hm ton t friend s c tho lun sau . The general form of a member operator function is shown here: Dng tng qut ca mt hm ton t thnh vin nh sau: return-type class-name::operator#(arg-list) { // operator to be performed

323

} The return of an operator function is often the class for which it is defined. (However, an operator function is free to return any type). The operator being overloaded is substituted for the #. For example, if the + is being overloaded, the function name would be operator+. The contents of arg-list vary depending upon how the operator function is implemented and the type of operator being overloaded. Kiu tr v ca mt hm ton t l lp m ton t c nh ngha. (Tuy nhin, mt hm ton t tr v kiu bt k). Ton t c Np chng thay th c cho k t #. V d, nu ton t + c Np chng th tn hm s l operator+. Ni dung ca arg-list thay i ph thuc vo cch m hm ton t c thc hin v kiu ton t c Np chng. There are two important restrictions to remember when you are overloading an operator. First, the precedence of the operator cannot be changed. Second, the number of operands that an operator takes cannot be altered. For example, you cannot overload the / operator so that it takes only one operand. C hai hn ch quan trng cn nh khi Np chng mt ton t. Th nht th t u tin ca cc ton t khng thay i. Th hai, s ton hng ca mt ton t khng thay i. V d, bn khng th Np chng ton t / cho n c ch mt ton hng. Most C++ operators can be overloaded. The only operators that you cannot overload are shown here: Hu ht cc ton hng trong C++ c th c Np chng. Cc ton t khng th Np chng c l: . :: * ? Also, you cannot overload the preprocessor operators. (the * operator is highly specialized and is beyond the scope of this book). Cng vy, bn khng th Np chng cc ton t tin x l. (Ton t c chuyn dng cao v ngoi phm vi cun sch). Remember that C++ defines operators very broadly, including such things as the [ ] subscript operators, the () function call operators, new and delete, and the . (dot) and b -> (arrow) operators. However, this chapter concentrates on overloading the most commonly used operators. Nh rng C++ nh ngha cc ton t rt rng, gm nhng ton t nh ton t ch s di [ ] v ton t gi hm (), new v delete, v ton t . (chm) v ton t -> (mi tn).

324

Tuy nhin, chng ny ch tp trung vo s Np chng cc ton t thng c s dng nht. Except for the =, operator functions are inherited by any derived class. However, a derived class is free to overload any operator it chooses (including those overloaded by the base class) relative to itself. Ngoi tr ton t =, cc hm ton t c k tha bi bt k lp dn xut no. Tuy nhin, mt lp dn xut hon ton Np chng c bt k ton t no m n chn (gm c nhng ton t c Np chng bi lp c s) i vi n. You have been using two overloaded operators: << and >>. These operators have been overloaded to perform console I/O. As mentioned, overloading these operators to perform I/O does not prevent them from performing their traditional jobs of left shift and right shift. Bn s dng hai ton t c np chng: << v >>. Nhng ton t ny c Np chng thc hin giao tip nhp xut. Nh ni, s Np chng cc ton t ny thc hin nhp xut / xut khng ngn tr chng thc hin cc cng vic truyn thng l dch tri v phi.

While it is permissible for you to have an operator function perform any activitywhether related to the traditional use of the operator or not-it is best to have an overloaded operators actions stay within the spirit of the operators traditional use. When you create overloaded operators that stray from this principle, you run the risk of substantially destructuring your program. For example, overloading the / so that the phrase I like C++ is written to a disk file 300 times is a fundamentally confusing misuse of operator overloading! Trong khi bn c php c mt hm ton t thc hin mt hot ng bt k - d c lin quan n cch s dng cc ton t theo truyn thng hay khng th tt nht nn cho cc tc ng ca cc ton t c Np chng theo tinh thn s dng truyn thng ca cc ton t. Khi bn to ra cc ton t Np chng khng tun theo nguyn tc ny, th c nguy c bn s lm hng chng trnh ca bn. V d, np chng ton t / sao cho cm t I like C++ c vit vo mt file a 300 ln th c bn to ra cch s dng sai s Np chng ca ton t. The preceding paragraph notwithstanding, there will be times when yo need to use an operator in a way not related to its traditional usage. The two best examples of this are the <<and >> operators, which are overloaded for console I/O. However, even in these cases, the left and right arrows provide a visual clue to their meaning. Therefore, if you need to overload an operator in a nonstandard way, make the greatest effort possible to use an appropriate operator.

325

Mc d on trn y, nhng c nhng lc bn s cn s dng mt ton t khng c lin quan g n cch s dng truyn thng ca ton t. Hai v d tt nht v iu ny l cc ton t << v >> c Np chng cho giao tip nhp / xut. Tuy nhin, ngay c trong nhng trng hp ny, cc mi tn hng phi v hng tri cng ni ln ngha ca cc ton t. Do nu bn cn Np chng mt ton t theo cch khng chun, th hy c gng s dng mt ton t thch hp. im cui cng: cc hm ton t c th khng c cc i s ngm nh. One final point: operator functions cannot have default arguments. iu lu cui cng: cc hm ton t khng c cc i s ngm nh.

OVERLOADING BINARY OPERATORS - QU TI TON T NH NGUYN


When a member operator function overloads a binary operator, the function will have only parameter. This parameter will receive the object that is on the right side of the operator. The object on the left side is the object that generates the call to the operator function and is passed implicitly by this. Khi mt hm ton t thnh vin Np chng mt ton t nh nguyn, hm s ch c mt tham s. Tham s ny s nhn i tng nm bn phi ton t. i tng bn tri l i tng to li gi cho hm ton t v c truyn bi this. It is important to understand that operator functions can be written with many variations. The examples here and elsewhere in this chapter are not exhaustive, but they do illustrate several of the most common techniques.

iu quan trng l cc hm ton t c vit theo nhiu cch khc nhau. Cc v d di y v nhng phn khc nhau trong chng ny khng hn l y nhng cng minh ha c nhiu k thut thng dng nht. EXAMPLES: (V D) 1. The following program overloads the + operator relative to the coord class. This class is used to maintain X,Y coordinates. Chng trnh sau thc hin Np chng cho ton t + i vi lp coord. Lp ny c dng duy tr cc ta X,Y.

326

// Overload the + relative to coord class. #include "iostream" using namespace std;

class coord { int x,y; // coordinate values public: coord() { x=0; y=0; } coord(int i, int j) { x=i; y=j; } void get_xy(int &i, int &j) { i=x;j=y;} coord operator + (coord ob2); };

// overload + relative to coord class. coord coord::operator +( coord ob2) { coord temp; temp.x = x + ob2.x; temp.y = y + ob2.y;

return temp; }

int main() { coord o1 (10, 10) , o2 (5, 3), o3;

327

int x,y;

o3 = o1 + o2; // add two objects - this // calls operator + () o3.get_xy(x,y); cout<<"(o1+o2) X: "<<x<<", y: "<<y<<"\n";

return 0; } This program displays the following: Chng trnh ny hin th: (o1 + o2) X: 15, Y: 13 Lets look closely at this program. The operator +() function returns an object of type coord that has the sum of each operands X coordinates in x and the sum of the Y coordinates in y. Notice the a temporary object called temp is used inside operator +() to hold the result, and it is this object that is returned. Notice also that neither operand is modified. The reason for temp is easy to understand. In this situation (as in most), the + has been overloaded in a manner consistent with neither operand be changed. For example, when you add 10 + 4, the result is 14, but neither 10 nor the 4 is modified. Thus, a temporary object is needed to hold the result. Hy xt k hn chng trnh ny. Hm operator +() tr v mt i tng c kiu coord l tng ca cc ta ca mi ton hng theo x v tng ca cc ta Y theo y. Ch rng i tng tm thi temp c dng bn trong operator+() gi kt qu, v l i tng c tr v. Cng ch rng ton hng khng c sa i. L do i vi temp th d hiu. Trong trng hp ny, ton t + c Np chng ph hp vi cch s dng s hc ca n. Do , iu quan trng l ton hng khng c thay i. V d, khi bn cng 10+4, kt qu l 14, nhng 10 v 4 l khng thay i. Do , cn mt i tng tm thi gi kt qu.

328

The reason that the operator+() function returns an object of type coord is that it allows the result of the addition of coord objects to be used in larger expressions. For examples, the statement. L do m hm operator+() tr v i tng c kiu coord l n cho php kt qu ca php cng cc i tng coord s dng trong biu thc ln hn. V d, cu lnh: O3 = o1 + o2; is valid only because the result of o1 + o2 is a coord object that can be assigned to o3. if a different type had been returned, this statement would have been invalid. Further, by returning a coord object, the addition operator allows a string of additions. For example, this is a valid statement: l ng ch v kt qu ca o1 + o2 l i tng coord c gn cho o3. Nu mt kiu khai thc c tr v, cu lnh ny s khng ng. Hn na, khi tr v mt i tng, ton t php cng cho php mt chui php cng. V d, y l cu lnh ng: o3 = o1 + o2 + o1 + o3; Although there will be situations in which you want an operator function to return something other than an object for which it is defined, most of the time operator functions that you create will return an object of their class. (The major exception to this rule is when the relational an logical operators are overloaded. This situationis examined in Section 6.3. Overloading the Relational and Logical Operators, later in this chapter). Mc d c nhiu trng hp bn mun mt hm ton t tr v mt i tng no thay v i tng m hm ton t nh ngha, hu ht cc hm ton t m bn to ra s tr v mt i tng ca lp m i vi lp ny, cc hm ton t c nh ngha (ngoi l quan trng i vi quy tc ny l khi cc ton t quan h v cc ton t lun l c Np chng). Trng hp ny c xt n trong phn cui ca chng ny. One final point about this example. Because a coord object is returned, the following statement is also perfectly valid: iu cui cng ca v d ny. V i tng coord c tr v, cu lnh sau y cng hon ton ng. (O1 + o2).get_xy(x,y); Here the temporary object returned by operator +() is used directly. Of course, after

329

this statement has executed, the temporary object is destroyed. y, i tng tm thi c tr v bi operator+() c s dng trc tip. D nhin, sau khi cu lnh ny c thi hnh, i tng tm thi s b hy b. 2. The following version of the preceding program overloads the and the = operators relative to the coord class. Dng sau y ca chng trnh trn thc hin Np chng cc ton t - v = i vi lp coord. #include "iostream" using namespace std; class coord { int x,y; // coordinate values public: coord() { x=0; y=0;} coord(int i, int j) { x=i; y=j; } void get_xy(int &i, int &j) { i=x; j=y; } coord operator + (coord ob2); coord operator - (coord ob2); coord operator = (coord ob2); };

330

// Overload + relative to coord class. coord coord::operator + (coord ob2) { coord temp;

temp.x = x + ob2.x; temp.y = y + ob2.y;

return temp; }

// Overload - relative to coord class. coord coord::operator - (coord ob2) { coord temp;

temp.x = x - ob2.x; temp.y = y - ob2.y;

return temp; } // Overload + relative to coord class. coord coord::operator = (coord ob2) { x = ob2.x; y = ob2.y;

331

return *this; // return the object that is //assigned } int main() { coord o1(10, 10), o2(5, 3), o3; int x,y;

o3 = o1 + o2; // add two objects - this //calls operator +() o3.get_xy(x,y); cout<<"(o1+o2) X: "<<x<<", Y: "<<y<<"\n";

o3 = o1 - o2; // subtract two objects o3.get_xy(x,y); cout<<"(o1-o2) X: "<<x<<", Y: "<<y<<"\n";

o3 = o1; // assign an object o3.get_xy(x,y); cout<<"(o1=o2) X: "<<x<<", Y: "<<y<<"\n";

return 0; } The operator () function is implemented similarly to operator +(). However, the above example illustrates a crucial point when you are overloading an operator in which the order of the operands is important. When the operator +() function was created, it did not matter which order the operands were in. (That is, A+B is the same as B+A). However, the subatraction operation is order dependent. Therefore, to correctly overload the subtraction operator, it is necessary to subtract the operand on the right

332

from the operand on the left. Because it is the left operand that generates the call to operator (), the subtraction must be in this order: Hm operator-() c thc hin tng t nh operator+(). Tuy nhin, n minh ha mt im cn ch khi Np chng mt ton t trong th t ca cc ton hng l quan trng. Khi hm operator+() c to ra, n khng ch n th t ca cc ton hng nh th no. (Ngha l, A + B cng ging B + A). Tuy nhin, php tr th li ph thuc vo th t ny. Do , Np chng ng mt ton t php tr th cn phi ly ton hng bn tri tr ton hng bn phi. V chnh ton hng bn tri to ra li gi i vi operator-() nn php tr phi theo th t: x = ob2.x; Remember: when a binary operator is overloaded, the left operand is passed implicitly to the function and the right operand is passed as an argument. Cn nh: khi mt ton t nh nguyn c Np chng, ton hng bn tri c truyn cho hm v ton hng bn phi c truyn bn phi c truyn nh mt i s.

Now look at the assignment operator function. The first thing you should notice is that the left operand (that is, the object being assigned a value) is modified by the operation. This is in keeping with the normal meaning of assignment. The second thing to notice is that the function returns *this. That is, the operator =() function returns the object that is being assigned to. The reason for this is to allow a series of assignments to be made. As you should know, in C++, the following type of statement is syntactically correct (and, indeed, very common): By gi chng ta xt hm ton t gn. Trc ht cn ch rng ton hng bn tri (ngha l i tng c gn cho mt gi tr) c thay i bi php ton. iu ny vn gi nguyn ngha thng thng ca php gn. iu th hai cn ch l hm tr v *this. Ngha l, hm operator=() tr v i tng s c gn. L do ny l cho php thc hin mt chui php gn. Nh bn bit trong C++, cu lnh sau y ng v mt ng php (v thng c s dng). a = b =c =d =0; By returning *this, the overloaded assignment operator allows objects of type coord to be used in a similar fashion. For example, this is perfectly valid:

333

Bng cch tr v *this, ton t gn c Np chng cho php cc i tng kiu coord c dng theo cch nh nhau. V d cu lnh ny hon ton ng. ob = o2 = o1; Keep in mind that there is no rule that requires an overloaded assignment function to return the object that receisves the assignment. However, if you want the overloaded = to behave relative to its class the way it does for the built-in types, it must return *this. Nh rng khng c quy tc no i hi hm gn Np chng tr v i tng m i tng ny nhn php gn. Tuy nhin nu bn mun Np chng ton t = n hot ng i vi lp theo cch nh i vi cc kiu nh sn bn trong th n phi tr v *this. 3. It is possible to overload an operator relative to a class so that the operand on the right side is an object of a built-in type, such as integer, instead of the class for which the operator function member. For example, here the + operator is overloaded to add an integer value to a coord object: C th Np chng mt ton t i vi mt lp cho ton hng bn phi l mt i tng c kiu nh sn, nh mt s nguyn chng hn, thay v lp m hm ton t i vi n l mt thnh vin. V d, y l ton t + c Np chng cng mt gi tr nguyn v i tng coord: // Overload + for ob + int as well as ob + ob. #include "iostream" using namespace std;

class coord { int x,y; // coordinate values; public: coord() { x = 0; y = 0;

334

} coord (int i, int j) { x = i; y = j; } void get_xy( int &i, int &j) { i = x; j = y; } coord operator + (coord ob2); //ob + ob coord operator + (int i); // ob + int };

// Overload + relative to coord class. coord coord::operator +( coord ob2) { coord temp;

temp.x = x + ob2.x; temp.y = y + ob2.y;

return temp; }

335

// Overload + for ob + int coord coord::operator +( int i) { coord temp;

temp.x = x + i; temp.y = y + i; return temp; }

int main() { coord o1(10, 10), o2(5, 3), o3; int x,y;

o3 = o1 + o2; // add two objects - calls //operator+(coord) o3.get_xy(x, y); cout<<"(o1 + o2) X: "<<x<<", Y: "<<y<<"\n";

o3 = o1 +100; // add objects + int - calls // operator+(int) o3.get_xy(x, y); cout<<"(o1 + 100) X: "<<x<<", Y: "<<y<<"\n";

return 0; }

336

It is important to remember that when you are overloading a member operator function so that an object can be used in an operation involving a built-in type, the built-in type must be on the right side of the operator. The reason for this is easy to understand. It is the object on the left that generates the call to the operator function. For instance, what happens when the compiler sees the following statement? iu quan trng cn nh l khi Np chng mt hm ton t thnh vin cho mt i tng c th c dng trong mt php ton c kiu nh sn, th kiu nh sn phi bn phi ca ton t. L do ny tht d hiu: chnh i tng bn tri to ra li gi cho hm ton t. Tuy nhin, iu g xy ra khi trnh bin dch gp cu lnh ny? o3 = 19 + o1; // int + ob

There is no built-in operation defined to handle the addtion of an integer to an object. The overloaded operator+(int i) function works only when the object is on the left. Therefore, this statement generates a compile-time error. (Soon you will see one way around this restriction). Khng c php ton nh sn c nh ngha thc hin php cng mt s nguyn vo mt i tng. Hm Np chng operator+(int i) ch hot ng khi i tng bn pha tri. Do , cu lnh ny to ra mt li sai v bin dch. 4. You can use a reference parameter in an operator function. For example, this is a perfectly acceptable way to overload the + operator relative to the coord class: Bn c th dng tham s qui chiu trong mt hm ton t. V d, y l cch hon ton chp nhn c Np chng ton t + i vi lp coord. coord coord::operator +(coord &ob) { coord temp; temp.x = x + ob2.x; temp.y = y + ob2.y;

return temp; }

337

One reason for using a reference parameter in an operator function is efficiency. Passing objects as parameters to functions often incurs a large amount of overhea and consumes a significant number of CPU cycles. However, passing the address of an object is always quick ad efficient. If the operator is going to be used often, using a reference parameter will generally improve performance significantly. Mt l do dng tham s quy chiu trong hm ton t l s hiu qu. Truyn cc i tng nh cc tham s cho cc hm thng chu nhiu th tc b sung v tiu tn mt s ng k cc chu k CPU. Tuy nhin, truyn a ch ca mt lp i tng thng nhanh v hiu qu. Nu ton t thng c s dng th vic dng tham s quy chiu s ci thin nng sut ng k. Another reason for using a reference parameter is to avoid the trouble caused when a copy of an operand is destroyed. As yo know from previous chapters, when an argument is passed by value, a copy of that argument is made. If that object has a destructor function, when the function terminates, the copys destructor is called. In some cases it is possible for the destructor to destroy something needed by the calling object. If this is the base, using a reference parameter instead of a value parameter is an easy (and efficient) way around the problem. Of course, you could also define a copy constructor that would prevent this problem in the general case. Mt l do khc dng tham s quy chiu l trnh nhng rc ri gy ra khi bn sao mt ton hng b hy. Nh bn bit t nhng chng trc, khi mt i s c truyn bi mt gi tr th bn sao ca i s c thc hin. Nu i tng c mt hm to, th khi hm kt thc, hm hy ca bn sao c gi. Trong mt s trng hp, hm hy c th hy b ci g bng i tng gi. Trong trng hp ny, vic s dng mt tham s quy chiu thay v mt tham s gi tr s l mt cch d dng gii quyt bi ton. Tuy nhin, cn nh rng bn cng c th nh ngha mt hm hy bn sao ngn nga vn ny trong trng hp tng qut.

EXERCISES(BI TP)
Relative to coord, overload the * and / operators. Demonstrat that they work. Hy Np chng ton t * v / i vi coord. Chng t chng hot ng. Why would the following be an inappopriate use of an overloaded operator? Ti sao phn di y l cch s dng khng thch hp ca mt ton t c qa ti. coord coord::operator%(coord ob) { double i;

338

cout<<" Enter a number: "; cin>>i; cout<<"root of "<<i<<" is"; cout<<sqr(i); }

On your own, experiment by changing the return types of the operator functions to something other than coord. See what types of errors result. Bn hy th thay i cc kiu tr v ca cc hm ton t i vi lp khc coord. Xem kiu g c kt qa sai.

OVERLOADING THE RELATIONAL AND LOGICAL OPERATORS - QU TI CC TON T QUAN H V LUN L

It is possible to overload the relational and logical operations. When you overload the relational and logical operators so that they behave in their traditional manner, you will not want the operator functions to return an object of the class for which they are defined. Instead, they will return an integer that indicates either true of false. This not only allows these operator functions to return a true/false value, it also allows the operators to be integrated into larger relational and logical expressions that involve other types of data. C th Np chng cc ton t quan h v lun l. Khi bn Np chng cc ton t quan h v lun l chng hot ng theo cch truyn thng, bn s khng cn cc hm ton t tr v mt i tng ca lp m cc hm ton t c nh ngha i vi lp ny. Thay v vy, cc hm ton t s tr v mt s nguyn ch ng hay sai. iu ny khng ch cho php cc hm ton t tr v gi tr ng / sai m n cn cho php cc ton t c kt hp vi nhau thnh mt biu thc quan h v lun l ln hn c cha cc kiu d liu khc.

339

Note: if you are using a modern C++ compiler, you can also have an overloaded relational or logical operator function return a value of type bool, although there is no advantage to doing so. As explained in Chapter 1, the bool type defines only two values: true and false. These values are automatically converted into nonzero and 0 values. Integer nonzero and 0 values are automatically converted into true and false. Ch : Nu bn s dng trnh bin dich C++ mi, bn cng c th Np chng hm ton t quan h hoc lun l v tr v gi tr ca c bool, mc d khng c li khi lm vy. Nh gii thch trong chng 1, c bool ch nh ngha hai gi tr: ng v sai. Nhng gi tr ny c t ng chuyn thnh s khc khng v bng 0. S nguyn khc khng v bng 0 c t ng chuyn thnh ng v sai.

EXAMPLE(V D) In the following program, the == and && operator are overloaded: Trong chng trnh sau, cc ton t == v && c Np chng. // Overload the == and relative to coord class. #include "iostream" using namespace std;

class coord { int x,y; // coordinate values public: coord() { x = 0; y = 0; } coord (int i, int j) {

340

x = i; y = j; } void get_xy(int &i, int&j) { i = x; j = y; } int operator ==(coord ob2); int operator &&(coord ob2); };

// Overload the == operator for coord int coord::operator==(coord ob2) { return x==ob2.x && y==ob2.y; }

// Overload the && operator for coord int coord::operator &&(coord ob2) { return (x && ob2.x) && (y && ob2.y); }

int main() { coord o1(10,10),o2(5,3),o3(10,10),o4(0,0);

341

if(o1==o2) cout<<" o1 same as o2 \n"; else cout<<" o1 and o2 differ\n";

if(o1==o3) cout<<" o1 same as o3 \n"; else cout<<" o1 and o3 differ\n";

if(o1&&o2) cout<<" o1 && o2 is true \n"; else cout<<" o1 && o2 is false\n";

if(o1&&o4) cout<<" o1 && o4 is true \n"; else cout<<" o1 && o4 is false\n";

return 0; }

EXERCISE(BI TP)
Overload the < and > operators relative to the coord class.

342

Qa ti cc ton t < v > i vi lp coord.

OVERLOADING A UNARY OPERATOR - QU TI TON T N NGUYN


Overloading a unary operator is similar to overloading a binary operator except that there is only one operand to deal with. When you overload a unary operator using a member function, the function has no parameters. Since there is only one operand, it is this operand that generates the call to the operator function. There is no need for another parameter. Np chng ton t n nguyn tng t nh Np chng ton t nh nguyn ngoi tr ch c mt ton hng. Khi bn Np chng mt ton t n nguyn bng cch dng hm thnh vin, th hm khng c tham s. V ch c mt ton hng, nn chnh ton hng ny to ra li gi cho hm ton t. Khng cn c mt tham s khc. EXAMPLES(V D) The following program overloads the increment operator (++) relative to the coord class: Chng trnh sau Np chng ton t tng (++) i vi lp coord: // Overload ++ relative to coord class #include "iostream" using namespace std;

class coord { int x,y; // coordinate values public: coord() {

343

x = 0; y = 0; } coord (int i, int j) { x = i; y = j; } void get_xy(int &i, int&j) { i = x; j = y; } coord operator ++(); };

// Overload ++ for coord class coord coord::operator ++() { x++; y++;

return *this; }

int main() {

344

coord o1(10, 10); int x,y;

++o1; // increment an object o1.get_xy(x,y); cout<<"(++o1) X: "<<x<<", Y: "<<y<<"\n";

return 0; } Since the increment operator is designed to increase its operand by 1, the overloaded ++ modifies the object it operates upon. The function also returns the object that it increments. This allows the increment operator to be used as part of a larger statement, such as this: V ton t tng c to ra lm tng ton hng ln mt n v nn Np chng ton t ++ s lm thay i i tng m n tc dng. Hm cng tr v i tng khi n tng. iu ny cho php ton t tng c s dng nh mt phn ca cu lnh ln hn, chng hn: o2 = ++o1; As with the binary operators, there is no rule that says you must overload a unary operator so that it reflects its normal meaning. However, most of the time this is what you will want to do. Vi cc ton t n nguyn, khng c quy lut cho rng bn phi Np chng ton t n nguyn sao cho n phn nh ngha bnh thng ca n. Tuy nhin, y chnh l nhng g bn cn tm. In early versions of C++, when an increment or decrement operator was overloaded, there was no way to determine whether an overloaded ++ or preceded or followed its operand. That is, assuming the preceding program, these two statements would have been identical: Trong cc n bn trc y ca C++, khi Np chng mt ton t tng hoc gim, khng c cch xc nh ton t Np chng ++ hay ng trc hay sau ton hng. Ngha l gi s trong chng trnh trc, hai cu lnh ny s ging nhau:

345

o1++; ++o1; However, the modern specification for C++ has defined a way by which the compiler can distinguish between these two statements. To accomplish this, create two versions of the operator++() function. The first is defined as shown int the preceding example. The second is declared like this: Tuy nhin, c t mi i vi C++ nh ngha cch m nh trnh bin dch c th phn bit hai cu lnh ny. thc hin iu ny, hy to ra hai dng hm operator++(). Dng th nht c nh ngha nh trong v d trn y. Dng th hai c khai bo nh sau: coord coord::operator++(int notused); If the ++ precedes its operand, the operator++() function is called. However, if the ++ follows its operand, the operator++(int notused) function is used. In this case, notused will always be passed the value 0. therefore, if the difference between prefix and postfix increment or decrement is important to your class objects, you will need to implement both operator functions. Nu ++ ng trc ton hng th hm operator++() c gi. Tuy nhin, nu ++ ng sau ton hng th hm operator++(int notused) c s dng. Trong trng hp ny, notused s lun lun c truyn gi tr 0. Do , nu s khc bit gia s tng (hoc gim) trc v sau l quan trng i vi cc i tng ca lp, bn s cn thc hin c hai ton t. As you know, the minus sign is both a binary and a unary operator in C++. You might be wondering how you can overload it so that it retains both of these uses relative to a class that you create. The solution is actually quite easy: you simply overload it twice, once as a binary operator and once as a unary operator. This program shows how: Nh bn bit, du tr l ton t nh nguyn ln n nguyn trong C++. Bn c th t hi lm cch no bn c th Np chng sao cho n vn gi nguyn li c hai tnh cht ny i vi lp do bn to ra. Gii php tht s hon ton d: bn ch Np chng n hai ln, mt ln nh ton t nh nguyn v mt ln nh ton t n nguyn. y l chng trnh ch r iu : // Overload - relative to coord class #include "iostream"

346

using namespace std;

class coord { int x,y; // coordinate values public: coord() { x = 0; y = 0; } coord (int i, int j) { x = i; y = j; } void get_xy(int &i, int&j) { i = x; j = y; } coord operator -(coord ob2); // binary minus coord operator -(); }; // unary minus

// Overload - for coord class coord coord::operator -(coord ob2)

347

{ coord temp;

temp.x = x - ob2.x; temp.y = y - ob2.y;

return temp; }

// Overload unary - for coord class coord coord::operator -() {

x = -x; y = -y;

return *this; }

int main() { coord o1(10, 10), o2(5, 7); int x,y;

o1 = o1 -o2;

348

o1.get_xy(x,y); cout<<"(o1 - o2) X: "<<x<<", Y: "<<y<<"\n";

o1 = -o1; // negation o1.get_xy(x,y); cout<<"(-o1) X: "<<x<<", Y: "<<y<<"\n";

return 0; }

As you can see, when the minus is overloaded as a binary operator, it takes one parameter. When it is overloaded as a unary operator, it takes no parameter. This difference in the number of parameters is what makes it possible for the minus to be overloaded for both operations. As the program indicates, when the minus sign is used as a binary operator, the operator-(coord ob2) function is called. When it is used as a unary minus, the operator-() function is called. Nh bn thy, khi du tr c Np chng nh mt ton t nh nguyn, n chn mt tham s. Khi n c Np chng nh ton t n nguyn, n khng chn tham s. S khc bit ny v tham s l iu lm cho du tr c Np chng i vi hai php ton. Nh chng trnh ch r, khi du tr c dng nh ton t nh nguyn, hm operator-(coord ob) c gi. Khi n c dng nh ton t n nguyn, hm operator() c gi.

EXERCISES(BI TP)
overload the - - operator for the coord class. Create both its prefix and postfix forms. Np chng ton t -- i vi lp coord. To c hai dng ng trc v ng sau. Overload the + operator for the coord class so that it is both a binary operator (as shown earlier) and a unary operator. When it is used as a unary operator, have the + make any negative coordinate value positive. Np chng ton t + i vi lp coord cho n l ton t nh nguyn (nh

349

trc y) ln ton t n nguyn. Khi c dng nh mt ton t n nguyn, hy cho ton t + thc hin mt gi tr ta m bt k thnh dng.

6.5. USING FRIEND OPERATOR FUNCTION - S DNG HM TON T FRIEND


As mentional at the start of this chapter, it is possible to overload an operator ralative to a class by using a friend rather than a member function. As you know, a friend function does not have a this pointer. In the case of a binary operator, this means that a friend operator function is passed both operands explictily. For unary operator , the single operand is passed. All other things being equal, there is no reason to use a friend rather than a member operator function, with one important exception, which is discussed in the examples. Nh ni trong phn u ca chng ny, c th Np chng ca mt ton t i vi lp bng cch dng hm friend hn l hm thnh vin member. Nh bn bit, hm friend khng c con tr this. Trong trng hp ton t nh nguyn, iu ny c ngha l mt hm ton t friend c truyn c hai ton hng. i vi cc ton t khc, khng c l do g s dng hm ton t friend hn l hm ton t thnh vin, vi mt ngoi l quan trng, s c tho lun trong v d. Remember: You cannot use a friend to overload the assignment operator. The assignment operator can be overloaded by a member operator function. Cn Nh:Bn c th dng mt hm friend Np chng mt ton t gn. Ton t gn ch c Np chng bi hm ton t thnh vin.

EXAMPLES:(Cc V D)

Here operator+() is overloaded for the coord class by using a friend function:

y, operator+() c Np chng hm i vi lp coord bng cch dng hm friend.

//Overload the - relative to coord class using a 350

friend. #include <iostream> using namespace std;

class coord { int x,y; //coordinate values public: coord() { x = 0; y = 0; } coord(int i, int j) { x = i; y = j; } void get_xy(int &i, int &j) { i = x; j = y; } friend coord operator+(coord ob1, coord ob2); }; 351

// Overload using a friend. coord operator+(coord ob1, coord ob2) { coord temp;

temp.x = ob1.x + ob2.x; temp.y = ob1.y + ob2.y; return temp; } int main() { coord o1(10, 10), o2(5, 3 ), o3; int x,y;

o3 = o1 + o2; // add two object - this calls operator+() o3.get_xy(x, y); cout<< "(o1 + o2 ) X: " << x << , Y: << y <<"\n"

return 0; } Notice that the left operator os passed to the first parameter and the rigth operator is passed to the second parameter.

352

Ch rng ton t hng bn tri c truyn cho tham s th nht v ton hng bn phi c truyn cho tham s th hai.

Overloading an operator provides one very important feature that member function do not. Using a friend operator function, you can allow object to be use in operations involing built-in types in which the built-in types is on the left side of the operator. As you saw earlier in this chapter, it is possible to overload a binary member operator function such that the left operand is an object and the right operand is a built-in types. But it is not possible to use a member function to allow the built-in type to occur on the left side of the operator. For example, assuming an overload member operator function, the first statement show here is legal; the second is not:

Vic Np chng ca mt ton t bng cch dng mt hm friend cung cp mt c im rt quan trng m mt hm thnh vin khng th c c. Bng cch dng ton t friend, bn c th cho nhng i tng c s dng trong cc php ton t c kiu nh sn, kiu nh sn mn bn tri ca ton t. Nh bn thy trong phn u ca chng ny, c th Np chng mt hm ton t thnh vin nh nguyn cho ton hng bn tri l i tng v ton hng bn phi l kiu nh sn. Nhng khng th s dng hm thnh vin cho kiu nh sn nm bn tri ton t. V d, gi s mt hm ton t thnh vin c Np chng, cu lnh u tin trong phn sau y l ng trong lc th hai th khng ng.

ob1 = ob2 + 10; // legal ob1 = 10 + ob2 ; // illegal

While it is possible to organize such statements like the first, always having to make sure that the object is on the left side of the operand and the built-in type on the right can be a cumbersome restriction. The solution to this problem is to make the overloaded operator function friends and define both possible situations.

353

Trong khi c th sp xp cc cu lnh nh cu lnh th nht lun bo m rng i tng bn tri ton hng v kiu nh sn bn phi ton hng th s sp xp nh th gp tr ngoi cng knh. Gii php cho vn ny l hm cho cc hm ton t c Np chng tr thnh cc friend.

As you know, a friend operator is explicitly passed both operands. Thus, it is possible to define one overloaded friend function so that the left operand is an object and the right operand is the other type. Then you could overload the operator again with the left operand being the built-in type and the right operand being the object. The following program illustrantes this method:

Nh bn bit , mt hm ton t friend c truyn c hai ton hng. Do c th nh ngha mt hm friend Np chng sao cho ton hng bn tri l mt i tng v ton hng bn phi l kiu khc. Sau , Np chng ton t ny ln na vi ton hng bn tri l kiu nh sn v ton hng bn phi l i tng. Chng trnh sau minh ha phng php ny:

//Use friend operator function to add flexiblity #include <iostream> using namespace std;

class coord { int x,y; //coordinate values public: coord() { x = 0; y = 0;

354

} coord(int i, int j) { x = i; y = j; } void get_xy(int &i, int &j) { i = x; j = y; } friend coord operator+(coord ob1, int i); friend coord operator+(int i, coord ob1); };

// Overload using a friend. coord operator+(coord ob1, int i) { coord temp;

temp.x = ob1.x + i; temp.y = ob1.y + i; return temp; }

355

// Overload for int + ob1 coord operator+(int i, coord ob1) { coord temp;

temp.x = ob1.x + i; temp.y = ob1.y + i; return temp; } int main() { coord o1(10, 10); int x,y;

o1 = o1 + 10; // object + integer o3.get_xy(x, y); cout<< "(o1 + 10 ) X: " << x << ", Y: " << y <<"\n";

o1 = 99 + o1; // integer + object o3.get_xy(x, y); cout<< "(99 + o1 ) X: " << x << ", Y: " << y <<"\n";

return 0; }

356

As a result of overloading friend operator function for both situation, both of these statements are now vail:

L kt qu ca vic Np chng hm ton t friend i vi cc hai trng hp, c hai cu lnh ny by gi ng. O1 = o1 + 10; O1 = 99 + o1;

If you want to use a friend operator to overload either the ++ or - - unary operator, you must pass the operand to the function as a reference parameter. This is because friend function do not have this pointers. Remember that the increment and decrement operators imply that the operand will be modified. However, if you overload these operators by using a friend that uses a value parameter, any modification that occur to the parameter inside the friend operator function will not affect to the object that generated the call. And sine no pointer to the object is passed implicitly ( that is, there is no this pointer ) when a friend is used, there is no way for the increment or decrement to affect the operand.

Nu bn mun dng mt hm ton t friend Np chng n nguyn ++ hoc ton t n nguyn --, bn phi truyn ton hng cho hm nh tham s quy chiu. Diu ny do cc hm friend khng c con tr this. Nh rng cc ton t tng v gim ch ra rng ton hng s c thay i. Tuy nhin nu bn Np chng cc ton hng ny bng cch dng friend th ton hng c truyn bi mt gi tr nh tham s. Do , nhng thay i bt k i vi tham s bn trong ton t friend s khng lm nh hng n i tng m i tng ny to ra li gi. V v khng c con tr v i tng c truyn mt cch tng minh(ngha l khng c con tr this) khi mt friend c dng nn khng c s tng hoc gim no tc ng n ton hng.

However, if you pass the operand to the friend reference parameter, changes tha occur inside the friend function affect the object that generates

357

the call. For example, here is a program that overloads the ++ operator by using a friend function:

Tuy nhin, bng cch truyn ton hng cho friend nh mt tham s tham chiu, nhng thay i xy ra bn trong hm friend c nh hng n i tng to ra li gi. v d,y l chng trnh Np chng ton t ++ bng cch dng mt hm friend.

//Overload the ++ using a friend #include <iostream> using namespace std;

class coord { int x,y; //coordinate values public: coord() { x = 0; y = 0; } coord(int i, int j) { x = i; y = j; } void get_xy(int &i, int &j) 358

{ i = x; j = y; } // Overload ++ using a friend. friend coord operator++(coord &ob); };

// Overload using a friend. coord operator++(coord &ob) //Use reference parameter { ob.x++; ob.y++;

return ob; //return object generating the call }

int main() { coord o1(10, 10); int x,y;

++o1;

//o1 is passed by reference

o1.get_xy(x, y);

359

cout<< "(++o1) X: " << x << ", Y: " << y <<"\n";

return 0; }

If you using a modern compiler, you can also distinguish between the prefix and posfix forms of the increment or decrement operators when using a friend operator in much the same way you did when using member functions. You simply add an integer parameter when defining the postfix version. For example, here are the prototypes for both the prefix and posfix versions of the increment operator relative to the coord class:

Nu hm dng trnh vin dch mi, bn cng c th phn bit gia cc dng ng trc v ng sau ca cc ton tng hay gim khi bn dng hm ton t friend nh bn dng vi cc hm thnh vin. Bn ch b sung tham s nguyn khi nh ngha dng ng sau. V d, y l cc nguyn mu cho c hai dng ng trc v ng sau ca ton t tng i vi lp coord.

coord operator++(coord &ob);

//pradix

coord operator++(coord &ob,int notuse);

If the ++ precedes its operand,the operator++(coord &ob ) function is calles. However, if the ++ follows its operand, the operator ++(coord &ob, int notused ) function is used. In this case notused will passed the value 0.

Nu ++ ng trc ton hng ca n, hm operator++(coord &ob) c gi.Tuy nhin, nu ++ ng sau ton hng ca n th hm operator++(coord &ob, int notuse) c s dng. Trong trng hp ny, notuse s c truyn gi tr 0.

360

EXERCISES: (Bi Tp) Overload the and / operator for the coord class using friend function.

Np chng cc ton t - v / i lp coord dng cc hm friend.

Overload the coord class so it can use coord object in operations in which an integer value can be multiplied by each coordinate. Allow the operations to use the either order: ob int or int ob.

Np chng ca lp coord s dng cc i tng coord trong cc php ton m gi tr nguyn c nhn vi mi ta , cho php cc php ton dng th t ca n : ob *int hoc int *ob

Explain why the solution to Exercise 2 reqires the use of the friend operator functions?

Gii thch ti sao li i vi bi tp 2 cn dng cc hm ton t friend ?

Using a friend, show how to overload the - - relative to the coord class. Define both the prefix and postfix froms.

S dng friend, hy Np chng ton t -- i vi lp coord. nh ngha c hai dng ng trc v ng sau.

361

6.6. A CLOSER LOOK AT THE ASSIGNMENT OPERATOR -

Mt Ci Nhn V Ton T Gn
As you have seem, it is possible to overload the assignment operator relative to a class. By defualt, when the assignment operator is applied to an object, a bitwise copy of the object on the right os put into the object on the left. If this is what you want, there is no reason to provide your own operator=() function. However, there are cases in which a strict bitwise copy is not desirable. You saw some example of this in Chapter 3, in case in which an object allocates memory. In these types of situations, you will want to provide a special assignment operation.

Nh bn thy, c th Np chng mt ton t gn i vi mt lp. Theo mc nh, khi mt ton t gn c p dng cho mt i tng th mt bn sao tng bit c t vo trong i tng bn tri. Nu y l iu bn mun th khng c l do g a ra hm operator=(). Tuy nhin, c nhng trng hp m bn sao tng bit chnh xc l khng cn. Bn gp mt s v d trong chng 3, nhng trng hp khi mt i tng s dng b nh. Trong nhng trng hp ny bn mun c mt php gn c bit.

Here is another version of the strtype class that you have seen in various forms in the preceding chapters. However, this version overloads the = operator so that the pointer p is not overwritten by an assignment operator.

y l mt phin bn ca lp strtype gp di nhiu dng khc nhau trong cc chng trong cc chng trc. Tuy nhin, phin bn ny Np chng ton t = cho con tr p khng b vit ln bi mt php gn.

#include <iostream> #include <cstring> #include <cstdlib> using namespace std;

362

class strtype { char *p; int len; public: strtype(char *s); ~strtype() { cout<< "Freeing " << (unsigned) p <<'/n'; delete [] p; } char *get() { return p; } strtype &operator=(strtype &ob); }; strtype::strtype(char *s) { int l; l = strlen(s) + 1; p = new char[1]; if(!p) { cout<< "Allocation error \n"; 363

exit(1); } len = 1; strcpy(p, s); } // Assign an object strtype &strtype::operator =(strtype &ob) { // see if more is needed if(len < ob.len) { // need to allocate more memory delete []p; p = new char (ob.len); cout<< " Allocation error \n"; exit(1); } len = ob.len; strcpy (p, ob.p); return *this; } int main() { strtype a("Hello"), b("There");

364

cout<< a.get() << '\n'; cout<< b.get() << '\n';

a = b; // now p is not overwritten

cout<< a.get() << '\n'; cout<< b.get() << '\n';

return 0; }

As you see, the overloaded assignment operator prevents p form being overwritten. If first checks to see if the object on the left has allocated enough memory is freed and another portion is allocated. Then the string is copied to that memory and the length is copied into len.

Nh bn thy, ton t gn c Np chng ngn cho p khng b vit ln. Trc ht n kim tra xem i tng bn tri cp b nh gi chui c gn cho n cha. Nu cha, th b nh s c gii thot v phn khc s c cp pht. Sau chui c php sao chp vo b nh v di sao chp vo len.

Notice two other important features about the operator=() function. First, it takes a reference parameter. This prevents a copy of object on the right side of the assignment form being made. As you know from previous chapters,when a copy is destroyed when the function terminaters. In this case, destroying the copy would call the destructor function, which would free p. However, this is the same p still needed by the object used as an argument. Using a reference parameter prevents this problem.

365

Ch c hai c im quan trng v hm operator=(). Th nht, n c mt tham s tham chiu. iu ny ngn nga s thc hin mt bn sao ca i tng bn phi ca php gn. Nh bn bit t chng trc, khi bn sao ca mt i tng c thc hin khi c truyn mt hm th bn sao ca n s b hy khi hm kt hm. Trong trng hp ny bn sao c gi hm hy gii phng p. Tuy nhin p vn cn cn thit i tng dng nh mt i s. Dng tham s tham chiu ngn nga c vn ny.

The second important of the operator=() function is that it returns a reference parameter, not an object. The reason for this is the same as the reason it uses a reference parameter. When a function returns an object, a temporary object is created that is destroyed after the return is complete. However,this means that the temporary objects destructor will be called, causing p to be freed, but p ( and the memory it points to ) is still needed by the object being assigned a value. Therefore, by returning a reference, you prevent a temporary object from being created.

c im quan trng th hai ca hm operator=() l n tr v mt tham chiu ch khng phi i tng. L do ny ging nh l do s dng tham s tham chiu. Khi mt hm tr v mt i tng th mt i tng tm thi c to ra v s t hy khi s tr v hon tt. Tuy nhin iu ny c ngha l hm hy ca i tng tm thi s c gi, gii phng p, nhng p ( v b nh m n tr v) vn cn vn cn cn thit do i tng c gn gi tr. Do , bng cch tr v mt tham chiu, bn c th ngn c mt i tng tm thi to ra.

Note:

As you learned in Chapter 5, creating a copy constructor is another way to prevent both of the problems desribed in the precding two paragraghs. But the copy constructor might not be as efficient a solution as using a reference parameter and a reference return type. This is because using a reference prevents the overhead assicitated with copying an object in either circumstance. As you can see, there are often several ways to accomplish the same end in C++. Learning to choose between them is part of becoming an excenllent C++ programmer.

Lu : Nh bn bit trong chng 5, vic lp mt hm to sao chp l mt cch khc ngn c hai vn c m t trong hai on chng trnh trn 366

y. Nhng hm sao chp khng phi l gii php c hiu qu nh s dng tham s tham chiu v kiu tr v mt tham chiu. iu ny do tham chiu ngn c cc th tc b sung kt hp. Nh bn c th bit, trong C++ thng c nhiu mc ch. Bit chn ta cch no chnh l cng vic tr thnh ngi lp trnh gii.

EXERCISE: (BI TP)

Given the following class declaration, fill in all details that will create a dynamic array type. That is, allocate memory for the array, strong a pointer to this memory in p. Store the size of the array, in bytes, in size. Have put() return a reference to the specified element. Dont allow the boundaries of the array to be overrun. Also, overload the assignment operator so that the allocated memory of each array is not accidentally destroyed when one array is assigned to another. (In the next section you will see a way to improve your solution to this exercise.)

Cho khai bo lp di y, hy thm vo cc chi tit to nn mt kiu mng an ton. Cng nh vy, hy Np chng ton t gn cho b nh c cp pht ca mt mng khng b hy tnh c. (Tham kho chng 4 tr thnh khng nh cch to mt mng an ton)

class dynarray { int *p; int size; public: dynarray(int s);// pass size of array in s int &put(int i);//return reference to element i int get(int i); 367 // return value of

Selement i // create operator = () function };

6.7. OVERLOADING THE [ ] SUBSCRIPT OPERATOR QU TI CA TON T [ ] CH S DI

The last operator that we will overload is the [ ] array subscripting operator. In C++, the [ ] is considered a binary operator for the purposes of overloading. The [ ] can be overloaded only by a member function. Therefore, the general form of a member operator [ ] () function is as show here:

Trc ton t ch s chng ta c th Np chng ca ton t [ ] ch s di trong mng. Trong C++, ton t [ ] c th coi nh l ton t nh nhn thay cho mc ch ca Np chng. Ton t [ ] c th l Np chng mt hm chc nng. Bi vy, tng quan chc nng ca cc hm thnh vin operator[ ] l s trnh by y.

type class-name::operator [](int index) { // }

Technically, the parameter does not have to be of type int , but operator [ ] ( ) functions are typically used to provide array subscripting and as an integer value is generally used.

368

K thut, tham s khng phi l kiu d liu int, nhng ton t hm operator [ ] () chc nng in hnh in hnh dng cung cp mng ch s di v mt gi tr s nguyn c s dng.

To understand how the [ ] operator works, assume that an object called O is indexed as shown here:

bit ton t [ ] operator c dng nh th no, gi thit mt i tng gi O mt l mt ch s ha th trnh by di y:

o[9]

This index will translate into the following call to the operator [ ] ( ) function:

Ch s c th thng qua trong li gi hm ton t operator [] ():

o.operator [](9)

That is, the value of the expression within the subscripting operator is passed to the operator [] () function in its explicit parameter. The this pointer will point to o, the object that generated the call.

Ngha l, gi tr ca biu thc bn trong ton t ch s di c chuyn cho hm operator [ ]() vi tham s r rng. Con tr c th ch ti O , i tng m khi c gi pht sinh.

EXAMPLES (V D)

369

In the following program, arraytype declares an array of five integers. Its constructor function initializes each member of the array. The overload operator [ ] ( ) function returns the value of the element specified by its parameter.

Trong chng trinh sau y, mng c khai bo vi chiu di 5. Mng xy dng cha cc con s ca mng. Hm operaptor [ ]( ) c chc nng tr li cc gi tr phn t c ch bi cc tham s ca n.

#include <iostream> using namespace std;

const int SIZE = 5;

class arraytype { int a[SIZE]; int i; public: arraytype() { for(i = 0; i < SIZE; i++) a[i] } int operator [](int i) { = i;

370

return a[i]; } };

int main() { arraytype ob; int i;

for( i = 0; i < SIZE ; i++) cout<< ob[i] << " ";

return 0; }

The program display the following output:

Kt qu sau khi chy chng trnh ;

0 1 2 3 4

The initialization of the array a by the constructor in this and the following programs is for the sake of illustration only. It is not required.

S khi to ca mt mng bi vic xy dng trong v chng trnh sau minh ha khng r mc ch .N khng ng yu cu. 371

It is possible to design the operator [ ] ( ) function in such a way that the [] can be on both the left and right sides of an assignment statement. To do this, return a reference to the element being indexed. For example, this program makes this change and illustrates its use:

Chng trnh c th thc hin thit k bi hm operator [ ] () trong mt [] c th c bn tri v bn phi ca bn cnh mt cu lnh ch nh. V d, chng trnh di lm thay i v minh ha v cch dng n.

#include <iostream> using namespace std;

const int SIZE = 5;

class arraytype { int a[SIZE]; int i; public: arraytype() { for(i = 0; i < SIZE; i++) a[i] } int &operator [](int i) { = i;

372

return a[i]; } };

int main() { arraytype ob; int i;

for( i = 0; i < SIZE ; i++) cout<< ob[i] << " ";

cout<< "\n";

//add 10 to each element in the array for( i = 0;i < SIZE; i++) ob[i] = ob[i] + 10; // on left of =

for( i = 0; i < SIZE ; i++) cout<< ob[i] << " ";

return 0; }

This program display the following output:

373

Kt qu sau khi chy chng trnh :

0 1 2 3 4 10 11 12 13 14

Because the operator [ ] ( ) function now returns a reference to the array element indexed by i, it can be used on the side of an assignment to modify an element of the array. (Of course, it can still be used on the right side as well). As you can see, this makes object of arraytype act normal arrays.

Bi v hm operator []() c chc nng tr v gi tr phn t trong mng ti v tr I, N c th c s dng bn cnh mt ch nh vic sa i mt phn t ca mng.( Tt nhin, n th c s dng bn cnh vn cn ng ). Trong khi bn c th thy, lc ny cu to ca i tng arraytype hnh ng bnh thng ca mng.

One advantage of being able to overload the [ ] operator is that it allows a better means of implementing safe array indexing. Either in this book you saw a simplified way to implement a safe array that relied upon functions such as get() and put() to access the elements of the array. Here you will see a better way to create a safe array that utilizes an overloaded [ ] operator. Recall that a safe array that is an array that is encapsulated within a class that performs bounds checking,this approach prevents the array boundaries form being overrun. By overload the [ ] operator for such an array, you allow it to be accessed just like a regular array.

Mt li th ca hin thn c kh nng Np chng ca [] operator l n cho php nhng phng thc tt hn thc hin vic ch s ca mng.Ci ny trong sch bn thy mt cch n gin ha thc thi mt cch chc chc trong mng a vo nhng chc nng ca get() v put() truy cp nhng phn

374

t trong mng. y bn c th thy tt hn to ra mng vi ton t Np chng [ ] operator. S gi li mt mng l mt mng khai bo trong mt lp m thi hnh gii hn kim tra, cch tip cn ny ngn cn vic trn mng. Bi ton t Np chng [] operator c dng trong mng, bn cho php n truy cp nh mng bnh thng.

To create a safe array, simply add bounds checking to the operator [ ] () function. The operator [ ] ( ) must also return a reference to the element being indexed. For example, this program adds a range check to the previous array program and proves that it works by generating a boundary error:

to ra mt mng an ton, n gin l thm vo vic kim tra trn mng bn trong hm ton t operator []. Hm operator []() phi tr li tham chiu cho phn t ang c ch s ha. V d, chng trnh ny thm mt kim tra phm vi vo chng trnh trc v chng minh n lm vic bi vic pht sinh mt li.

// A safe array example #include <iostream> #include < cstdlib> using namespace std;

const int SIZE = 5;

class arraytype { int a[SIZE]; public: 375

arraytype() { int i; for(i = 0; i < SIZE; i++) a[i] } int &operator [](int i); }; // Provide range checking for arraytype int &arraytype::operator [](int i) { if( i < 0 || i < SIZE -1) { cout<< "\nIndex value of "; cout<< i << " is out of bounds.\n"; exit(1); } return a[i]; } int main() { arraytype ob; int i; = i;

//this is OK

376

for( i = 0; i < SIZE ; i++) cout<< ob[i] << " ";

/* this generates a run-time error because SIZE +100 is out of range */ ob[SIZE+ 100] = 99; //error!

return 0; }

In this program when the statement

Trong chng trnh ny khi khai bo Ob[SIZE + 100 ] = 99;

executes, the boundary error is intercepted by operator [ ] ( ) and the program is terminated before any damage can be done. chy, li di th ng li bi operator [ ]() v chng trnh c hon thnh trc b hng c th chy c.

Because the overloading of the [ ] operator allows you to create safe arrays that look and act just like regular arrays, they can be seamlessly integrated into your programming environment. But be careful. A safe array adds overhead that might not be acceptable in all situations. In fact, the added overhead is why C++ does not perform boundary checking on arrays in the first place. However, in applications in which you want to be sure that a boundary error does not take place, a safe array will be worth the effort.

377

Bi v hm Np chng ca [ ] operator cho php bn to ra mt mng an ton m ci nhn v hnh ng cng nh nhng mng bnh thng, h c th khng c ng ni vo mi trng bn trong chng trnh ca bn. Nhng phi thn trng. Khi thm mt phn t vo u ca mng m khng chp c trong tt c v tr. Tht ra, vic thm vo u l ti sao C++ khng thc hin kim tra di ca mng trong v tr u tin.Tuy nhin, trong ng dng trn bn mun thc hin ng m khng c s bo li khi t r di, mt mng an ton s ng gi n lc .

EXERCISES: (BI TP)

6. Modify Example 1 in Setion 6.6 so that strtype overloads the [ ] operator. Have this operator return the character at the specified index. Also, allow the [ ] to be used on the left side of the assignment. Demonstrate its use.

Hy khai bo li phng thc trong v d 1 mc 6.6 strtype thnh hm Np chng [] operator. Hy tr v cho phng thc mt k t. Ngoi ra, ton t [ ] cng c dng nh hm ph ch nh. Gii thch cc cch dng hm.

7. Modify your answer to Exercise 1 form Section 6.6 so that it uses [ ] to index the dynamic array. That is, replace the get() and put () functions with the [ ] operator.

Hy khai bo li cc phng thc trong v d 1 ca mc 6.6 m s dng ton t [ ] ch s ha mng ng.Khi , thay th s dng chc nng get() v put() cng vi ton t [ ] operator.

SKILLS CHECK KIM TRA K NNG

378

MASTERY SKILLS CHECK (Kim tra ky nng lnh hi):

At this point you should be able to perform the following exercises and answer the questions.

n y bn c th thc hin cc bi tp v tr li nhng cu hi sau:

Overload the >> and << shift operators relative to the coord class so that the following types of operations are allowed:

Np chng ca cc ton t >> v << i vi lp coord cho cc kiu php ton sau y:

ob<< integer ob>> integer

Make sure your operators that shift the x and y values by the amount specified.

Hy lm cho cc ton t ca bn nng cc gi tr x v y ln mt lng c ch r.

Given the class

Cho lp

379

class three_d { int x,y,z; public: three_d(int i, int j, int x) { x = i; y = j; z = x; } three_d() { x = 0; y = 0; z = 0; } void get(int &i, int &j, int &k) { i = x; j = y; k = z; } };

380

overload the +, -, ++, and - - operator for this class (For the incrememt and deccrement operator, overload only the prefix form)

hy np chng ton t +, -, ++ v i vi lp ny (i vi cc ton t tng v gim, ch np chng theo dng ng trc)

Rewrite your answer to Question 2 si that it uses reference parameters instead of value parameters to the operator functions. (Hint : you will need to use friend functions for the increment and operators.)

Vit li phn tr li cho bi tp 2 n dng cc tham s tham chiu thay v cc tham s gi tr i vi cc hm ton t.( Hng dn : Bn cn dng cc hm friend cho cc ton t tng v gim )

How do friend operator function differ from member operator function ?

Cc hm ton t friend khc cc hm ton t thnh vin nh th no ?

Explain why you might need to overload the assignment operator.

Hy gii thch ti sao bn cn phi Np chng ton t gn.

Can operator=() be a friend function?

Operator = () c th l mt hm friend ?

Overload the + for the three_d class in Question 2 so that it accepts the 381

following types of operator:

Np chng ton t + i vi lp three_d cho n nhn cc kiu php ton sau:

ob + int ; int + ib;

Overload the ==, !=, and || operator relative to the three_d class from Question 2.

Np chng cc ton t ==, !=, v operator i vi lp three_d

Explain the mian reason for overloading the [ ] operator.

Gii thch l do khai bo ton t [ ] operator trong main .

Cumulative Skills Ckeck


Kim tra k nng tng hp:

This section checks how well you have integrated materrial in this chapter with that from the preceding chapters.

Phn ny kim tra xem kh nng ca bn kt hp chng ny vi chng trc nh th no.

382

Create a strtype class that allows the following types of operator:

To lp strtype cho php cc kiu ton t sau:

string concatenation using the + operator. Ghp chui bng cch dng ton t +. string assignment using the = operator. Gn chui bng cch dng ton t =. string comparisons using < , >, and = =. So sanh chui bng cch dng cc ton t, v ==.

Feel free to use fixed-length string. This is a challenging assignment, but with some thought (and experimentation), you should be able to accomplish.

C th dng chui c di c nh. y l cng vic c tnh th thch, nhng vi suy ngh ( v kinh nghim thc hnh ), bn c th thc hin bi kim tra ny.

_________________________________________________

383

CHNG 7 INHERITANCE - TNH K THA


Chapter objectives 7.1. BASE CLASS ACCESS CONTROL iu khin truy cp lp c s. 7.2. USING PROTECTED MEMBER S dng cc thnh vin bo v 7.3. CONSTRUCTOR, DESTRUCTORS, AND INHERITANCE Hm to, hm hy, v tnh k tha 7.4. MULTIPLE INHERITANCE Tnh a k tha 7.5. VIRTUAL BASE CLASSES Cc lp nn c s o

You were introduced to the concept of inheritance earlier in thisbook. Now it is expplore it more thoroughly.Inheritance is one of the three principles of OOP and, as such, it is an import feature of C++. Inheritance does more than just support the concept of hierarchical classification; in Chapter 10 you will learn how inheritance provides support for polymorphism, another principal feature of OOP.

Trong phn u ca cun sch, chng ti gii thiu khi nim tnh a tha. By gi chng ti trnh by y hn. Tnh k tha l mt trong ba nguyn l ca vic lp trnh hng i tng (OOP) v l c im quan trng ca C++. Trong C++, tnh a k tha khng ch h tr khi nim phn loi theo cp bc ,m 384

trong chng 10 bn s bit tnh a k tha cn h tr tnh a dng, mt c im quan trng khc ca OOP.

The topics covered in this chapter include base class access control and the protected access specifier, inheriting multiple base classes, passing arguments to base class constructors, and virtual base classes.

Cc ch bao trm trong chng ny gm iu kin truy cp lp c s v ch nh truy cp protected, k tha a lp c s truyn i s cho cc hm to lp c s v cc lp c s o.

Review Skills Check Kim tra k nng n tp

Before proceeding, you should be able to correctly answer the following questions and do the exericeses.

Trc ht khi bt u, bn nn tr li chnh xc cc cu hi v lm cc bi tp sau:

When an operator is overloaded, does it lose any of its otiginal functionality?

Khi no mt ton t c gi Np chng, ton t c mt chc nng gc ca n khng ?

Must an operator be overloaded relative to a user-defined type, such as a class?

385

Mt ton t phi c Np chng i vi lp?

Can the precedence of an overloaded operator be changed? Can the number of operands be altered?

Th t u tin ca cc t c Np chng c thay i khng? S ton hng c thay i khng?

Given the following partilly completed program, fill in the needed operator functions:

Cho chng trnh thc hin mt phn sau y, hy b sung cc ton hng cn thit:

#include <iostream> using namespace std;

class array { int nums [10]; public: array(); void set(int n[10]); void show(); array operator+(array ob2);

386

array operator-(array ob2); int operator==(array ob2); }; array::array() { int i; for(i = 0; i < 10; i++) nums[i] =0; }

void array::set(int *n) { int i; for(i = 0; i < 10; i++ ) nums[i] = n[i]; }

void array::show() { int i; for( i = 0; i < 10; i++) cout<< nums[i] << " ";

cout<< "\n"; }

387

//Fill in operator function

int main() { array o1, o2, o3; int i[10]= { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

o1.set(i); o2.set(i);

o3 = o1 + o2; o3.show();

o3 = o1 - o3; o3.show();

if(o1 == o2) cout<< " o1 equals o2 \n"; else cout<< "o1 does not equals o2\n";

if(o1 == o3) cout<< " o1 equals o3 \n"; else 388

cout<< "o1 does not equals o3\n";

return 0; }

Have the overload + add each element of each operand. Have the overloaded - subtract each element of the right operand from the left. Have the overloaded == return true if each element of each operand is the same and return false otherwise.

Hy cho ton t Np chng + cng mi phn t ca ton hng.Hy cho ton t - tr phn t ton hng bn tri vi mi phn t ca ton hng bn phi. Hy cho ton t Np chng == tr v gi tr ng nu mi phn t ca mi ton hng l ging nhau v tr v gi tr sai nu ngc li.

Convert the solution to Exercise 4 so it overloads the operators by using friend functions.

Chuyn i li gii i vi bi tp 4 cho n Np chng cc ton t bng cch dng cc hm friend.

Using the class and support function from Exercise 4, overload the ++ operator by using a member function and overload the operator by using a friend. ( Overload only the prefix forms of ++ and --.)

Dng lp v hm h tr t bi tp 4, hy Np chng hm ton t ++ bng cch dng hm thanh vin v Np chng ton t -- bng cch dng friend.

389

Can the assignment operator be overloaded by using a friend function ?

C th Np chng ton t gn bng cch dng cc hm friend khng?

1.1. BASE CLASS ACCESS CONTROL IU KHIN TRUY CP LP C S

When one class inherits another, it uses this general form:

Khi mt lp k tha l mt lp khc, n s dng cc dng tng qut:

class derived-class-name:access base-class-name { //. }

Here access is one three keywords: public, private, or protected. A discussion of the protected access specifier is deferred until the next section of this chapter. The other two are discussed here.

y access l mt trong ba lp t kha: public, private, or protected.phn tho lun v ch nh truy cp protected s c trnh by trong phn tip theo ca chng ny. Hai ch nh cn li c trnh by y.

The access dpecifier determines how element of the base class are inherited by

390

the derived class. When the access specifier for the inherited base class is public, all public members of the base become public members of the derived class. If the access specifier is private, all public members of the base become private members of the derived class. In either case, any private members of the base remain private to it and are inaccessible by the derived class. Ch nh truy cp xc nh cch m cc phn t ca lp c s c k tha bi lp dn xut. Khi ch nh truy cp i vi lp c s c k tha l public, th mi thnh vin chung ca lp c s tr thnh cc thnh vin chung ca lp dn xut.Ngc li, cc thnh vin ringca lp co s vn cn thnh vin ring ca n v khng th truy cp lp dn xut.

It is improtant to undertand that if the access specifier is private, public members of the base become private members of the derived class, but these members are still accessible by member function of the derived class.

iu quan trng cn hiu l nu ch nh truy cp la private, th cc thanh vin chung ca lp c s thnh cc thnh vin ring ca lp dn xut nhng nhng thnh vin ny vn c truy cp bi cc hm thnh vin ca lp dn xut.

Technically, access is optional. If the specifier is not present, it is private by defualt if the derived class. If the derived class is a struct, public is the default in the absence of an explicit access specofier. Frankly, most programmers explicitly specify access for the sake of clarity.

V mt k thut, th s truy cp phi la chn. Nu khng c ch r, l private bi lp dn xut l class. Nu lp c dn xut ra mt struct, public l s mc nh thiu trong vic truy xut n.Thc s,mt chng trnh r rng ch nh s truy xut n ch.

EXAMPLES: (V D)

Here is a short base class and a derived class that inherits it (as public): 391

y l mt c s ngn v mt lp dn xut k tha n (nh mt lp chung)

#include <iostream> using namespace std;

class base { int x; public: void setx(int n) { x = n; } void showx() { cout<< x << '\n'; }

};

//Inherit as public class derived: public base { int y; 392

public: void sety(int n) { y = n; } void showy() { cout<< } }; y << '\n';

int main() { derived ob;

ob.setx(10); // access member of base class ob.sety(20); // access member of derived class

ob.showx(); // access member of base class ob.showy(); // access member of derived class

return 0; }

As the program illustrates, because base is inherited as public, the public members of base_set() and showx() become public members of derived 393

and are, therefore accessible by any other part of the program. Specifically, they are legally called within main().

Nh chng trnh minh ha, v base c k tha nh mt lp chung nn cc thnh vi chung ca base_set() v showx() tr thnh cc thnh vin chung ca derived v do c th c truy cp bi bt k phn no ca chng trnh. c bit, chng c gi mt cch hp l trong main().

It is important to understand that just because a derived class inherist a base as public, it does not mean that the derived class has access to the bases private members. For example, this addition to derived from the preceding example is incorrect.

iu quan trng cn bit l do mt lp dn xut k tha mt lp c s nh l chung, khng c ngha l lp dn xut truy cp c n cc thnh vin ring ca lp c s. V d, phn b sung ny i vi derived trong v d trn y l khng ng:

#include <iostream> using namespace std;

class base { int x; public: void setx(int n) { x = n; } 394

void showx() { cout<< x << '\n'; }

};

//Inherit as public - this has an error! class derived: public base { int y; public: void sety(int n) { y = n; } /* Cannot access private member of base class x is a private member of base and not available within derived */ void show_sun() { cout<< x + y << '\n '; //Error ! } void showy()

395

{ cout<< } }; y << '\n';

In this example, the derived class attemts to access x, which is a private member of base. This is an error because the private parts of c base class remain private to it no matter how it is inheried.

Trong v d ny,lp derived th truy cp x, l thnh vin ring base. Tuy nhin y l li sai v phn ring ca lp c s vn l ring ca n cho d n c k tha.

Here is a vartion of the program shown in Example 1; this time derived inherits base as private. This change causes the program to be in error, as indicated in the comments.

y l chng trnh c nu trong v d 1 k tha base nh mt lp ring. S thay i ny lm cho chng trnh c li. Nh c r trong cc li ch gii.

#include <iostream> using namespace std;

class base { int x; public:

396

void setx(int n) { x = n; } void showx() { cout<< x << '\n'; }

};

//Inherit base as private class derived: public base { int y; public: void sety(int n) { y = n; } void showy() { cout<< } }; y << '\n';

397

int main() { derived ob;

ob.setx(10); // ERROR - now private to derived class ob.sety(20); // access member of derived class -Ok

ob.showx(); // ERROR - now private to derived class ob.showy(); class // access member of derived

return 0; }

As the comments in this (incorrect ) program illustrate , both showy() and setx() become private to derived and are not accessible outside of it.

Cc li ch gii trong chng trnh (sai) ny ch ra rng c hai hm showx() v setx() u tr thnh ring i vi derived v khng c truy cp t bn ngoi.

Keep in mind that showx() and setx() are still public within base no mater how they are inherited by some derived class. This means that an object of type base could access these functions anywhere. However, relative to objects of type derived, they become private.

398

iu quan trng hiu l showx() v setx() vn l chung trong base cho d chng c k tha bi lp dn xut no . iu ny c ngha l i tng c kiu base c th truy cp cc hm ny bt k y. Tuy nhin, i vi cc i tng kiu derived, cc hm tr thnh ring.

As stated, even though public members of a base class become private members of a derived class when inherited using the private specifier, they are still accessible within the derived class. For example, here is a fixed version of the preceding program :

For example. Given this fragment: V d . trong on chng trnh sau:

base base_ob;

base_ob.setx(1); // is legal because base_ob is of type base

the call setx() is legal because setx() is public within base. Li gi n setx() l hp l v setx() la f chung trong base.

Nh ni, mc d cc thnh vin chung ca lp c s tr thnh cc thnh vin ring ca lp dn xut khi c k tha bng ch nh private, cc thnh vin vn cn c truy cp bn trong lp dn xut. v d,y l phin bn c nh ca chng trnh trn.

// This program is fixed. #include <iostream>

399

using namespace std;

class base { int x; public: void setx(int n) { x = n; } void showx() { cout<< x << '\n'; }

};

//Inherit base as private class derived: public base { int y; public: // setx is accessible from within derived void setxy(int n, int m) {

400

setx(n); y = m; } // show is accessible from within derived void showxy() { showx(); cout<< } }; y << '\n';

int main() { derived ob;

ob.setxy(10, 20);

ob.showxy();

return 0; }

In this case, the function setx() and showx() are accessed inside the derived class, which is perfectly legal because they are private members of that class.

401

Trong trng hp ny , cc hm setx() v showx() c truy cp bn trong lp dn xut, iu ny th hon ton hp l v chng l thnh vin ring ca lp .

EXERCISES (Bi Tp)

Examine this skeleton: Xt on chng trnh:

#include <iostream> using namespace std;

class mybase { int a,b; public: int c; void setab(int i,int j) { a = i; b = j; } void getab(int &i, int &j ) { i = a; j = b; 402

};

class derived1: public mybase { //... }

class derived2: private mybase { //... }

int main() { derived1 ob; derived1 ob; int i,j ;

//...... return 0; }

Within main(), which of the following statements are legal ?

403

Trong mian() ,cc cu lnh no sau y l hp l?

A.o1.getab(i, j); B.o2.getab(i, j); C.o1.c = 10; C.o2.c = 10;

What happens when a public member is inherited as public? What happens when it is inherited as private?

iu g xy ra khi mt thnh vin ring c k tha nh mt thnh vin chung? iu g xy ra khi n c k tha nh mt thnh vin ring?

If you have not done so, try all the examples presented in this section. On your own, try various changes relative to the access specifiers and observe the results.

Hy chy v d trong phn ny,ty theo bn hy thc hin cc thay i khc nhau i vi cc ch nh truy cp v xem cc kt qu

1.2. USING PROTECTED MEMBERS - S DNG CC THNH VIN C BO V


As you know from the preceding section, a derived class does not have access to the private members of the base class. This means that if the derived class needs access to some member of the base, that member must be public. However, there will be times when you want to keep a member of a base class private but still allow a derived class access to it. To accomplish this goal, C++ includes the protected access specifier. Nh bn bit t phn trn y, mt lp dn xut khng truy cp c cc thnh vin ring

404

ca lp c s. Ngha l nu lp dn xut mun truy cp mt thnh vin no ca lp c s th thnh vin phi l chung. Tuy nhin, s c nhng lc bn mun mt thnh vin ca lp c s vn l ring nhng vn cho php lp dn xut truy cp ti n. thc hin mc ch ny, C++ c ch nh truy cp protected. The protected access specifier is equivalent to the private specifier with the sole exception that protected members of a base class are accessible to members of any class derived from that base. Outside the base or derived classes, protected members are not accessible. Ch nh truy cp protected tng ng vi ch nh private vi mt ngoi l duy nht l cc thnh vin c bo v (protected) ca lp c s c th c truy cp i vi cc thnh vin ca mt lp dn xut t lp c s . Bn ngoi lp c s hoc lp dn xut, cc thnh vin c bo v khng th c truy cp. The protected access specifier can occur anywhere in the class declaration, although typically it occurs after the (default) private members are declared and before the public members. The full general form of a class declaration is shown here: Ch nh truy cp protected nm bt k u trong khai bo lp, mc d n thng nm sau (theo mc nh) cc thnh vin ring c khai bo v trc cc thnh vin chung. Dng y tng qut ca khai bo lp nh sau: class class-name { //private members protected: // optional // protected members public: // public members }; When a protected member of a base is inherited as public by the derived class, it becomes a protected member of the derived class. If the base is inherited as private, a protected member of the base becomes a private member of the derived class. Khi mt thnh vin c bo v ca mt lp c s c k tha bi lp dn xut vi ch nh public, n tr thnh thnh vin c bo v ca lp dn xut. Nu lp dn xut c k tha vi ch nh private, th mt thnh vin c bo v ca lp c s tr thnh thnh vin ring ca lp dn xut.

405

A base class can also be inherited as protected by a derived class. When this is the case, public and protected members of the base class become protected members of the derived class. (Of course, private members of the base class remain private to it and are not accessible by the derived class.) Lp c s c th c k tha vi ch nh protected bi lp dn xut. Trong trng hp ny, cc thnh vin chung v c bo v ca lp c s tr thnh cc thnh vin c bo v ca lp dn xut. (D nhin cc thnh vin ring ca lp c s vn cn ring i vi lp c s v khng c truy cp bi lp dn xut). The protected access specifier can also be used with structures. Ch th truy cp protected cng c dng vi cu trc v hi.

EXAMPLES(CC V D)
This program illustrates how public, private, and protected members of a class can be accessed:

Chng trnh ny minh ha cch m cc thnh vin chung, ring v c bo v ca mt lp c th c truy cp:

#include <iostream> using namespace std;

class samp { // private by default int a; protected: // still private relative to samp int b;

406

public: int c;

samp (int n, int m) { a = n; b = m; } int geta() { return a; } int getb() { return b; } };

int main() { samp ob (10, 20);

// ob.b = 99; Error! b is protected and thus private ob.c = 30; // OK, c is public

407

cout << ob.geta () << ; cout << ob.getb () << << ob.c << \n;

return 0; }

As you can see, the commented-out line is not permissible in main() because b is protected and is thus still private to samp.

Nh bn c th thy, dng c ch gii l khng c php trong main() v b c bo v v do vn cn ring i vi samp.

The following program illustrates what occurs when protected members are inherited as public:

Chng trnh sau minh ha iu g xy ra khi cc thnh vin c bo v c k tha nh cc thnh vin chung:
#include <iostream> using namespace std; class base { protected: // private to base int a, b; // but still accessible by derived public: void { setab(int n, int m)

408

a = n; b = m; } };

class derived:public base { int c; public: void setc(int n) { c = n; } // this function has access to a and b from base void showabc() { cout << a << << b << << c << \n; } }; int main() { derived ob; /* a and b are not accessible here because they are private to both base and derived. */ ob.setab(1, 2); ob.setc(3);

409

ob.showabc ();

return 0; }

Because a and b are protected in base and inherited as public by derived, they are available for use by member functions of derived. However, outside of these two classes, a and b are effectively private and unaccessible. V a v b c bo v trong base v c k tha nh thnh vin chung bi derived, nn chng c hiu lc cho s dng bi cc hm thnh vin ca derived. Tuy nhin, bn ngoi hai lp ny, a v b hon ton l ring v khng th truy cp c.

As mentioned earlier, when a base class is inherited as protected, public and protected members of the base class become protected members of the derived class. For example, here the preceding program is changed slightly, inheriting base as protected instead of public:

Nh ni trc y, khi mt lp c s c k tha nh protected, cc thnh vin c bo v v chung ca lp c s tr thnh cc thnh vin c bo v ca lp dn xut. V d, sau y l chng trnh c sa i li cht t t chng trnh trn y:

// This program will not compile. #include <iostream> using namespace std;

class base { protected: // private to base int a, b; // but still accessible by derived

410

public: void setab(int n, int m) { a = n; b = m; } };

class derived : protected base { // inherit as protected int c; public: void setc (int n) { c = n; } // this function has access to a and b from base void showabc() { cout << a << << b << << c << \n; } };

int main() 411

{ derived ob;

// ERROR: setab() is now a protected member of base. ob.setab(1, 2); // setab() is not accessible here.

ob.setc(3);

ob.showabc();

return 0; }

As the comments now describe, because base is inherited as protected, its public and protected elements become protected members of derived and are therefore inaccessible within main().

Nh m t trong cc li ch gii, do base c k tha nh protected nn cc thnh vin chung v c bo v ca n tr thnh cc thnh vin c bo v ca derived v do khng th truy cp c trong main().

EXERCISES(BI TP)
What happens when a protected member is inherited as public? What happens when it is inherited as private? What happens when it is inherited as protected?

iu g xy ra khi mt thnh vin c bo v li c k tha nh thnh vin chung? iu g xy ra khi n c k tha nh mt thnh vin ring? iu g xy ra khi n c k tha nh mt thnh vin c bo v? 412

Explain why the protected category is needed?

Gii thch ti sao cn n phm tr c bo v?

In Exercise 1 from Section 7.1, if the a and b inside myclass were made into protected instead of private (by default) members, would any of your answers to that exercise change? If so, how?

Trong bi tp 1 ca phn 7.1, nu a v b bn trong myclass c thc hin tr thnh nhng thnh vin c bo v thay v cc thnh vin ring (theo mc nh) th phn gii p ca bn c thay i khng? Ti sao?

1.3. CONSTRUCTORS, DESTRUCTORS, AND INHERITANCE - HM TO, HM HY V TNH K THA


It is possible for the base class, the derived class, or both to have constructor and or destructor functions. Several issues that relate to these situations are examined in this section. Lp c s, lp dn xut hoc c hai c th c cc hm to v/hoc hm hy. Nhiu vn c lin quan n cc trng hp ny c kho st trong phn ny.

413

When a base class and a derived class both have constructor and destructor functions, the constructor functions are executed in order of derivation. The destructor functions are executed in reverse order. That is, the base class constructor is executed before the constructor in the derived class. The reverse is true for destructor functions: the derived classs destructor is executed before the base classs destructor. Khi c lp c s ln lp dn xut c cc hm hy v to, cc hm to c thi hnh theo th t dn xut. Cc hm hy c thi hnh theo th t ngc li. Ngha l, hm to ca lp c s c thi hnh trc hm to ca lp dn xut. iu ngc li th ng cho cc hm hy: hm hy ca lp dn xut c thi hnh trc hm hy ca lp c s. If you think about it, it makes sense that constructor functions are executed in order of derivation. Because a base class has no knowledge of any derived class, any initialization it performs is separate from and possibly prerequisite to any initialization performed by the derived class. Therefore, it must be executed first. Nu bn ngh v iu ny, cc hm to c thi hnh theo th t dn xut. Bi v lp c s khng nhn bit lp dn xut, bt k khi u no do lp c s thc hin l khc bit vi khi u do lp dn xut thc hin. Do hm to ca lp c s phi c thc hin trc. On the other hand, a derived classs destructor must be executed before the destructor of the base class because the base class underlies the derived class. If the base classs destructor were executed first, it would imply the destruction of the derived class. Thus, the derived classs destructor must be called before the object goes out of existence. Ngc li, hm hy ca lp dn xut phi c thi hnh trc hm hy ca lp c s bi v lp c s nm di lp dn xut. Nu hm hy ca lp c s c thi hnh trc th s n lp dn xut b hy. Do hm hy ca lp dn xut phi c gi trc khi i tng khng cn tn ti. So far, none of the preceding examples have passed arguments to either a derived or base class constructor. However, it is possible to do this. When only the derived class takes an initialization, arguments are passed to the derived classs constructor in the normal fashion. However, if you need to pass an argument to the constructor of the base class, a little more effort is needed. To accomplish this, a chain of argument passing is established. First, all necessary arguments to both the base class and the derived class are passed to the derived classs constructor. Using an expanded form of the derived classs constructor declaration, you then pass the

414

appropriate arguments along to the base class. The syntax for passing along an argument from the derived class to the base class is shown here: Khng c v d no trn y truyn i s cho hoc hm to ca lp dn xut hoc hm to ca lp c s. Tuy nhin, c th thc hin c iu ny. Khi ch c lp dn xut thc hin s khi u, i s c truyn cho hm to ca lp dn xut theo cch bnh thng. Tuy nhin, nu cn truyn i s cho hm to ca lp c s th phi thc hin khc i mt t. lm iu ny th cn lp mt chui truyn i s. Trc ht, tt c cc i s cn thit cho c lp c s ln lp dn xut c truyn cho hm to ca lp dn xut. S dng dng m rng ca khai bo hm to ca lp dn xut, cc i s thch hp s c truyn cho lp c s. C php truyn i s t lp dn xut n lp c s nh sau:

derived-constructor (arg-list): base (arg-list) { // body of derived class constructor }

Here base is the name of the base class. It is permissible for both the derived class and the base class to use the same argument. It is also possible for the derived class to ignore all arguments and just pass them along to the base. y base l tn ca lp c s. C lp dn xut ln lp c s c php s dng i s ging nhau. Lp dn xut cng c th b qua mi i s v truyn chng cho lp c s.

EXAMPLES(CC V D)
Here is a very short program that illustrates when base class and derived class constructor and destructor functions are executed:

y l mt chng trnh rt ngn minh ha khi no th cc hm to v hm hy ca lp c s v lp dn xut c thi hnh:

415

#include <iostream> using namespace std;

class base { public: base () { cout << "Constructing base class \n"; } ~base () { cout << "Destructing base class \n"; } };

class derived : public base { public: derived () { cout << "Constructing derived class \n"; } ~derived ()

416

{ cout << "Destructing derived class \n"; } };

int main() { derived o;

return 0; }

This program displays the following output:

Chng trnh ny hin th kt qu sau:

Constructing base class Constructing derived class Destructing derived class Destructing base class

As you can see, the constructors are executed in order of derivation and the destructors are executed in reverse order.

Nh bn c th thy cc hm to c thi hnh theo th t dn xut v cc hm hy c thi hnh theo th t ngc li. 417

This program shows how to pass an argument to a derived classs constructor:

Chng trnh sau cho bit cch truyn mt i s cho hm to ca lp dn xut:

#include <iostream> using namespace std;

class base { public: base() { cout << "Constructing base class \n"; } ~base() { cout << "Destructing base class \n"; } };

class derived : public base { int j; public:

418

derived (int n) { cout << "Constructing derived class \n"; j = n; } ~derived () { cout << "Destructing derived class \n"; } void showj() { cout << j << \n; } };

int main() { derived o(10);

o.showj();

return 0; }

Notice that the argument is passed to the derived classs constructor in the normal fashion. 419

Ch rng i s c truyn cho hm to ca lp dn xut theo cch bnh thng.

In the following example, both the derived class and the base class constructors take arguments. It this specific class, both use the same argument, and the derived class simply passes along the argument to the base.

Trong v d sau, hm to ca lp dn xut ln ca lp c s nhn mt i s. Trong trng hp c th ny, c hai s dng i s ging nhau, v lp dn xut ch truyn i s cho lp c s.

#include <iostream> using namespace std;

class base { int i; public: base (int n) { cout << "Constructing base class \n"; i = n; } ~base() { cout << "Destructing base class \n"; } 420

void showi() { cout << i << \n; } }; class derived : public base { int j; public: derived (int n) : base(n) { // pass arg to base class cout << "Constructing derived class \n"; j = n; } ~derived () { cout << "Destructing derived class \n"; } void showj() { cout << j << \n; } };

421

int main() { derived o(10);

o.showi(); o.showj();

return 0; }

Pay special attention to the declaration of deriveds constructor. Notice how the parameter n (which receives the initialization argument) is both used by derived() and passed to base().

Ch n khai bo hm to ca derived. Ch cch tham s n (nhn i s khi u) c dng bi derived() v c truyn cho base().

In most cases, the constructor functions for the base and derived classes will not use the same argument. When this is the case and you need to pass one or more arguments to each, you must pass to the derived classs constructor all arguments needed by both the derived class and the base class. Then the derived class simply passes along to the base those arguments required by it. For example, this program shows how to pass an argument to the derived classs constructor and another one to the base class:

Trong hu ht cc trng hp, cc hm to i vi lp c s v lp dn xut s khng dng i s ging nhau. Khi trong trng hp bn cn truyn mt hay nhiu i s cho mi lp, bn phi truyn cho hm to ca lp dn xut tt c cc i s m c hai lp dn xut v c s cn n. Sau lp dn xut ch truyn cho lp c s nhng i s no m lp c s cn. V d, chng trnh ny ch ra cch truyn mt

422

i s cho hm to ca lp dn xut v mt i s khc cho lp c s.

#include <iostream> using namespace std;

class base { int i; public: base (int n) { cout << "Constructing base class \n"; i = n; } ~base() { cout << "Destructing base class \n"; } void showi() { cout << i << \n; } };

class derived : public base

423

{ int j; public: derived (int n, int m) : base(m) { // pass arg to base class cout << "Constructing derived class \n"; j = n; } ~derived () { cout << "Destructing derived class \n"; } void showj() { cout << j << \n; } };

int main() { derived o(10, 20);

o.showi(); o.showj();

424

return 0; }

It is not necessary for the derived classs constructor to actually use an argument in order to pass one to the base class. If the derived class does not need an argument, it ignores the argument and simply passes it along. For example, in this fragment, parameter n is not used by derived(). Instead, it is simply passed to base():

iu quan trng cn hiu l i vi hm to ca lp dn xut khng cn phi nhn mt s truyn cho lp c s. Nu lp dn xut khng cn i s, n b qua i s v ch truyn cho lp c s. V d, trong on chng trnh sau, tham s n khng c dng bi derived(). Thay v vy, n ch truyn cho base():

class base { int i; public: base (int n) { cout << "Constructing base class \n"; i = n; } ~base() { cout << "Destructing base class \n"; }

425

void showi() { cout << i << \n; } };

class derived : public base { int j; public: derived (int n) : base(n) { // pass arg to base class cout << "Constructing derived class \n"; j = 0; // n not used here } ~derived () { cout << "Destructing derived class \n"; } void showj() { cout << j << \n; } };

426

EXERCISES(BI TP)

Given the following skeleton, fill in the constructor function for myderived. Have it pass along a pointer to an initialization string to mybase. Also, have myderived() initialize len to the length of the string.

Cho chng trnh sau, hy b sung hm to cho myderived. Hy cho myderived truyn cho mybase mt con tr v mt chui khi u. Cng vy, hy cho myderived() khi u len vi di ca chui.

#include <iostream> #include <cstring> using namespace std;

class mybase { char str [80]; public: mybase (char *s) { strcpy(str, s); } char *get() { return str; 427

} };

class myderived : public mybase { int len; public: // add myderived() here int getlen() { return len; } void show() { cout << get() << \n; } };

int main() { myderived ob("hello");

ob.show(); cout << ob.getlen() << \n;

428

return 0; }

Using the following skeleton, create appropriate car() and truck() constructor functions. Have each pass along appropriate arguments to vehicle. In addition, have car() initialize passengers as specified when an object is created. Have truck() initialize loadlimit as specified when an object is created.

S dng dn chng trnh sau lp cc hm to car() v truck() thch hp. Mi hm truyn cc i s thch hp cho vehicle. Ngoi ra, cho car() khi u passengers nh c ch r khi i tng c to ra. Cho truck() khi u loadlimit nh c ch r khi mt i tng c to ra.

#include <iostream> using namespace std;

// A base class for various types of vehicles. class vehicle { int num_wheels; int range; public: vehicle (int w, int r) { num_wheels = w; range = r; }

429

void showv() { cout << "Wheels: " << num_wheels << \n; cout << "Range: " << range << \n; } };

class car : public vehicle { int passengers; public: // insert car() constructor here void show() { showv(); cout << "Passengers: " << passengers << \n; } };

class truck : public vehicle { int loadlimit; public: // insert truck() constructor here void show() 430

{ showv(); cout << "loadlimit" << Loadlimit << \n; } };

int main() { car c(5,4,500); truck t(30000, 12, 1200);

cout << "Car: \n"; c.show(); cout << "\n Truck: \n"; t.show();

return 0; }

Have car() and truck() declare objects like this:

Cho car() v truck() khai bo cc i tng nh th ny:

car ob(passengers, wheels, range); truck ob(loadlimit, wheels, range);

431

1.4. MULTIPLE INHERITANCE - TNH A K THA


There are two ways that a derived class can inherit more than one base class. First, a derived class can be used as a base class for another derived class, creating a multilevel class hierarchy. In this case, the original base class is said to be an indirect base class of the second derived class. (Keep in mind that any class-no matter how it is created-can be used as a base class.) Second, a derived class can more base classes are combined to help create the derived class. There are several issues that arise when multiple base classes are involved, and these issues are examined in this section. C hai cch mt lp dn xut k tha hn mt lp. Th nht, lp dn xut c dng nh mt lp c s cho mt lp dn xut khc, to ra th bc lp nhiu cp. Trong trng hp ny, cp c s gc c gi l lp c s gin tip ca lp dn xut th hai. (Nh rng, bt k lp no-cho d c to ra-c th c dng nh mt lp c s). Th hai, lp dn xut c th k tha trc tip hn mt lp c s. Trong trng hp ny, hai hay nhiu lp c s c kt hp ra to ra lp dn xut. C nhiu vn ny sinh khi nhiu lp c s c tnh n, v nhng vn ny c xt n trong phn ny. When a base class is used to derive a class that is used as a base class for another derived class, the constructor function of all three classes are called in order of derivation. (This is a generalization of the principle you learned earlier in this chapter.) Also, destructor functions are called in reverse order. Thus, if class B1 is inherited by D1, and D1 is inherited by D2, B1s constructor is called first, followed by D1s, followed by D2s. The destructors are called in reverse order. Khi mt lp c s c dng dn ra mt lp m lp ny li c dng lm lp c s cho mt lp dn xut khc th cc hm to ca c ba lp c gi theo th t dn xut. (y l s m rng ca nguyn l m bn bit trc y trong chng trnh ny). Cng vy, tt c cc hm hy c gi ra theo th t ngc li. Do , nu lp B1 c k tha bi lp D1 v D1 c k tha bi lp D2, th hm to ca B1 c gi u tin, ri n hm to ca D1, tip n l hm to ca D2. Hm hy c gi theo th t ngc li.

432

When a derived class directly inherits multiple base classes, it uses this expanded declaration: Khi mt lp dn xut k tha trc tip nhiu lp c s, n dng cch khai bo m rng sau: class derived-class-name : access base1, access base2, , access baseN { // body of class }

Here base1 through baseN are the base class names and access is the access specifier, which can be different for each base class. When multiple base classes are inherited, constructors are executed in the order, left to right, that the base classes are specified. Destructors are executed in the opposite order y, base1 n baseN l tn cc lp c s v access l ch nh truy cp c th khc nhau i vi mi lp. Khi nhiu lp c s c k tha, cc hm to c thi hnh theo th t t tri qua phi m cc lp c s c ch r. Cc hm hy c thi hnh theo th t ngc li. When a class inherits multiple base classes that have constructors that require arguments, the derived class passes the necessary arguments to them by using this expanded form of the derived class constructor function: Khi mt lp k tha nhiu lp c s c cc hm cu to cn nhiu i s, lp dn xut s truyn cc i s cn thit cho cc hm cu to ny bng cch dng dng m rng ca hm cu to ca lp dn xut: derived-constructor (arg-list) : base1 (arg-list), base2(arg-list), , baseN (arg-list) { // body of derived class constructor }

433

Here base1 though baseN are the names of the base classes.

y, base1 n baseN l tn cc lp c s.

When a derived class inherits a hierarchy of classes, each derived class in the chain must pass back to its preceding base any arguments it needs.

Khi mt lp dn xut k tha mt h thng th bc cc lp, mi lp dn xut trong chui cc lp ny phi truyn cho lp c s ng trc cc tham s m lp dn xut ny cn.

EXAMPLES(CC V D)

Here is an example of a derived class that inherits a class derived from another class. Notice how arguments are passed along the chain from D2 to B1.

y l v d v lp dn xut k tha mt lp dn xut khc ca mt lp khc. Ch cch cc i s c truyn theo chui t D2 n B1.

// Multiple Inheritance #include <iostream> using namespace std;

class B1 { int a; public: 434

B1(int x) { a = x; } int geta() { return a; } };

// Inherit direct base class. class D1 : public B1 { int b; public: D1(int x, int y) : B1(y) // pass y to B1 { b = x; } int getb() { return b; } };

435

// Inherit a derived class and an indirect base. class D2 : public D1 { int c; public: D2(int x, int y, int z) : D1(y, z) // pass args to D1 { c = x; } access to */ /* Because bases inherited as public, D2 has public elements of both B1 and D1. void show() { cout << geta() << << getb() << ; cout << c << \n; } };

int main() { D2 ob(1, 2, 3);

ob.show(); // geta() and getb() are still public here

436

cout << ob.geta() << << ob.getb << \n;

return 0; }

The call to ob.show() displays 3 2 1. In this example, B1 is an indirect base class of D2. Notice that D2 has access to the public members of both D1 and B1. As you should remember, when public members of a base class are inherited as public, they become public members of the derived class. Therefore, when D1 inherits B1, geta() becomes a public member of D1, which becomes a public member of D2.

Li gi i vi ob.show() hin th 321. Trong v d ny B1 l lp c s gin tip ca D2. Ch rng D2 truy cp n cc thnh vin chung ca D1 v B1. Bn nh li, khi cc thnh vin chung ca mt lp c s c k tha, chng tr thnh cc thnh vin chung ca lp dn xut. Do , khi D1 k tha B1, geta() tr thnh thnh vin chung ca D1, v tr thnh thnh vin chung ca D2.

As the program illustrates, each class in a class hierarchy must pass all arguments required by each preceding base class. Failure to do so will generate a compiletime error.

Nh chng trnh minh ha, mi lp trong mt h thng th bc lp phi truyn tt c mi i s m lp c s trc cn n. Sai st khi thc hin s sinh ra li thi gian bin dch.

The class hierarchy created in this program is illustrated here:

H thng th bc lp to ra trong chng trnh ny c minh ho nh sau:

437

B1

D1

D2

Before we move on, a short discussion about how to draw C++-style inheritance graphs is in order. In the preceding graph, notice that the arrows point up instead of down. Traditionally, C++ programmers usually draw inheritance charts as directed graphs in which the arrow points from the derived class to the base class. While newcomers sometimes find this approach counter-intuitive, it is nevertheless the way inheritance charts are usually depicted in C++. Trc khi chng ta tip tc, mt tho lun ngn lm cch no dng nn biu k tha kiu C++ mt cch c th t. Trong biu c trc , ch rng mi tn i ln thay v i xung. Theo truyn thng, nhng ngi lp trnh C++ thng v nhng biu k tha theo nh hng vi nhng mi tn t lp c dn xut ra n lp c s. Trong khi nhng ngi mi n i khi tm thy cch tip cn ny tri vi mong i, tuy vy l cch m nhng biu k tha thng c miu t trong C++.

Here is a reworked version of the preceding program, in which a derived class directly inherits two base classes:

y l phin bn c vit li ca chng trnh trc trong lp dn xut k tha trc tip hai lp c s.

#include <iostream>

438

using namespace std;

// Create first base class. class B1 { int a; public: B1(int x) { a = x; } int geta() { return a; } };

// Create second base class. class B2 { int b; public: B2(int x) { b = x;

439

} int getb() { return b; } }; // Directly inherit two base classes.

class D : public B1, public B2 { int c; public: // here, z and y are passed directly to B1 and B2 D(int x, int y, int z) : B1(z), B2(y) { c = x; } /* Because bases inherited as public, D has access to public elements of both B1 and B2. */ void show() { cout << geta() << << getb() << ; cout << c << \n; } };

440

int main() { D ob(1, 2, 3);

ob.show();

return 0; }

In this version, the arguments to B1 and B2 are passed individually to these classes by D. This program creates a class that looks like this: Trong phin bn ny, cc i tng c truyn ring l cho cc lp B1 v B2 bi D. Chng trnh ny to ra mt lp m trng nh th ny:

B1

B2

D1

The following program illustrates the order in which constructor and destructor functions are called when a derived class directly inherits multiple base classes:

Chng trnh sau y minh ha th t gi cc hm to v hm hy khi lp dn xut k tha trc tip nhiu lp c s:

#include <iostream>

441

using namespace std;

class B1 { public: B1() { cout << "Constructing B1 \n"; } ~B1() { cout << "Destructing B1 \n"; } };

class B2 { int b; public: B2() { cout << "Constructing B2 \n"; } ~B2() {

442

cout << "Destructing B2 \n"; } };

// Inherit two base classes. class D : public B1, public B2 { public: D() { cout << "Constructing D \n"; } ~D() { cout << "Destructing D \n"; } };

int main() { D ob;

return 0; }

443

This program displays the following:

Chng trnh ny hin th:

Constructing B1 Constructing B2 Constructing D Destructing D Destructing B2 Destructing B1

As you have learned, when multiple direct base classes are inherited, constructors are called in order, left to right, as specified in the inheritance list. Destructors are called in reverse order.

Nh bn bit, khi nhiu lp c s trc tip c k tha, cc hm to c gi theo th t t tri qua phi, nh c ch r trong danh sch k tha. Cc hm hy c gi theo th t ngc li.

EXERCISES(BI TP)

What does the following program display? (Try to determine this without actually running the program.)

Chng trnh sau hin th? (Th xc nh m khng chy chng trnh)

#include <iostream> 444

using namespace std;

class A { public: A() { cout << "Constructing A\n"; } ~A() { cout << "Destructing A\n"; } };

class B { public: B() { cout << "Constructing B\n"; } ~B() { cout << "Destructing B\n";

445

} };

class C : public A, public B { public: C() { cout << "Constructing C\n"; } ~C() { cout << "Destructing C\n"; } };

int main() { C ob;

return 0; }

Using the following class hierarchy, create Cs constructor so that it initializes k and passes on arguments to A() and B().

446

S dng h thng th bc lp sau y, hy lp hm to ca lp C cho n khi u k v truyn trn cc i s cho A() v B().

#include <iostream> using namespace std;

class A { int i; public: A(int a) { i = a; } };

class B { int j; public: B(int a) { j = a; } };

447

class C : public A, public B { int k; public: /* Create C() so that it initializes k and passes arguments to both A() and B() */ };

1.5. VIRTUAL BASE CLASSES - CC LP C S O


A potential problem exists when multiple base classes are directly inherited by a derived class. To understand what this problem is, consider the following class hierarchy: C mt vn tn ti khi nhiu lp c s c k tha trc tip bi mt lp dn xut. hiu vn ny, hy xt h thng th bc lp sau:

Base

Base

Derived1

Derived2

Derived3

Here the base class Base is inherited by both Derived1 and Derived2. Derived3 directly inherits both Derived1 and Derived2. However, this implies that Base is actually inherited twice by Derived3-first it is inherited through Derived1, and then again through Derived2. This causes ambiguity when a member of Base if used by Derived3. Since two copies of Base are included in Derived3, is a reference to a 448

member of Base referring to the Base inherited indirectly through Derived1 or to the Base inherited indirectly through Derived2? To resolve this ambiguity, C++ includes a mechanism by which only once copy of Base will be included in Derived3. This feature is called a virtual base class.

y, lp Base c k tha bi hai lp Derived1 v Derived2. Derived3 k tha trc tip c hai Derived1 v Derived2. Tuy nhin iu ny ch ra rng thc s Base c k tha hai ln bi Derived3 ln th nht n c k tha thng qua Derived1, v ln th hai c k tha thng qua Derived2. Bi v c hai bn sao ca Base c trong Derived3, nn mt tham chiu n mt thnh vin ca Base s tham chiu v Base c k tha gin tip thng qua Derived1 hay tham chiu v Base c k tha gin tip thng qua Derived2? gii thch tnh khng r rang ny, C++ c mt c ch m nh ch c mt bn sao ca Base trong Derived3. c im ny c gi l lp c s o (virtual base class). In situations like the one just described, in which a derived class indirectly inherits the same base class more than once, it is possible to prevent two copies of the base from being present in the derived object by having that base class inherited as virtual by any derived classes. Doing this prevents two (of more) copies of the base from being present in any subsequent derived class that inherits the base class indirectly. The virtual keyword precedes the base class access specifier when it is inherited by a derived class. Trong nhng trng hp nh va m t trong mt lp dn xut k tha gin tip cng mt lp c s hn mt ln th n c th ngn chn c 2 bn sao ca lp c s cng hin din trong i tng dn xut bng cch cho lp c s c k tha nh virtual bi bt k cc lp dn xut no. Thc hin vic ny s ngn chn c 2 bn sao ca lp c s hin din trong lp dn xut bt k tip theo m lp ny k tha gin tip lp c s. T kha virtual ng trc ch nh truy cp lp c s khi n c k tha bi mt lp dn xut.

EXAMPLES(CC V D)
Here is an example that uses a virtual base class to prevent two copies of base from being present in derived3.

y l v d dng mt lp c s o ngn nga 2 bn sao ca base c mt trong

449

derived3:

// This program uses a virtual base class. #include <iostream> using namespace std;

class base { public: int i; };

// Inherit base as virtual. class derived1 : virtual public base { public: int j; };

// Inherit base as virtual here, too. class derived2 : virtual public base { public: int k; };

450

/* Here, derived3 inherits both derived1 and derived2. However, only one copy of base is present. */ class derived3: public derived1, public derived2 { public: int product() { return i * j * k; } };

int main() { derived3 ob;

ob.i = 10; // unambigous because only one copy present ob.j = 3; ob.k = 5;

cout <<"Product is " << ob.product() << \n;

return 0; }

451

If derived1 and derived2 had not inherited base as virtual, the statement

Nu derived1 v derived2 khng k tha base nh mt lp o, th cu lnh:

ob.i = 10;

would have been ambiguous and a compile-time error would have resulted. (See Exercise 1, below.)

s khng r rng v s to ra li thi gian bin dch. (Xem bi tp 1 di y).

It is important to understand that when a base class is inherited as virtual by a derived class, that base still exists within that derived class. For example, assuming the preceding program, this fragment is perfectly valid:

Quan trng cn hiu l khi mt lp c s c k tha nh mt lp o bi mt lp dn xut th lp c s vn cn tn ti trong lp dn xut . V d, gi s vi chng trnh trn y, on chng trnh ny hon ton ng:

derived1 ob; ob.i = 100;

The only difference between a normal base class and a virtual one occurs when an object inherits the base more than once. If virtual base classes are used, only one base class is present in the object. Otherwise, multiple copies will be found.

452

S khc bit duy nht gia lp c s thng v lp c s o xy ra khi mt i tng k tha lp c s hn mt ln. Nu cc lp c s o c s dng th ch c mt lp c s hin din trong i tng. Ngc li, nhiu bn sao s c tm thy.

EXERCISES (BI TP)

Using the program in Example 1, remove the virtual keyword and try to compile the program. See what types of errors result.

Dng chng trnh trong v d 1, b t kha virtual v th bin dch chng trnh. Xem cc li.

Explain why a virtual base class might be necessary.

Gii thch ti sao cn lp c s o.

SKILLS CHECK(KIM TRA K NNG) Mastery Skills Check Kim tra k nng lnh hi

At this point you should be able to perform the following exercises and answer the questions. n y bn c th thc hin cc bi tp v tr li cc cu hi sau:

Create a generic base class called building that stores the number of floors a building has, the number of rooms, and its total square footage. Create a derived class

453

called house that inherits building and also stores the number of bedrooms and the number of bathrooms. Next, create a derived class called office that inherits building and also stores the number of fire extinguishers and the number of telephones. Note: Your solution may differ from the answer given in the back of this book. However, if it is functionally the same, count it as correct.

Hy to lp c s chung building lu tr s tng nh m mt ta nh c s phng v s tng din tch. Hy to lp dn xut house k tha building v lu tr. S phng ng v phng tm. Cng vy, to lp dn xut office k tha building v cng lu tr s bnh cu ha v s my in thoi. Ch : li gii ca bn phi khc vi li gii cui cun sch ny. Tuy nhin, nu v mt chc nng ging nhau th m n s ng.

When a base class is inherited as public by the derived class, what happens to its public members? What happens to its private members? If the base is inherited as private by the derived class, what happens to its public and private members?

Khi mt thnh vin chung ca lp c s c k tha nh mt thnh vin chung bi lp dn xut, iu g s xy ra cho cc thnh vin chung? iu g s xy ra cho cc thnh vin ring? Nu lp c s c k tha theo cch ring bi lp dn xut, iu g s xy ra cho cc thnh vin chung v ring?

Explain what protected means. (Be sure to explain what it means both when referring to members of a class and when it is used as an inheritance access specifier.)

Hy gii thch ngha ca protected. (Hy gii thch theo hai ngha khi tham chiu n cc thnh vin ca mt lp v khi c dng lm ch nh truy cp tnh k tha).

When one class inherits another, when are the classes constructors called? When are their destructors called?

454

Khi mt lp k tha mt lp khc, khi no cc hm to ca cc lp c gi? Khi no cc hm hy ca cc lp c gi?

Given this skeleton , fill in the details as indicated in the comments:

Cho dn chng trnh sau, hy b sung cc chi tit theo ch dn trong cc li ch gii.

#include <iostream> using namespace std;

class planet { protected: double distance; // miles from the sun int revolve; // in days public: planet(double d, int r) { distance = d; revolve = r; } };

class earth : public planet {

455

double circumference; // circumference of orbit public: /* Create earth (double d, int r). Have it pass the distance and days of revolution back to planet. Have it compute the circumference of the orbit. (Hint: circumference = 2r*3.1416.) */ /* Create a function called show() that displays the information. */ };

int main() { earth ob(93000000, 365);

ob.show();

return 0; }

Fix the following program:

Hy sa li chng trnh sau:

/* A variation on the vehicle hierarchy. Butthis program contains an error. Fix it. Hint: try compiling it as is and observe the error messages.

456

*/ #include <iostream> using namespace std;

// A base class for various types of vehicle. class vehicle { int num_wheels; int range; public: vehicle (int w, int r) { num_wheels = w; range = r; } void showv() { cout << "Wheels: " << num_wheels << "\n"; cout << "Range: " << range << "\n"; } };

enum motor { gas, electric, diesel

457

};

class motorized : public vehicle { enum motor mtr; public: motorized(enum motor m, int w, int r) : vehicle (w, r) { mtr = m; } void showm() { cout << "Motor:"; switch (mtr) { case gas : cout << "Gas\n"; break; case electric : cout << "Electric\n"; break; case diesel : cout << "Diesel\n"; break; } } };

458

class road_use : public vehicle { int passengers; public: road_use(int p, int w, int r) : vehicle (w, r) { passengers = p; } void showr() { cout << "Passengers: " << passengers <<'\n; } };

enum steering { power, rack_pinion, manual };

class car : public motorized, public road_use { enum steering strng; public: r, car (enum steering s, enum motor m, int w, int int p) : road_use(p, w, r), motorized(m, w, r),

459

vehicle (w, r) { strng = s; } void show() { showv(); showr(); showm(); cout << "Steering:"; switch (strng) { case power : cout << "Power\n"; break; case rack_pinion : cout "Rack and Panion\n"; break; case manual : cout << "Manual\n"; break; } } };

int main() { car c (power, gas, 4, 500, 5);

c.show(); 460

return 0; }

CUMULATIVE SKILLS CHECK Kim tra k nng tng hp

This section checks how well you have integrated material in this chapter with that from the preceding chapters.

Phn ny kim tra xem bn kt hp chng ny vi cc chng trc nh th no.

In Exercise 6 from the preceding Mastery Skills Check section, you might have seen a warning message (or perhaps an error message) concerning the use of the use of the switch statement within car and motorized. Why?

Trong bi tp 6 ca phn Kim Tra K Nng Lnh Hi trn y, bn thy mt li cnh bo (hoc c th l mt thng bo li) lin quan n s dng cu lnh switch trong car() v motor(). Ti sao?

As you know from the preceding chapter, most operators overloaded in a base class are available for use in a derived class. Which one or ones are not? Can you offer a reason why this is the case?

Nh bn bit t chng trc, hu ht cc ton t c qu ti trong mt lp c s th c th c s dng trong lp dn xut. C mt hay nhiu ton t no th khng c nh th? Bn c th trnh by l do cho trng hp ny?

461

Following is reworked version of the coord class from the previous chapter. This time it is used as a base for another class point is in. On your own, run this program and try to understand its output.

Sau y l phin bn sa i ca lp coord trong chng trc. Ln ny, n c dng lm lp c s cho mt lp khc gi l quad cng duy tr gc phn t trong c mt im c th. Bn hy thay chng trnh ny v tm hiu d liu xut.

/* Overload the +, -, and = relative to coord class. Then use coord as a base for quad. */ #include <iostream> using namespace std;

class coord { public: int x, y; // coordinate values coord() { x = 0; y = 0; } coord(int i, int j) { x = i; y = j; }

462

void get_xy(int &i, int &j) { i = x; j = y; } coord operator + (coord ob2); coord operator - (coord ob2); coord operator = (coord ob2); };

// Overload + relative to coord class. coord coord::operator + (coord ob2) { coord temp;

cout << "Using coord operator + () \n";

temp.x = x + ob2.x; temp.y = y + ob2.y;

return temp; }

// Overload - relative to coord class. coord coord::operator - (coord ob2)

463

{ coord temp;

cout << "Using coord operator - () \n";

temp.x = x - ob2.x; temp.y = y - ob2.y;

return temp; }

// Overload = relative to coord. coord coord::operator = (coord ob2) { cout << "Using coord operator = () \n";

x = ob2.x; y = ob2.y;

return *this; // return the object that is assigned to }

class quad : public coord { int quadrant; 464

public: quard () { x = 0; y = 0; quadrant = 0; } quard (int x, int y) : coord (x, y) { if(x>=0 && y>=0) quadrant = 1; else if(x<0 && y>=0) quadrant = 2; else if(x<0 && y<0) quadrant = 3; else quadrant = 4; } void showq() { cout << "Point in Quadrant:" << quadrant <<\n; } quad operator = (coord ob2); }; 465

quad quad::operator = (coord ob2) { cout << "Using quad operator=()\n";

x = ob2.x; y = ob2.y; if(x>=0 && y>=0) quadrant = 1; else if(x<0 && y>=0) quadrant = 2; else if(x<0 && y<0) quadrant = 3; else quadrant = 4; return *this; }

int main() { quad o1(10, 10), o2(15, 3), o3; int x, y;

o3 = o1 + o2; //add two objects-this calls operator+() 466

o3.get_xy(x, y); o3.showq(); cout << "(o1 + o2)X:"<<x<<", Y:" <<y<< "\n";

o3 = o1 - o2; // subtract two objects o3.get_xy(x, y); o3.showq(); cout << "(o1 - o2)X:"<<x<<", Y:" <<y<< "\n";

o3 = o1; // assign an object o3.get_xy(x, y); o3.showq(); cout << "(o3=o1)X:"<<x<<", Y:" <<y<< "\n";

return 0; }

Again on your own, convert the program shown in Exercise 3 so that it uses friend operator functions.

Ln na, hy chuyn i chng trnh trong bi tp 3 sao cho n s dng cc hm ton t friend.

467

CHNG 8 INTRODUCING THE C++ I/O SYSTEM - DN NHP H THNG NHP/XUT C++
Chapter objectives
8.1. SOME C++ I/O BASICS Vi c s nhp/ xut trong C++ 8.2. FORMAT I/O

468

Nhp xut c nh dng 8.3. USING WIDTH(), PRECISION(), AND FILL() S dng cc hm width(), precision(), fill() 8.4. USING I/O MANIPULATORS S dng b thao tc nhp xut 8.5. CREATING YOUR OWN INSERTERS To b chn ring ca bn 8.6. CREATING EXTRACTORS To b chit

Although you have been using C++ style I/O since the first chapter of this book, it is time to explore it more fully. Like its predecessor, C, the C++ language includes a rich I/O system that is both flexible and powerful. It is important to understand that C++ still supports the entire C I/O system. However, C++ supplies a complete set of object-oriented I/O routines. The major advantage of the C++ I/O system is that it can be overload relative to class that you create. Put differently, the C++ I/O system allows you seamlessly integrate new types that you create. Mc d cc bn c s dng Nhp/Xut ca C++ k t u ca cun sch ny, vn s c trnh by li mt cch cn k. Ging nh trong ngn ng C, C++ c mt h thng Nhp/Xut linh ng v hu hiu. Mt iu quan trng m bn cn bit n l C++ vn h tr hon ton h thng Nhp/Xut nh hng i tng. u im chnh ca h thng Nhp/Xut ca C++ l n c th c qu ti ln cc lp do bn to ra. Hay ni cch khc, h thng Nhp/Xut ca C++ cn cho php tch hp vo cc kiu t to mt cch m thm. Like the C I/O system, the C++ object-oriented I/O system makes little distinction between console and file I/O. File and console I/O are really just different perspectives on the same mechanism.The examples in this chapter use console I/O, but the information presented is applicable to file I/O as well. (File I/O is examined in detail in Chapter 9.) Ging nh C, h thng Nhp/Xut ca C++ hu nh khng phn bit gia thit b v tp tin Nhp/Xut trn thit b chun v trn tp tin ch l nhng dng v khc nhau ca 469

cng mt c ch m thi. Trong chng ny, nhng chng trnh mu c vit cho cc thit b Nhp/Xut, nhng n cng hon ton c th p dng cho Nhp/ xut tp tin (Vn Nhp/Xut tp tin s c cp chi tit trong chng 9). At the time of this writing, there are two versions of the I/O library in use: the older one that is based on the original specifications for C++ and the newer one defined by Standard C++. For the most part the two libraries appear the same to the programmer. This is because the new I/O library is, in essence, simply an updated and improve version of the old one. In fact, the vast majority of the differences between the two occur beneath the surface, in the way that the libraries are implemented-not in the way that they are used. From the programmers perspective, the main difference is that is that the new I/O library contains a few additional features and defines some new data types. Thus, the new I/O library essentially a superset of the old one. Nearly all programs originally written for the old library will compile without substantive changes when the new library is used. Since the old-style I/O library is now obsolete, this book describes only the new I/O library as defined by Standard C++. But most of the information is applicable to the old I/O library as well. Ti thi im bi vit ny, c hai phin bn th vin nhp xut c s dng: ci c hn da trn c s nhng c tnh gc ca C++ v phin bn mi hn c nh ngha bi C++ chun. i vi ngi lp trnh trnh th 2 th vin ny hu ht u ging nhau. n gin l v th vin nhp xut mi l phin bn cp nht v ci tin ca phien bn c trc n. Thc t, phn ln s khc bit gia 2 th vin ny xy ra di b mt, cch m cc th vin ny thc hin khng nh cch m chng c s dng. ng t gc ca ngi lp trnh th khc bit chnh l th vin nhp xut mi cha ng mt vi c tnh b sung v nh ngha mt s kiu d liu mi. V vy, v bn cht th th vin nhp xut mi l mt tp hp ln hn bao gm c ci c. Gn y, tt c cc chng trnh c vit nguyn gc vi th vin c c bin dch khng c thay i g khi s dng th vin mi. V rng th vin kiu c ny hin gi li thi, nn cun sch ny ch m t th vin nhp xut kiu mi c nh ngha bi C++ chun. Tuy nhin hu ht thng tin c p dng y cng ph hp vi th vin c. This chapter covers several aspects of C++s I/O system, including formatted I/O, I/O manipulators, and creating your own I/O inserters and extractors. As you will see, the C++ system shares many features with the C I/O system. Ni dung ca chng ny lin quan n cc kha cnh c trng ca cc h thng Nhp/Xut C++ bao gm Nhp/Xut c nh dng, b thao tc Nhp/Xut, to lp b chn (inserter) v b chit (extractor). Qua , bn s thy c nhiu u im ging nhau gia h thng Nhp/Xut ca C v C++.

470

REVIEW SKILLS CHECK: (kim tra k nng n tp) Before proceeding, you should be able to correctly answer the following questions and do the exercises. Trc khi bt u chng mi, bn hy tr li cc cu hi v lm cc bi tp sau y: Create a class hierarchy that stores information about airships. Start with a general base class called airship that stores the number of passengers and the amount of cargo (in pounds) that can be carried. Then create two derived classes call airplane and balloon from airship. Have airplane store the type of engine used (propeller or jet) and range, in miles. Have balloon store information about the type of gas used to lift the balloon (hydrogen or helium) and its maximum altitude (in feet). Create a short program that demonstrates this class hierarchy. (Your solution will, no doubt, differ from the answer shown in the back of this book. If it is functionally similar, count it as correct). Hy to ra mt phn cp lp lu tr thng tin v cc tu bay. Hy bt u mt lp c s tn l airship cha thng tin v s lng hnh khch ti a v trng lng hng ha ti a (n v tnh l pound) m tu bay c th ch c. Sau , t lp c s airship, hy to hai lp dn xut (derived class) mang tn l airplane v balloon. Lp airplane lu kiu ca ng c (gm ng c cnh qut v ng c phn lc ), tm xa (n v tnh l mile). Lp balloon lu thng tin v loi nhin liu s dng ch kh cu (gm hai loi l hyrogen v helium), cao ti a (n v tnh l feet). Hy vit mt chng trnh ngn minh ha cho phn cp lp trn.( D nhin l bi gii ca bn phi khc cht t so vi bi gii phn sau quyn sch ny. Nu gii thut ca chng tng t nhau, tc l bn gii ng.) What is protected used for? Cng dng ca b c t thm nhp c bo v (protected) l g? Give the following class hierarchy, in what order are the constructor functions called? In what order are the destructor functions called? Trong phn cp lp sau y, hm to (constructor) c gi n nh th no? Hm hy (destructor) c gi n nh th no? #include<iostream.h> class A{ public:

471

A(){cout<<Constructing A\n;} ~A(){cout<<Destructing A\n;} };

class B:publicA{ public: B(){cout<<Constructing B\n;} ~ B(){cout<<Destructing B\n;}

};

class C: publicB{ public: C(){cout<<Constructing C\n;} ~ C(){cout<<Destructing C\n;} };

Main() { C ob; return 0; }

Give the following fragment, in what order are the constructor functions called? Trong on chng trnh sau y, hm to c gi n nh th no? class myclass: public A, public B, public C {

472

Fill in the missing constructor function in this program: in vo chng trnh sau y nhng hm to cn thiu: #include<iostream.h> class base{ int l, j; public: //need constructor Void showij() {cout <<l<<<<j<<\n;} }; class derived : public base { int k; public; //need constructor void show() {cout <<k<<; showij();} }; main() { dereved ob(1, 2, 3); ob.show(); return 0; }

In general, when you define a class hierarchy, you begin with the most _________ class and move to the most _________ class. (Fill in the missing words). Ni chung, khi nh ngha mt phn cp lp, ngi ta bt u t lp . nht,

473

v dn n lp . nht. (in vo khong trng).

1.1. SOME C++ I/O BASICS - C s Nhp/Xut C++


Before we begin our examination of C++ I/O, a few general comments are in order. The C++ I/O system, like the C I/O system, operates through streams. Because of your C programming experience, you should already know what a stream is, but here is a summary. A stream is a logical device that either produces or consumes information. A stream is linked to a physical device by the C++ I/O system. All streams behave in the same manner. Even if the actual the physical devices they are linked to differ. Because all streams act the same, the I/O system presents the programmerwith a consistent interface, even though it operates on devices with differing capabilities. For example, the same function that you use to write to the screen can be used to write to a disk file or to the printer. Trc ht, ging nh C, h thng Nhp/Xut ca C++ cng iu khin cc lung (stream). i vi cc bn tng lp trnh C,chc hn bit lung l g, tuy nhin y chng ta s tm tt khi nim ny. Lung l mt thit b lun l c kh nng to ra hoc s dng thng tin. Nh h thng Nhp/Xut ca C++, mi lung c lin kt vi thit b vt l. Tuy nhin, cho d c nhiu loi thit b vt l khc nhau, nhng cc lung u c x l nh nhau. Chnh v vy m h thng Nhp/Xut c th vn hnh trn bt k loi thit b no. Ly v d, cch m bn c th xut thng tin ra mn hnh c th s dng cho vic ghi thng tin ln tp tin trn a hay xut ra my in. As you know, when a C program begins execution, three predefined streams are automatically opened: stdin, stdout, and stderr. A similar thing happens when a C++ program starts running. When a C++ program begins, these four streams are automatically opened: Stream cin cout cerr clog Meaning Standard input Standard output Standard error Buffered version of cerr Default device Keyboard Screen Screen Screen

474

Nh chng ta bit, khi mt chng trnh C c thc thi, c ba lung nh sn c m mt cch t ng l : stdin, stdout, stderr. Tng t nh vy, khi mt chng trnh C++ c thc thi, s c bn lung c m mt cch t ng. l:

Lung cin cout cerr clog

ngha Thit b nhp chun Thit b xut chun Thit b bo li chun Phin bn ca cerr

Thit b mc nh Bn phm Mn hnh Mn hnh Mn hnh

As you have probably guessed, the stream cin, cout, and cerr correspond to Cs stdin, stdout, and stderr. You have already been using cin and cout. The stream clog is simply a buffered versions of cerr. Standard C++ also opens wide (16-bit) character versions of these streams called wcin, wcout, wcerr, and wclog, but we wont be using them in this book. The wide character streams exist to support languages, such as Chinese, that require large character sets. Bn c th on c rng cc t cin, cout v stdin, stdout v stderr ca C. Chng ta s dng cin v cout. Lung clog l mt phin bn nm vng m ca cerr. C++ chun cng m rng cc phin bn ca cc lung ny gi l wcin, wcout, wcerr, and wclog, nhng chng ta s khng s dng chng trong cun sch ny. Cc lung k t m rng ny h tr cho cc ngn ng khc, nh ting Trung Quc chng hn, ngn ng ny i hi mt b k t rng hn. By default, the standard streams are used to communicate with the console. However, in environments that support I/O redirection, these streams can be redirected to others devices. Do mc nh, cc lung chun c lin kt vi thit b xut nhp chun. Tuy nhin, chng ta c th nh lai cho cc lung lin kt gn vi cc thit b xut nhp khc. As you learned in chapter 1, C++ provides support for its I/O system in the header file <iostream>. In this file, a rather complicated set of class hierarchies is defined that supports I/O operations. The I/O classes begin with a system of template classes. Template classes, also called generic classes, will be discussed more fully in Chapter 11: briefly, a template class defines the form of a class without fully specifying the

475

data upon which it will operate. Once a template class has been defined, specific instances of it can be created. As it relates to the I/O library, Standard C++ creates two specific versions of the I/O template classes: one for 8-bit characters and another for wide characters. This book will discuss only the 8-bit characters classes, since they are by far the most frequently used. Trong chng 1, C++ cung cp cc h tr cho h thng Nhp/Xut trong file mc <iostream>. Ni dung ca tp tin ny l cc phn cp lp h tr cc thao tc nhp/xut. Cc lp nhp xut bt u vi mt h thng cc lp mu. Cc lp mu ny, cn c gi l cc lp tng qut, s c trao i y hn trong chng 11: ni mt cch ngn gn, mt lp mu nh ngha dng ca lp khng nh r y d liu m lp vn dng. Lp mu nh ngha trong mt ln v cc phin bn ring ca n c th c to ra sau . V n lin quan n th vin nhp xut nn C++ chun to ra 2 phin bn ring cho cc lp nhp/ xut mu ny: mt ci cho cc k t 8 bit v ci kia cho cc k t ln hn. Cun sch ny ch trao i v cc lp k t 8 bit, v chng th thng xuyn c s dng. The C++ I/O system is built upon two related, but different, template class hierarchies. The first is derived from the low-level I/O class called basic_streambuf directly. The class hierarchy that you will most commonly be working with is derived from basic_ios. This is a high-level I/O class that provides formatting, errorchecking, and status information related to stream I/O. basic_ios is used as a base for several derived classes, including basic_istream, basic_ostream and basic_iostream. These classes are used to create streams capble of input, output and input/output, respectively. C++ c hai phn cp lp Nhp/Xut, chng c lin h ci nhau nhng chng khng ging nhau. Phn cp lp Nhp/Xut th nht c suy dn t lp Nhp/Xut cp thp, tn l basic_streambuf. Lp ny cung cp cc thao tc c bn ca Nhp/Xut ca C++. Bn khng cn phi s dng trc tip lp streambuf ny, tr phi bn ang lp trnh nhp/xut trnh cao. Thng thng, bn s s dng mt phn cp lp nhp xut khc, c tn l basic_ios. l lp nhp/xut cp cao, n cung cp cc thao tc v nh dng, kim li, thng tin trng thi ca cc lung nhp/xut. lp basic_ios l lp c s bao gm cc lp istream, ostream, iostream. Ba lp xut ny c s dng to ra cc lung nhp, xut, v nhp/xut. As explained earlier, the I/O library creates two specific versions of the class hierarchies just described: one for 8-bit characters and one for wide characters. The following table shows the mapping of the template class names to their 8-bit character-based versions (including some that will be used in Chapter 9):

476

Template Class basic_streambuf basic_ios basic_istream basic_ostream basic_iostream basic_fstream basic_ifstream basic_ofstream

8-Bit Character-Based Class streambuf ios istream ostream iostream fstream ifstream ofstream

Nh c gii thch trn, th vin nhp/xut to ra 2 phin bn ring cho cc phn cp lp c th l: mt phin bn cho cc k t 8 bit v mt cho cc k t rng hn. Bng sau cho bit nh x ca cc tn lp mu cho cc phin bn c s ca k t 8 bit (bao gm mt s c s dng trong chng 9): Lp mu basic_streambuf basic_ios basic_istream basic_ostream basic_iostream basic_fstream basic_ifstream basic_ofstream Lp c s k t 8 bit streambuf ios istream ostream iostream fstream ifstream ofstream

The character-based names will be used throughout the remainder of this book, since they are the names that you will you in your programs. They are also the same names that were used by the old I/O library. This is why the old and the new I/O

477

libraries are compatible at the source code level. Cc tn ca k t c s s c s dng trong phn cn li ca cun ny, v chng c s dng trong cc chng trnh ca bn. Cc tn ny cng ging nh tn c s dng trong th vin nhp/xut c. l l do ti sao cc th vin c v mi tng thch vi nhau cp ca m ngun ny. One last point: The ios class contains many member functions and variables thar control or monitor the fundamental operation of a stream. It will be referred to frequently. Just remember that if you include <iostream> in your program, you will have access to this important class. im lu cui cng: Lp ios cha nhiu hm v bin dng iu khin, v kim sot cc thao tc c bn ca mt lung. Lp ny thng c tham kho n. Nu bn mun chng trnh ng dng ca mnh c th s dng c lp ios ny, hy np tp tin tiu <iostream>.

1.2. FORMATTED I/O - Nhp/Xut C nh Dng


Until now, all examples in this book displayed information to the screen using C++s default formats. However, is is possible to output information in a wide variety of forms. In fact, you can format data using C++s I/O system in much the same way that you do using Cs printf( ) function. Also, you can alter certain aspects of the way information is input. Cho n by gi, tt c cc chng trnh mu trong quyn sch ny u hin th thng tin ra mn hnh theo dng mc nh ca C++. Tuy nhin, chng ta hon ton c th xut thng tin theo nhiu hnh thc . Ni tm lai, bn c th nh dng d liu bng h thng nhp/xut ca C++ bng cch ging nh s dng hm printf() ca C chun. Ngoi ra, bn c th thay i mt s hnh thc nhp d liu. Each stream has associated with it a set of format flags that control the way information is formatted. The I/O class declares a bitmask enumeration called fmtflags, in which the following values are defined: Mi lung ca C++ c i km vi mt tp hp cc c nh dng. cc c inh dng ny xc nh cch thc th hin ca thng tin. Lp nhp/xut khai bo mt kiu lit k d liu gi l fmtflags c nh ngha bi cc gi tr sau:

478

adjustfield basefield boolalpha dec fixed

floatfield hex internal left oct

right scientific showbase showpoint showpos

skipws unitbuf uppercase

These values are used to set or clear the format flags and are defined within ios. If you are usin an order, nonstandard compiler, it may not define the fmtflags enumeration type. In this case, the format flags will be encoded into a long integer. Cc gi tr ny c s dng thit lp hay xa b cc c nh dng v c nh ngha trong ios. Nu bn s dng mt trnh bin dch khng t chun, n c th nh ngha kiu lit k fmtflags. Trong trng hp ny, cc c nh dng s c m ha thnh mt s nguyn di. When the skipws flags is set, leading whitespace characters (spaces, tabs, and newlines) are discarded when input is being perform on a stream. When skipws is cleared, whitespace characters are not discarded. Khi cc c skipws c thit lp, cc k t khong trng (gm cc k t khong cch, tab, v xung dng) c b i khi c mt lung. Khi c ny b xa, cc k t khong trng s khng b b i. When the left flag is set, output is left justified. When right is set, output is right justified. When the internal flag is set, a numeric value is padded to fill a field by inserting spaces between any sign or base character. If none of these flags is set, output is right justified by default. Khi c left c t, kt xut s c canh bin tri. Khi bn thit lp c right, kt xut c canh phi. khi t c internal, mt gi tr s c thm vo in vo mt trng bng cc chn vo cc khong gia k t c s hoc du. Nu khng c c no c thit lp, theo mc nh, kt xut s c canh bin phi. By default, numeric values are output is decimal. However, it is possible to change the number base. Seting the oct flag causes ouput to be displaced in octal. Seting the hex flag causes ouput to be displaced in hexadecimal. To return output to decimal, set the dec flag. Mc nhin, cc gi tr s c trnh by di dng s thp phn. Tuy nhin, chng ta 479

c th thay i c s ca kt xut. Thit lp c oct s lm cho kt xut c trnh by dng s h bt phn. Khi t c h kt xut l s h thp lc phn. tr li kiu s thp phn, ta thit lp c dec. Setting showbase causes the base of numeric values to be shown. For example, if the conversion base is hexadecimal the value 1F will be displaced as 0x1F. Khi t c showbase, c s ca gi tr s c trnh by. V d, nu s c trnh by h thp lc phn, gi tr 1F s c thay bng 0x1F. By default, when scientific notation is displaced, the e is lowercase. Also, when a hexadecimal value is displaced, the x is lowercase. When uppercase is set, these characters are displaced uppercase. Setting showpos causes a leading plus sign to be displaced before positive values. Theo mc inh, khi mt con s kiu s m c in ra, ch e c trnh by kiu ch thng. thng t nh vy, khi in mt gi tr s h thp lc phn, k t x dng ch h thp lc c trnh by kiu ch thng. khi c upease c thit lp, cc k t ni trn s c trnh by bng kiu ch in hoa. Vic thit lp c showpos lm cho xut hin du cng pha trc cc gi tr s dng. Setting showpoint causes a decimal point and trailing zeros to be displaced for all floating-point output whether needed or not. Thit lp c showpoint cho php in du chm thp phn v cc s khng i sau km theo cc gi tr kiu chm ng. If the scientific flag is set, floating-point numeric values are displaced using scientific notation. When fixed is set, floating-point values are displaced using normal notation. When neither flag is set, the compiler chooses an approriate method. C scientific c thit lp lm cc gi tr s kiu chm ng c trnh by di dng m. Nhng khi t c fixed, cc gi tr s kiu du chm ng s c th hin theo dng bnh thng. Nu bn khng lp mt c no trong hai c ni trn, trnh bin dch s chn cch thch hp. When unitbuf is set, the buffer is flushed after each insertion operation. When boolalpha is set, Booleans can be input or output using the keywords true and false.

480

Khi c unitbuf c ci t, b nh m c xa sch sau mi hnh ng chn vo. Khi c boolalpha c ci t, cc lun l c th c nhp v xut bng vic s dng t kha true v false. Since it is common to refer to the oct, dec, and hex fields, they can be collectively referred to as basefield. Similarly, the left, right, and internal fields can be referred to as adjustfield. Finally, the scientific and fixed fields can be referenced as floatfield. K t by gi, y l chuyn bnh thng tham chiu ti c oct, c dec v c hex, chng c th c tp hp tham chiu nh basefield. Tng t, c left, c right v c internal c th tham chiu nh adjustfield. Cui cng, c scientific v c fixed c th tham chiu nh floatfield. To set a format flag, use the setf( ) function. This function is a member of ios. Its most common form is shown here: Hm setf() dng thit lp c nh dng. hm ny thuc lp ios. Dng s dng thng gp hm ny l: fmtflags setf(fmtflags flags);

This function returns the previous settings of the format flags and turns on those flags specified by flags. (All other flags are unaffected). For example, to turn on the showpos flag, you can use this statement: Hm ny tr v gi tr c thit lp trc ca c v thit lp cc c c tn trong hm. (Cc c khc khng c tn trong mnh gi hm s khng b nh hng. ). V d, thit lp c showpos, ta s dng mnh sau: stream.setf(ios::showpos);

Here stream is the stream you wish to affect. Notice the use of the scope resolution operator. Remember, showpos is an enumerated constant within the ios class. Therefore, it is necessary to tell the compiler this fact by preceding showpos with the class name and thwe scope resolution operator. If you dont, the constant showpos will simply not be recognized. y, stream l tn lung bn mun tc ng. Bn hy lu n vic s dng ton t phm vi y. V c showpos l mt hng kiu lit k thuc lp ios. V vy, chng 481

ta cn phi thng bo cho trnh bin dch bit iu ny bng cch vit tn lp ios v ton t phm vi :: trc tn c showpos. Nu khng trnh bin dch s khng nhn bit c t showpos. It is important to understand that setf( ) is a member function of the ios class and affects streams created by that class. Therefore, any call to setf( ) is done relatve to a specific stream. There is no concept of calling setf( ) by itself. Put differently, there is concept in C++ of global format status. Each stream maintains its own format status information individually. Mt iu quan trng na l hm setf() l mt thnh phn ca lp ios, v n s nh hng n cc lung to ra bi lp ios ny. Cho nn , tt c cc mnh gi hm seft() u s dungj cho mt lung c th, v hm seft() khng th gi mt cch chung chung c. hay ni khc i, trong C++, chng ta khng th t c nh dng phm vi ton cc chung cho t c cc lung. Mi lung s mang mt thng tin trng thi cc nh dng ring ca n v chng c lp vi nhau. It is possible to set more than one flag is a single call to setf( ), rater than making multiple calls. To do this, OR together the values of the flags you want to set. For example, this call sets the showbase and hex flags for cout: Chng ta c th gi hm setf( ) lp nhiu c cng lc thay v gi hm ny nhiu ln cho mi c. thc hin iu ny, chng ta s dng ton t OR. V d thit lp 2 c showbase v hex, chng ta dng mnh sau: cout.setf(ios::showbase | ios::hex);

REMEMBER: Because the format flags are defined within the ios class, you must access their values by using ios and the scope resolution operator. For example, showbase by itself will not be recognized; you must specify ios::showbase. Cn nh: V cc c nh dng c nh ngha trong lp ios, bn phi truy xut n gi tr ca cc c ny bng cch s dng tn lp ios cng vi ton t phm vi. V d, chng ta phi vit l ios::showbase thay v ch vit l showbase s khng c nhn bit. The complement of setf( ) is unsetf( ). This member function of ios clears one or more format flags. Its most common prototype form is shown here: Cng vi hm thit lp c nh dng setf( ), cn c hm xa c unsetf( ). Hm ny cng l mt thnh phn ca lp ios, dng xa mt hay nhiu c nh dng. Dng s

482

dng thng gp ca hm ny l: Void unsetf(fmtflags flags);

The flags specified by flags are cleared. (All other flags are unaffected). There will be times when you want to know, but not alter, the current format settings. Since both setf( ) and unsetf( ) alter the setting of one or more flags, ios also includes the member function flags( ), which simply returns the current setting of each format flag. Its prototype is shown here: Cc c nh dng c ch r bi cc c ca hm s b xa (cc c khc th khng b nh hng). C nhiu ln khi chng ta bit, nhng khng c s thay i, cch thit lp nh dng hin thi. Bi v c hai hm setf() v unsetf() thay i thit lp ca mt hoc nhiu c, nn ios cng bao gm hm thnh phn flags(), n gin tr v s thit lp hin thi ca mi c nh dng. Mu ca n c trnh by nh sau : Fmtflags flags( );

The flags() function has a second form that allows you to set all format flags associated with a stream to those specified in the argument to flags( ). The prototype for this version of flags( ) is shown here: Hm flags() c mt dng th hai cho php chng ta thit lp tt c cc c nh dng lin kt vi mt lung v c ch ti i s flags(). Mu ca phin bn ny ca hm flags() c trnh by nh sau : Fmtflags flags(fmtflags f);

When you use this version, the bit pattern found in f is copied to the variable used to hold the format flags associated with the stream, thus overwriting all previous flag settings. The function returns the previous setings. Khi bn s dng dng ny, tng bit ca gi tr f s c gn cho bin lu tr c nh dng, v s xa tnh trng c nh dng trc . Hm s tr v thit lp trc .

483

EXAMPLES:
Here is an example that shows how to set several of the format flags: y l v d minh ha cch thit lp mt vi c nh dng: include <iostream> using namespace std ;

int main( ) { // display using default settings cout << 123.23 << hello << 100 << \n ; cout << 100.0 << \n\n ;

// now change formats cout.unsetf (ios: :dec) ; cout.setf (ios: :hex | ios: :scientific) ; cout << 123.23 << hello << 100 << \n ;

cout.setf (ios: :showpos) ; cout << 10 << << -10 << \n ;

cout.setf (ios: :showpoint | ios: :fixed) ; cout << 100.0 ;

return 0 ; }

484

This program displays the following output: 123.23 hello 100 10 -10 100 1.232300e-02 hello 64 a ffffffff6 +100.000000

Notice that the showpos flag affects only decimal output. It does not affect the value 10 when output in hexadecimal. Also notice the unsetf( ) call that turns off the dec flag (which is on by default). This call is not needed by all compilers. But for some compilers, the dec flag overrides the other flags, so it is necessary to turn it off when turning on either hex or oct. In general, for maximum portability, it is better to set only the number base that you want to use and clear the others.S Lu rng c showpos ch nh hng n kt xut ca gi tr s thp phn. N khng nh hng n kt xut ca gi tr 10 khi gi tr ny c trnh by s h thp lc phn. Cng ch n li gi hm unsetf( ) s tt c dec (c thit lp nh mc nh). Li gi ny th khng cn thit i vi mi trnh bin dch. Nhng trong mt vi trnh bin dch, c dec c vit chng ln cc c khc, do vy n cn c tt i khi bt c hex hoc c oct. Ni chung, tin li ti a, s tt hn khi ch thit lp c s s m bn mun s dng v xa nhng ci khc. The following program illustrates the effect of the uppercase flag. It first sets the uppercase, showbase, and hex flags. It then outputs 88 in hexadecimal. In this case, the X used in the hexadecimal notation is uppercase. Next, it clears the uppercase flag by using unsetf( ) and again outputs 88 in hexadecimal. This time, the x is lowercase. Chng trnh sau minh ha tc dng ca c uppercase. C c thit lp u tin l uppercase, showbase, v hex. Tip theo chng trnh s in ra gi tr 88 dng s m, v k t X dng thng bo c s 16 c in di kiu ch in hoa. Sau , chng trnh dng hm unsetf( ) xa c uppercase, kt qu l xut ra 88 dng thp lc phn v k t x dng kiu ch thng. #include <iostream> 485

using namespace std ;

int main( ) { cout.unsetf( ios: :dec) ; cout.setf( ios: :uppercase | ios: :showbase | ios: :hex) ;

cout << 88 << \n ; cout.unsetf(ios: :uppercase) ; cout << 88 << \n ; return 0 ; } The following program use flags( ) to display the settings of the format flags relative to cout. Pay special attention to the showflags( ) function. You might find it useful in programs you write. Chng trnh sau s dng hm flag( ) hin thng tin trng thi ca cc c nh dng thuc lung cout. Hy c bit ch trng n hm showflag(), bn s thy c tnh hu dng ca n: #include <iostream> using namespace std ;

void showflag( ) ; int main( ) { showflags( ) ; cout.setf( ios: :oct | ios: :showbase | ios: :fixed) ;

486

showflags( ) ; return 0 ; }

void showflags( ) { ios: :fmtflags f ; f = cout.flags( ) ;

if( f & ios: :skipws) cout << skipws on\n ; else cout << skipws off\n ;

if( f & ios: :left) cout << left on\n ; else cout << left off\n ;

if( f & ios: :right) cout << right on\n ; else cout << right off\n ;

if( f & ios: :internal)

487

cout << internal on\n ; else cout << internal off\n ;

if( f & ios: :dec) cout << dec on\n ; else cout << dec off\n ; if( f & ios: :oct) cout << oct on\n ; else cout << oct off\n ;

if( f & ios: :hex) cout << hex on\n ; else cout << hex off\n ;

if( f & ios: :showbase) cout << showbase on\n ; else cout << showbase off\n ;

if( f & ios: :showpoint) cout << showpoint on\n ;

488

else cout << showpoint off\n ;

if( f & ios: :showpos) cout << showpos on\n ; else cout << showpos off\n ;

if( f & ios: :uppercase) cout << uppercase on\n ; else cout << uppercase off\n ;

if( f & ios: :scientific) cout << scientific on\n ; else cout << scientific off\n ;

if( f & ios: :fixed) cout << fixed on\n ; else cout << fixed off\n ;

if( f & ios: :unitbuf) cout << unitbuf on\n ;

489

else cout << unitbuf off\n ;

if( f & ios: :boolalpha) cout << boolalpha on\n ; else cout << boolalpha off\n ;

cout << \n ; }

Inside showflags( ), the local variable f is declared to be of type fmtflags. If your compiler does not define fmtflags, declare this variable as long instead. The output from the program is shown here: Bn trong hm showflags( ), bin cc b f c khai bo kiu fmtflags. Nu trnh bin dch khng nh ngha fmtflags, th khai bo bin ny theo kiu long. Kt qu xut ra ca chng trnh trn nh sau: Skipws on Left off Right off Internal off Dec on oct off hex off showbase off showpoint off showpos off 490

uppercase off scientific off fixed off unitbuf off boolalpha off

skipws on left off right off internal off dec on oct on hex off showbase on showpoint off showpos off uppercase off scientific off fixed on unitbuf off boolalpha off

The next program illustrates the second version of flags( ). It first constructs a flag mask that turn on showpos, showbase, oct, and right. It then uses flags( ) to set the flag variable associated with cout to these settings. The function showflags( ) verifies that the flags are set as indicated. (This is the same function used in the previous program ).

491

4. Chng trnh tip sau y minh ha cho cch s dng th hai ca hm flags(). Trc tin chng trnh s to ra mt mt n c dng t cc c showpos, showbase, oct v right. Sau chng trnh s thit lp cc bin c kt hp vi hm cout cho cc thit t ny. Hm showflags( ) xc nh cc c c bt. (y l hm s dng nh trong chng trnh trc). #include <iostream> using namespace std;

void showflags( ) ; int main( ) { // show default condition of format flags showflags( ) ;

// showpos, showbase, oct, right are on, others off ios :: fmtflags f = ios :: showpos | ios :: showbase | ios :: oct | ios :: right ;

cout.flags(f) ; showflags( ) ; return 0 ; }

//set flags

EXERCISES:
1. write a program that sets couts flags so that integers display a + sign when positive values are displayed. Demonstrate that you have set the format flags correctly.

492

Vit chng trnh thit lp trng thi cc c nh dng sao cho cc gi tr nguyn dng c in ra c mang du +. Hy in ra cho bit cc c nh dng c thit lp ng. 2. write a program that sets couts flags so that the decimal point is always shown when floating-point values are displayed. Also, display all floating-point values in scientific notation with an uppercase E. Hy vit chng trnh thit lp trng thi cc c nh dng ca lung cout sao cho cc tr s kiu chm ng c trnh by vi kiu chm thp phn. Ngoi ra cc tr s kiu chm ng c in ra di dng s m vi k t E ch phn m c trnh by bng kiu in hoa. 3. write a program that saves the current state of the format flags, sets showbase and hex, and displays the value 100. Then reset the flags to their previous values. Hy vit chng trnh thc hin vic lu li trang thi hin ti ca cc c nh dng, thit lp 2 c showbase v hex v xut ra gi tr 100. Sau thit lp cc c tr li nh trng thi trc .

1.3. USING WIDTH( ), PRECISION( ), AND FILL( ) S DNG HM WIDTH(), PRECISION( ), V FILL( ):
In addition to the formatting flags, there are three member functions defined by ios that set these format parameters: the field width, the precision, and the fill character. These are width( ), precision( ), and fill( ), respectively. By default, when the value is output, it occupies only as much space as the number of characters it takes to display it. However, you can specify a minimum field width by using the width( ) function. It prototype is shown here: Cng vi cc c nh dng, cn c ba hm thnh phn na ca lp ios. Cc hm ny thit lp cc tham s nh dng gm c: rng ca trng, chnh xc v k t in vo. Cc hm ny l width( ), precision( ), v fill( ). Theo mc nh, vic xut mt tr s cn mt s khong trng bng vi s lng k t cn thit th hin con s . Tuy nhin khi s dng hm width( ), bn c th quy nh mt rng ti thiu ca trng. Dng th hin ca hm ny l:

493

Streamsize width(streamsize w) ; Here w becomes the field width, and the previous field width is returned. The streamsize type is defined by <iostream> as some form of integer. In some implementations, each time an output operation is performed, the field width returns to its default setting, so it might be necessary to set the minimum field width before each output statement. y w l rng ca trng, hm s tr v rng ca trng trc . Kiu streamsize c nh ngha bng <iostream> nh mt vi dng s nguyn. Trong khi ng dng, sau mi ln mt thao tc xut c thc hin, rng ca trng c tr li gi tr mc nh ca n, cho nn chng ta cn phi t li rng ca trng trc mi lnh xut. After you set a minimum field width, when a value uses less than the specified width, the field is padded with the current fill character (the space by default) so that the field width is reached. However, keep in mind that if the size of the output value exceeds the minimum field width, the field will be overrun. No values are truncated. By default, six digits of precision are used. You can set this number by using the precision( ) function. Its prototype is shown here: Sau khi thit lp rng trng ti thiu, nu gi tr c in ra c chiu di ngn hn rng ti thiu, trng s c thm vo mt hay nhiu k t in vo xut ra c chiu di bng ng vi rng ti thiu. Nhng nu nh gi tr c in ra cn s khong trng vt qu rng ti thiu, trng s b trn v gi tr khng b ct bt. chnh xc gm 6 ch s sau du chm thp phn l thit lp mc nh. Tuy nhin, chng ta c th quy nh li chnh xc bng hm precision( ). Dng hm nh sau: Streamsize precision(streamsize p); Here the precision is set to p and the old value is returned. By default, when a field needs to be filled, it is filled with spaces. You can specify the fill character by using the fill( ) function. Its prototype is shown here: Vi p l chnh xc cn thit lp v tr v gi tr l chnh xc trc . Theo mc nh, nu c mt trc cn c lp y, n s c lp bng cc k t khong cch. Nhng chng ta vn c th thay i k t khong trng dng lp vo bng k t khc nh vo hm fill( ). Dng hm nh sau: Char fill(char ch) ;

494

After a call to fill( ), ch becomes the new field character, and the old one is returned. Sau khi gi hm fill( ), ch tr thnh k t lp vo mi v tr v cho hm k t trc .

EXAMPLES (Cc v d):


Here is a program that illusrates the format functions: Chng trnh sau minh ha cho cc hm nh dng ni trn: #include <iostream> using namespace std ;

int main( ) { cout.width(10) ; cout << hello << \n ; default cout.fill(%) ; cout.width(10) ; cout << hello << \n ; default cout.setf(ios: : left) ; cout.width(10) ; // right justify by // set fill character // set width // right justify by // left- justify //set width // output left justified

cout << hello << \n ;

cout.width(10) ;

// set width

495

cout. precision(10);

// set 10 digits of precision

cout << 123.234567 < \n ; cout.precision(6) ; cout << 123.234567 << \n ; return 0; }

This program displays the following output: Chng trnh xut ra nh sau: hello %%%%%hello Hello%%%%% 123.234567 123.235%%%

Notice that the field width is set before each output statement. Ch l rng ca trng c nh li trc mi lnh xut. The following program shows how to use the C++ I/O format functions to create an aligned table of numbers: Chng trnh sau cho bit cch s dng cc hm nh dng nhp/xut ca C++ in ra mt bng s: // create a table of square roots and squares. #include <iostream> #include <cmath> using namespace std ;

496

int main( ) { double x ;

cout.precision(4) ; cout << x sqrt(x) x^2\n\n ;

for (x = 2.0 ; x <= 20.0 ; x++) { cout.width(7) ; cout << x << cout.width(7) ; cout << sqrt(x) << cout.width(7) ; cout << x*x << \n ; } ; ;

return 0 ; }

This program creates the following table: Chng trnh in ra bng s nh sau: x sqrt(x) 2 3 1.414 1.732 x^2 4 9

497

4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

2 2.236 2.449 2.646 2.828 3 3.162 3.317 3.464 3.606 3.742 3.873 4 4.123 4.243 4.359 4.472

16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400

EXERCISES:
Create a program that prints the natural log and base 10 log of the numbers from 2 to 100. Format the table so the numbers are right justified within a field width of 10, using a precision of five decimal places. Hy vit chng trnh in ra logarit t nhin v logarit c s 10 ca cc s t 2 n 100. nh dng bng s sao cho cc s c canh bin phi vi rng trng l 10. S dng chnh xc l 5. Create a function called center( ) that has this prototype: To ra mt hm gi l center( ) c nh dng nh sau:

498

Void center(char *s) ;

Have this function center the specified string on the screen. To accomplish this, use the width( ) function. Assume that the screen is 80 characters wide. (For simplicity, you may assume that no string exceeds 80 characters). Write a program that demonstrates that your function works. Chc nng ca hm ny l canh gia mt chui k t trn mn hnh. thc hin hm ny, hy s dng hm width( ). Gi s l mn hnh c rng 80 ct (v cho n gin, gi s chui k t c chiu di khng qu 80 k t). On your own, experiment with the format flags and the format functions. Once you become familiar with the C++ I/O system, you wil have no trouble using it to format output any way you like. Bn hy t thc hnh cc ni dung lin quan n cc c v hm nh dng. Mt khi bn quen thuc vi h thng nhp xut ca C++, bn s loi b c cc trc trc khi s dng n nh dng kt xut theo mun.

1.4. USING I/O MANIPULATORS S DNG B THAO TC NHP XUT


There is a second way that you can format information using C++s I/O system. This method uses special functions called I/O manipulators. As you will see, I/O manipulators are, in some situations, easier to use than the ios format flags and functions. C mt cch th 2 m bn c dng nh dng thong tin l s dng h thng I/O trong C++. Phng thc ny dng nh 1 hm c bit gi thao tc I/O.Nh bn thy, thao tc I/O, trong mt vi trng hp, d s dng hn nh dng c v hm ios. I/O manipulators are special I/O format functions that can occur within an I/O statement, instead of separate from it the way the ios member functions must. The 499

standard manipulators are shown in Table 8.1. As you will see, Many of the I/O manipulators parallel member functions of the ios class. Many of the I/O manipulators are shown in Table 8.1 were added recently to Standard C++ and will be supported only by modern compilers. Thao tc I/O thng l cc hm mu nhp xut m c th xut hin trong cu lnh I/O, thay v phi chia ra nh l cc hm thnh vin ca ios . Cc thao tc nhp xut chun c th hin trong bng 8.1. Nh bn thy, nhiu thao tc nhp xut I/O th tng ng vi cc hm trong lp ios.Nhiu thao tc I/O trong bng 8.1 mi c b sung vo th vin chun ca C++ v ch c h tr bi cc trnh bin dch hin i. To access manipulators that take parameters, such as setw(), you must include <iomanip> in your program. This is not necessary when you are using a manipulators that does not require an argument. truy xut n cc thao tc nhp xut m c tham s, chng hn nh setw(), bn phi thm vo th vin <iomanip> trong chng trnh ca bn. iu ny khng cn thit khi bn s dng thao tc nhp xut m khng c i s. As stated above, the manipulators can occur in the chain of I/O operations. For example: Cc thao tc nhp xut c th xut hin trong mt chui cc ton t I/O. V d nh: Cout<< oct<<100<<hex<<100; Cout<<setw(10)<<100;

The first statement tells cout to display integers in octal and then outputs 100 in octal. It then tells the stream to display integers in hexadecimal and then outputs 100 in hexadecimal format. The second statement sets the field to 10 and then displays 100 in hexadecimal format again. Notice that when a manipulator does not take an argument, such as oct in the example, it is not followed by parentheses. This is because it is the address of the manipulators that is passed to the overloaded << operator.

Cu lnh th nht dng cout hin th s nguyn trong h thp phn v sao xut 100 h thp phn. Sau tip tc hin th s nguyn trong h thp lc phn v xut 100 ra mn hnh trong nh dng thp lc phn. Dng lnh th 2 th t li trng 10 v xut ra 100 ra trong nh dng thp lc phn. Ch rng khi m mt thao tc nhp 500

xut khng c i s, chng hn nh l oct trong v d trn, n thng khng nm trong du ngoc n. V vy a ch ca thao tc c b qua np chng ton t <<. Manipulator boolalpha dec endl Purpose Turn on boolalpha flag Turns on dec flag Outputs a newline character and flushes the stream Output a null Tuens on fixed flag Flush a stream Turns on hex flag Turns on internal flag Turns on left flag Turns off boolalpha flag Turns off showbase flag Turns off showpoint flag Turn off showpos flag Turns off skipws flag Turn off unitbuf flag Turn off uppercase Turns on oct flag Turns off the flags specified in f Turns on right flag Turn on scientific flag Sets the number base to base Input/Output Input/output Input/output Output

ends fixed flush hex internal left noboolalpha noshowbase noshowpoint noshowpos noskipws nounitbuf nouppercase oct resetiosflags(fmtflag s f) right scientific setbase(int base)

Output Output Output Input/output Output Output Input/output Output Output Output Input Output Output Input/output Input/output Output Output Input/output

501

setfill( int ch) setiosflags( fmtflags f) setprecision( int p) setw(int w) showbase showpoint showpos skipws unibuf uppercase ws

Sets the fill character to ch Turn on the flags specified in f Sets the number of digits of precision Sets the field width to w Turns on showbase flag Turns on showpoint flag Turns on showpoint flag Turns on skipws flag Turns on unibuf flag Turns on uppercase Skips leadung white space

Output Input/output Output Output Output Output Output Output Output Output Input

Keep in mind that an I/O manipulator affects only the stream of which the I/O expression is a part. I/O manipulators do not affect all streams currently opened for use. As the preceding example suggests, the main advantages of using manipulators over the ios member functions is that they are often easier to use and allow more compact code to be written. Hy nh rng mt thao tc I/O ch nh hng trn dng m biu thc I/O nm trong . Thao tc I/O khng nh hng n tt c cc dng hin thi ang m. Nh v d trn, tc dng chnh ca vic dng thao tc nhp xut hn l cc hm ca ios l n thng d dng v cho php nhiu thao tc trn 1 dng. If you wish to set specific format flags manually by using a manipulator, use setiosflags(). This manipulator performs the same function as the member function setf(). To turn off flags, use the resectiosflags() manipulator. This manipulator is equivalent to unsetf(). Nu bn mun t 1 nh dng c c th thng dng bng cch s dng thao tc nhp 502

xut, s dng hm setiosflags(). Thao tc ny biu din ging nh l hm setf(). tt c, s dng thao tc resectiosflags() . Thao tc ny tng ng vi unsetf().

EXAMPLE:
This program demonstrates several of the I/O manipulators: Chng trnh m ra mt vi thao tc I/O: #include <iostream> #include <iomanip> using namespace std; int main() { cout<<hex<<100<<endl; cout<<oct<<10<<endl; cout<<setfill('X')<<setw(10); cout<<100<<"hi "<<endl; return 0; }

This program display the following: 64 12 XXXXXXX144hi

Here is another version of the program that displays a table of the squares and square roots of the numbers 2 through 20. This version uses I/O manipulators instead of member functions and format flags.

503

y l mt phin bn khc ca chng trnh hin th bng bnh phng v cn bc 2 ca mt s t 2 n 20. Trong phin bn ny thao tc I/O dng thay cho hm v nh dng c. #include <iostream> #include <iomanip> #include <cmath> using namespace std; int main() { double x; cout<<setprecision(4); cout<<" x sqrt(x) x^2\n\n";

for ( x=2.0; x<=20.0;x++) { cout<<setw(7)<<x<<" ; ";

cout<<setw(7)<<sqrt(x)<< " "; cout<<setw(7)<<x*x;'\n'; } return 0; }

One of the most interesting format flags added by the new I/O library is boolalpha . This flag can be set either directly or by using the new manipulator boolalpha or noboolalpha. What makes boolalpha so interesting is that setting it allows you to input and output Boolean values using the keywords true and false. Normally you must enter 1 for true and 0 for false. For example, consider the following program: Mt trong nhng iu th v ca c chun c thm vo bi th vin I/O mi l boolalpha. C ny c th c t ko trc tip hoc c s dng bi thao tc 504

boolalpha hoc noboolalpha. Ci g lm boolalpha tr nn th v? l thit lp n cho php bn nhp v xut gi tr lun l bng cch s dng bn phm true or false. Thng bn phi nhp 1 cho true v 0 cho false. Xem mt v d di y: #include <iostream> using namespace std; int main() { bool b; cout<<" Before setting boolalpha flag: "; b=true; cout<<b<<" "; b=false; cout<<b<<endl; cout<<"After setting boolalpha flag: "; b=true; cout<<boolalpha<<b<<" "; b=false; cout<<b<<endl; cout<<"Enter a Boolean value"; cin>>boolalpha>>b;//you can enter true or false cout<<"You entered "<<b; return 0; }

Here is sample run: Before setting boolalpha flag: 1 0

505

After setting boolalpha flag: true false Enter a Boolean value :true You entered true As you can see, once the boolalpha flag has been set, Boolean values are input and output using the words true or false. Notice that you must set the boolalpha flags for cin and cout separately. As with all format flags, setting boolalpha for own stream does not imply it is also set for another. Nh bn thy, mt ln c boolalpha c thit lp.Gi tr lun l c nhp v xut bng cch s dng t true hay false. Ch rng bn phi thit lp c boolalpha cho hm cin v cout. V vi tt c cc c nh dng, thit lp c boolalpha cho cc lung ring khng ko theo vic thit lp cho cc lung khc.

EXERCISES
Redo Exercise 1 and 2 form section 8.3, this time using I/O manipulators instead of member functions and format flags. Lm li bi 1 v 2 trong phn 8.3, ln ny dng thao tc I/O thay v dng hm thnh vin v c nh chun. Show the I/O statement that outputs the value 100 in hexadecimal with the base indicator ( the 0x) shown. Use the setiosflags() manipulator to accomplish this. Ch ra cu lnh I/O dng xut gi tr 100 trong h thp lc phn vi t ch dn l 0x . Dng thao tc setiosflags() thc hin iu ny. Explain the effect of setting the boolalpha flag. Gii thch tc dng ca vic t c boolalpha.

1.5. CREATING YOUR OWN INSERTERS TO B CHN VO:

506

As stated earlier, one of the advantages of the C++I/O system is that you can overload the I/O operators for classes that you create. By doing so, you can seamlessly incorporate your classes into your C++ programs, In this section you learn how to overload C++s output operator<<. Ging nh cc phn trc , mt trong nhng thun li ca h thng I/O trong C++ l bn c th np chng cc ton t cho lp ca bn to. Bng cch lm nh th, ban c th kt ni cht ch lp ca bn vo chng trnh C++ ca bn. Trong phn ny bn s hc c cch lm nh th no np chng ton t << trong C++. In the language of C++, the output operation is called an insertion and the << is called the insertion opertator. When you overload the << for output, you are creating an inserter function, or inserter for short. The rationale for these terms comes from the fact that an output operator inserts information into a stream. Trong ngn ng C++, thao tc xut c gi l mt b chn v << c gi l ton t chn. Khi bn np chng << cho vic xut d liu, bn ang to mt hm chn hoc l mt b chn ngn. L do c bn cho vic ny n t s tht l mt thao tc xut s a thng tin vo mt dng All inserter functions have this general form: Mi hm chn u c cu trc chung nh sau: Ostream & operator<<( ostream& istream, class name ob) { //body of inserter return stream; } The first parameter is a reference to an object of type ostream. This means that stream must be an output stream. ( Remember, ostream is derived from the ios class). The second parameter receives the object that will be output. ( This can also be reference parameter, if that is more suitable to your application.) Notice that the inserter function return if the overload << is going to be used in a series of I/O expressions, such as cout<<ob1<<ob2<<ob3; Tham s u tin l mt tham chiu ca i tng ca loi ostream. iu ny c ngha 507

l dng phi l mt dng xut. ( Nh rng, ostream c xut pht t lp ios). Tham s th 2 l mt i tng c xut ra.(n c th l tham s truyn theo a ch, nu iu l ph hp cho ng dng ca bn). Ch rng hm chn tr v nu s np chng << c dng trong mt dy cc lnh I/O, chng hn nh cout<<ob1<<ob2<<ob3 Within an inserter you can perform any type of procedure. What an inserter does is completely up to you. However, for the inserter to be consistent with good programming practices, you should limit its operations to outputting information to a stream. Trong mt b chn bn c th biu din bt k mt loi th tc no. Mt b chn lm g hon thin . Tuy nhin, mt b phi nht qun vi chng trnh, bn nn hn ch thao tc ca n xut thng tin trong dng Although you might find this surprising at first, an inserter cannot be a member of the class on which it is designed to operate. Here is why: When an operator function of any type is a member of a class, the left operand, which is passed implicitly through the this pointer, is the object that generates the calls to the operator function. This implies that the left operand is an object of that class. Therefore, if an overloaded operator function is a number of a class, the left operand must be an object of that class. However, when you create an inserter, the left operand is a stream and the right operand is the object that you want to output. Therefore, an inserter cannot be a member function. Vy m bn c th ngc nhin vi iu ny, mt b chn khng th l mt thnh phn ca lp. L do l : khi m hm ton t ca bt k loi no l mt thnh vin ca lp, ton hng bn tri, c th ngm nh b qua thng qua con tr this, l mt i tng gi hm ton t. Mt gi l ton hng bn tri l mt i tng ca lp. Bi vy, nu mt ton t c np chng cho mt s lp. ton hng bn tri phi l mt i tng ca lp.Tuy nhin khi bn tao mt b ci, ton hng bn tri l dng v ton hng bn phi l mt i tng ca lp m bn mun xut ra. V vy, mt b chn khng th l mt thnh phn ca lp . The fact that an inserter cannot be a member function might appear to be a serious flaw in C++ because it seems to imply that all data of a class that will be output using an inserter will need to be this is not the case. Even though inserters cannot be members of the class upon which they are designed to operate, they can be friends of the class. In fact, in most programming situations you will encounter, an overloaded inserter will be a friend of the class for which it was create. S tht mt b chn khng th l mt hm thnh vin c th xut hin l mt l hng nghim trng ca C++ bi v n dng nh l vic a tt c d liu ca lp s c

508

xut ra ngoi s dng b chn s tr nn cn thit ko phi trong trng hp ny. Mc d b chn ko th l thnh phn ca mt lp c thit k cho thao tc, chng c th l bn ca lp. S tht, trong hu ht chng trnh bn s gp vic ny, vic np chng mt b chn s l mt hm bn ca lp.

EXAMPLES
As a simple first example, this program contains an inserter for the coord class, developed in a previous chapter: y l mt v d n gin u tin, mt chng trnh cha mt b chn cho lp coord, pht trin t chng trc: // Use a friend inserter for objects of type coord #include <iostream> using namespace std;

class coord { int x,y; public: coord(){ x=0;y=0;} coord ( int i , int j){ x=i;y=j;} friend ostream& operator<<(ostream & stream, coord ib); }; ostream& operator<<(ostream & stream, coord ob) { stream<<ob.x<<","<<ob.y<<"\n"; return stream;

509

} int main() { coord a(1,1),b(10,23); cout<<a<<b; return 0; }

This program displays the following 1 ,1 10,23

The inserter in this program illustrates one very important point about creating your own inserters: make them as general as possible. In this case, the I/O statement inside the inserter outputs the values of x and y to stream, which is whatever stream is passed to the function. As you will see in the following chapter, when written correctly the same inserter that outputs to the screen can be used to output any stream. Sometimes beginners are tempted to write the coord inserter like this: B chn trong chng trnh minh ha mt iu rt quan trng v vic tao mt b chn cho chnh bn:lm chng mt cch tng qut. Trong trng hp ny, cu lnh nhp xut bn trong b chn xut mt gi tr ca x v y vo dng, m bt c ci g trn dng u vt qua hm. Nh bn thy chng trc, khi m vit nhiu b chn th vic xut ra mn c th c dng xut bt c dng no. Mt vi ngi mi lp trnh s b li cun vit b chn cho coord nh th ny: Ostream & operator << ( ostream& stream, coord ob) { cout<<ob.x<<ob.y<<\n; Return stream; }

510

In this case, the output statement is hard-coded to display information on the standard output device linked to cout. However, this prevents the inserter from being used by other streams. The point is that you should make your inserters as general as possible because there is no disadvantage to doing so. Trong trng hp ny, cu lnh xut l mt cu lnh ti ngha hin th thng tin trn thit b xut chun lien kt vi cout. Tuy nhin, vic ngn cn 1 b chn bng cch dng mt dng khc. im ny l im m bn co th lm cho b chn ca mnh tng qut nht bi v khng c bt k 1 s bt thun li no. For the sake of illustration, here is the preceding program revised so that the inserter is not a friend of the coord class. Because the inserter does not have access to the private part of coord, the variables x and y have to be made public. minh ha, y l mt chng trnh c trc c vit li m b chn khng phi l bn ca lp coord. Bi v b chn khng c php truy xut n thnh phn ring ca lp coord, gi tr ca x v y phi l public. // Use a friend inserter for objects of type coord #include <iostream> using namespace std;

class coord { public: int x,y; coord(){ x=0;y=0;} coord ( int i , int j){ x=i;y=j;} ostream& operator<<(ostream & stream, coord ib); }; ostream& operator<<(ostream & stream, coord ob)

511

{ stream<<ob.x<<","<<ob.y<<"\n"; return stream; } int main() { coord a(1,1),b(10,23); cout<<a<<b; return 0; }

An inserter is not limited to displaying only textual information. An inserter can perform any operation or conversion necessary to output information in a form needed by a particular device or situation. For example, it is perfectly valid to create an inserter will need to send appropriate plotter codes in addition to the information. To allow you to taste the flavor of this type of inserter, the following program creates a class called triangle, which stores the width and height of a right triangle. The inserter for this class displays the triangle on the screen. Mt b chn khng ch hn ch trong vic hin th thng tin. Mt b chn c th biu din bt k thao tc no hoc vic chuyn i cn thit xut thng tin trong nh dng cn bng mt thit b c bit hay hon cnh.V d nh, tht l hon ho to mt b chn gi n m in ph hp b sung vo thng tin. Cho php bn nm iu th v ca loi b chn ny, chng trnh di y to ra lp triangle, m ch chiu rng v cao ca mt tam gic vung. B chn ca lp ny hin th tam gic ny ra mn hnh. //This program draw right triangle #include <iostream> using namespace std; class triangle { int height,base; 512

public: triangle ( int h,int b){ height=h;base=b;} friend ostream& operator<<(ostream&stream,triangle ob); }; //Draw a triangle. ostream &operator<<(ostream & stream , triangle ob) { int i,j,h,k; i=j=ob.base-1; for ( h=ob.height-1;h;h--) { for (k=i;k;k--) stream<<' '; stream<<'*'; if (j!=i) { for (k=j-i-1;k;k--) stream<< ' '; stream<<'*'; } i--; stream<<'\n'; } for (k=0;k<ob.base;k--) stream<<'*'; 513

return stream; } int main() { triangle t1(5,5),t2(10,10),t3(12,12); cout<<t1; cout<<endl<<t2<<endl<<t3; return 0; }

Notice that this program illustrate how a properly designed inserter can be fully integrated into a normal I/O expression. This program displays the following. Ch rng chng trnh ny minh ha lm th thit k ng cch mt hm chn c th kt hp mt cch tt nht vi lnh nhp xut c bn. Chng trnh di y s biu hin iu ny.

EXERCISES
given the following strtype class and partial program, create an inserter that displays a string To hm chn cho lp strtype v 1 chng trnh nh hin th mt chui #include <iostream> #include <cstring> #include <cstdlib> using namespace std;

class strtype {

514

char *p; int len; public: strtype(char *ptr); ~strtype(){delete []p;} friend ostream& operator<<(ostream & stream,strtype& obj); }; strtype::strtype(char *ptr) { len =strlen(ptr)+1; p=new char[len]; if(!p) { cout<<"Allocation error\n"; exit(1); } strcpy(p,ptr); } //Create operator << inserter function here int main() { strtype s1("This is a test "),s2( "I like C++"); cout<<s1<<'\n'<<s2; return 0; } 515

Replace the show() function in the following program with an inserter function Thay th hm show() trong chng trnh di bng hm chn. #include <iostream> using namespace std; class planet { protected: double distance; int revolve; public: planet( double d, int r){distance =d; revolve = r;} }; class earth:public planet{ double public: earth ( double d,int r): planet(d,r) { circumference= 2*distance*3.1416; } void show() { cout<<"Distance from sun : <<distance <<'\n'; cout<< " Days in obit : "<<revolve<<'\n'; } 516 circumference;

}; int main() { earth ob(9300000,365); cout<<ob; return 0; } Explain why an inserter cannot be member function Gii thch ti sao mt b chn khng th l mt hm thnh vin trong class

1.6. CREATING EXTRACTORS TO B CHIT:


Just you can overload the << output operator, you can overload the >> input operator. In C++, the >> is referred to as the extraction operator and a function that overloads it is called an extractor. The reason for this term is that the act of inputting information from a stream removes ( that is, extracts) data from it. Khng ch vi ton t xut <<, bn cn c th np chng i vi ton t nhp >>. Trong C++, ton t >> c dng nh l mt ton t ngun v mt hm np chng c gi t hm ny. l l do gii hn trong thao tc nhp thng tin t mt dng s kin loi b d liu t n. The general form of an extractor function is shown here: Hnh thc chung ca mt hm nhp c th hin di y: istream & operator>>( istream & stream, class-name &ob) { // Body of extractor

517

Return stream; } Extractor return a reference to istream, which is an input stream. The first parameter must be a reference to an input stream. The second parameter is a reference to the object that is receiving input. Hm nhp tr v mt tham s l istream,nm trong dng nhp. Tham s u tin phi l tham chiu nhp t dng. Tham s th 2 l mt tham s ca i tng m s nhn thao tc nhp. For the same reason that an inserter cannot be a member function, any operation within an extractor, it is best to limit its activity to inputting information. Cng ging nh hm chn , mi thao tc trong hm nhp khng th l thnh vin ca mt lp, l hn cht trong hot ng nhp thng tin.

EXAMPLES
This program adds an extractor to the coord class: Chng trnh ny thm mt thao tc nhp vo lp coord #include <iostream> using namespace std;

class coord { int x,y; public: coord(){ x=0;y=0;} coord ( int i , int j){ x=i;y=j;} friend ostream& operator<<(ostream & stream, coord ib);

518

friend istream& operator>>(istream & stream, coord ib); }; ostream& operator<<(ostream & stream, coord ob) { stream<<ob.x<<","<<ob.y<<"\n"; return stream; } istream& operator>>(istream & stream, coord &ob) { stream<<ob.x<<","<<ob.y<<"\n"; return stream; } int main() { coord a(1,1),b(10,23); cout<<a<<b; cin>>a; return 0; } Notice how the extractor also prompts the user for input. Although such prompting is not required ( or even desired for most situations), this function shows how a customized extractor can simplify coding when a prompting message is needed. lm cch no m hm nhp cng nhc nh ngi dng nhp. Mc d khng cn thit ( .), hm ny ch ra lm cch no chn hm nhp c lm n gin m lnh khi m thng bo nhp tht s cn thit.

519

Here an inventory class is created that stores the name of an item, the number on hand, and its cost. The program includes both an inserter and an extractor for this class. y l mt lp kim k c to cha tn ca mt mn , s lng trn tay, v gi ca n. Chng trnh bao gm c hm chn v hm nhp trong lp. #include <iostream> #include <cstring> using namespace std; class inventory { char item[40]; int onhand; double cost; public: inventory(char *i,int o, double c) { strcpy(item,i); onhand=o; cost =c; } friend ostream& operator<<(ostream &stream, inventory ob); friend istream& operator<<(istream &stream, inventory &ob); }; friend ostream& operator<<(ostream &stream, inventory ob) { stream<<ob.item<<" : "<<ob.onhand;

520

stream<<"on hand at S" << ob.cost<<"\n"; return stream; } friend istream& operator<<(istream &stream, inventory &ob); { cout<< "Enter item name : "; stream>>ob.item; cout<<" Enter number on hand : "; stream>>ob.onhand; cout<<"Enter cost :"; stream >> ob.cost; return stream; } int main() { inventory ob("hammer",4,12,55); cout<<ob; cin>>ob; cout<<ob; return 0; }

EXERCISES
Add an extractor to the strtype class from exercise 1 in the preceding section Thm vo hm nhp cho lp strtype trong bi 1 trong phn trc

521

Create a class that stores an integer value and its lowest factor. Create both an inserter and an extractor for this class. To mt lp ch gi tr mt s nguyn v tha s nh nht. To c hm chn v hm nhp cho lp ny.

SKILL CHECK Mastery Skill Check (Kim tra k nng lnh hi) At this point you should be able to perform the following exercises and answer the questions. Ti thi im ny bn c th thc hin cc bi tp di y v tr li cc cu hi: Write a program that displays the number 100 in decimal, hexadecimal, and octal. ( Use the ios format flags) Vit chng trnh biu din s 100 trong h thp phn, h thp lc phn v h bt phn ( s dng nh dng c ios). Write a program that displays the value 1000.5364 in 20 character field, left justified, with two decimal places, using as a fill character. ( Use the ios format flags). Vit chng trnh hin th gi tr 100.5364 trong vng 20 k t, canh tri, vi 2 phn thp phn, s dng in y vo cc k t. ( Dng nh dng c ios). Rewrite your answers to Exercises 1 and 2 so that they use I/O manipulators. Vit li cu tr li cho bi 1 v 2 s dng thao tc I/O Show how to save the format flags for cout and how to restore them. Use either member functions or manipulators. Ch ra lm cch no lu li c chun choc out v lm sao khi phc li chng. S dng cc hm thnh vin khc hay l thao tc nhp xut. Create an inserter and an extractor for this class: To b chn v hm nhp cho lp ny Class pwr { Int base; Int exponent;

522

Double result;// base to the exponent power Public: Pwr(int b,int e); }; Pwr::pwr( int b,int e) { Base =b; Exponent =e; Result =1; For (; e;e--) result =result *base; }

Create a class called box that stores the dimensions of a square. Create an inserter that displays a square box on the screen. ( Use any method you like to display the box). To mt lp gi 1 ci hp cha kch thc ca hnh vung. To b chn hin th hnh lp phng ra mn hnh. ( S dng bt k phng thc no hin th hp)

Cumulative Skills checks- Kim tra k nng tch ly This section checks how well you have integrated material in this chapter with that from the preceding chapters. Trong phn kim tra ny s ch cho bn cch kt hp d liu t chng ny vi cc chng khc trc n. Using the Stack class shown here, create an inserter that displays the contents of the stack/ Demonstrate that your inserter works. S dng lp stack , to b chn hin th cc gi tr trong stack . #include <iostream> using namespace std;

523

#define SIZE 10 class stack { char stck[SIZE]; int tos; public: stack(); void push( char ch); char pop(); }; stack::stack() { tos=0; } void stack::push(char ch) { if(tos==SIZE) { cout<<"Stack is full \n"; return; } stck[tos]=ch; tos++; } char stack::pop()

524

{ if (tos==0) { cout<<"stack is empty\n"; return; } tos--; return stck[tos]; }

write a program that contains a class called watch. Using the standard time functions, have this classs constructor read the system time and store it. Create an inserter that displays the time. Vit chng trinh cha lp ng h. S dng hm thi gian chun, c mt lp dng c v cha gi h thng. Tao mt hm chn hin th thi gian. Using the following class, which converts feet to inches, create an extractor that prompts the user for feet. Also, create an inserter that displays the number of feet and inches. Include a program that demonstrates that your inserter and extractor work. S dng lp di y,lp chuyn t feet sang inches, to mt hm nhp nhc nh ngi dng nhp dng feet. V to mt b chn hin th s dng feet v inches. Bao gm c chng trnh m t hot ng ca hm nhp v b chn . Class ft_to_inches { double feet; Double inches; Public: Void set ( double f) { feet=f;

525

Inches= f*12; } };

CHAPTER 9 ADVANCE C++ I/O NHP XUT NNG CAO CA C++


Chapter objective
9.1 9.2 9.3 9.4 526 CREATING YOUR OWN MANIPULATORS To cc thao tc ring FILE I/O BASICS File I/O c bn UNFORMATTED BINARY I/O I/O nh phn khng nh dng MORE UNFORMATTED I/O FUNCTIONS

Nhng hm I/O khng nh dng tip theo 9.5 9.6 9.7 RANDOM ACCESS Truy xut ngu nhin CHECKING I+N THE I/O STATUS Kim tra trng thi I/O CUSTOMIZED I/O AND FILES Ty bin I/O v file

This chapter continues the examination of the C++ I/O system. In it you will learn to create your own I/O manipulators and work with files. Keep in mind that the C++ I/O system is both rich and flexible and contains many features. While it is beyond the scope of this book to include all of those features, the most important ones are discussed here. A complete description of C++ I/O system can be found in my book C++. The Complete Reference (Berkely: Osborne/McGraw-Hill) Chng ny tip tc xem xt h thng nhp/xut ca C++. y bn s c hc v cch to ra cc b thao tc nhp/xut ca chnh mnh v cch thc hin nhp xut tp tin. Hy nh l h thng nhp/xut ca C++ c tht nhiu hm v chng rt linh ng. Nhng trong phm vi c hn ca quyn sch ny chng ta khng th trnh by cc hm ni trn, thay vo , mt s hm quan trng nht s c cp n. c bit y v h thng ny, bn c th tm thy trong cun sch sau y ca ti: The Complete Reference.

The C++ I/O system described in this chapter reflects the one defined by Standard C++ and is compatible with all major C++ compilers. If you have an older of nonconforming compiler, its I/O system will not have all the capabilities described

527

here. Ch : H thng nhp/xut ca C++ phn nh chun tr nh ca C++ chun v tng thch phn ln cc trnh bin dch C++. Nu bn ang s dng mt phin bn c hay khng tng thch th h thng nhp/xut ca phin bn ny s khng th thc hin c cc tnh nng s c ni n di y. Before proceeding, you should be able to correctly answer the following questions and do the exercises. Trc khi i vo ni dng chnh ca chng, bn hy tr li ng cc cu hi v lm cc bi tp sau. Write a program that displays the sentence C++ is fun in a 40-character-wide field using a colon (:) as the fill character. 1. Vit chng trnh in cu C++ is fun vi rng ca trng l 40 v s s dng du 2 chm (:) lm k t lp vo. Write a program that displays the outcome of 10/3 to three decimal places. Use ios member functions to do this. 2. Vit chng trnh trnh by kt qu ca biu thc 10/3 vi chnh xc l 3 ch s sau du thp phn. Hy s dng cc hm thnh phn ca ios. Redo the preceding program using I/O manipulators. Lm li bi tp 2 s dng b thao tc nhp xut thay cho hm. What is an inserter? What is an extractor? B chn l g, b chit l g? Given the following class, create an inserter and extractor for it. Cho lp nh di y hy to b chn v b chit cho n: class date {

528

char date[9] ; public: // add inserter and extractor };

What header must be included if your program is to use I/O manipulators that take parameters? Ta phi np tp tin tiu no s dng c b thao tc nhp xut c tham s? What predefined streams are created when a C++ program begins execution? Cc lung no c t ng m ra khi thc thi mt chng trnh C++?

9.1. CREATING YOUR OWN MANIPULATORS TO CC THAO TC RING


In addition to overloading the insertion and extraction operators, you can further customize C++s I/O system by creating your own manipulator functions. Custom manipulators are important for two main reasons. First, a manipulator can consolidate a sequence of several separate I/O operators. For example, it is not uncommon to have situations in which the same sequence of I/O operators occurs frequently within a program. In these cases you can use a custom manipulator to perform these actions, thus simplifying your source code and preventing accidental errors. Second, a custom manipulator can be important when you need to perform I/O operators on a nonstandard device. For example, you could use a manipulator to send control codes to a special type of printer or an optical recognition system. Ngoi vic qu ti ton t chn v trch lc, bn cn c th thit k h thng nhp xut ca C++ theo cc hm thao tc ca chnh bn. Vic to cc th tc rt quan trng bi hai nguyn do sau. Th nht, 1 th tc c th cng c mt dy cc ton t khc nhau. V d, n s khng c g l khi c mt s tnh hung m ging nhau xy ra vi cc ton t bn trong chng trnh. Trong nhng trng hp ny bn c th dung mt th tc ty bin thc hin cc hnh ng ny, v n gin ha on m ca bn v trnh 529

nhng li nguy hi. Th hai, mt th tc ty bin c th quan trng khi bn cn s dng cc ton t trn mt thit b khng chuyn dng. V d, ban c th dung th tc gi mt m iu khin n mt chic my in loi c bit no hay mt h thng nhn dng quang hc. Custom manipulators are a feature of C++ that supports OOP, but they can also benefit programs that arent object-oriented. As you will see, custom manipulators can help make any I/O-instensive program clearer and more efficient. As you know, there are two basic types of manipulators: those that operate on input streams and those that operate on output streams. In addition to these two broad categories, there is a secondary division: those manipulators that take an argument and those that dont. There are some significant differences between the way a parameterless manipulator and a parameterized manipulator are created. Further, creating parameterized manipulator is substantially more difficult than creating parameterless ones and is beyond the scope of this book. However, writing your own parameterless manipulators is quite easy and is examined here. All parameterless manipulator output functions have this skeleton: Cc th tc ty bin l mt tnh nng ca C++ m h tr cho OOP, nhng chng cng c li cho nhng chng trnh m khng hng i tng. Nh bn s thy, cc th tc ty bin c th gip bn to ra bt k chng trnh mnh m mt cch r rng hn v hiu qu hn. Nh bn bit, c hai loi th tc c bn : th tc iu khin nhng lung vo v th tc iu khin nhng lung ra. Ngoi hai loi ny, cn c mt cch chia th hai: cc th tc m mang mt i s v khng mang i s. C hai s khc nhau th tc khng c tham s v c tham s l b to. Hn na vic to th tc c tham s th kh hn to ra th tc khng tham s v nm ngoi phm vi cp ca sch. Tuy nhin, t vit cc th tc khng tham s kh d v c cp y. Tt c hm th tc khuyt tham s u c khung sn nh sau: ostream &manip-name(ostream &stream) { // your code here Return stream; } Here mani-name is the name of the manipulator and stream is a reference to the 530

invoking stream. A preference to the stream is returned. This is necessary if a manipulator is used as part of a larger I/O expression. It is important to understand that even though the manipulator has as its single argument a preference to the stream upon which it is operating, no argument is used when the manipulator is called in an output operation. All parameterless input manipulator functions have this skeleton: y many-name l tn ca th tc v stream l s tham kho cho dn chng lung. Mt s u tin cho lung l tr v. Tht cn thit nu mt ton t c dng nh mt phn ca vic biu din nhp xut ln hn. Quan trng l phi hiu rng mc d th tc c i s u tin cho lung m n iu khin, th khng i s c dng khi th tc gi vic xut. Tt c thc tc khng i s c dng sau:

istream &manip-name(istream &stream) { // your code here Return stream; } An input manipulator receives a reference to the stream on which it was invoked. This stream must be returned by the manipulator.

Mt b thao tc nhp nhn mt tham chiu n lung m n thao tc trn . Lung ny phi c tr v bi b thao tc. It is crucial that your manipulators return a reference to the involking stream. If this is not done, your manipulators cannot be used in sequence of input or output operations.

531

EXAMPLES
As a simple first example, the following program creates a manipulator called setup( ) that sets the field width to 10, the precision to 4, and the fill character to *. Trong v ny, chng trnh to ra mt b thao tc mi tn l setup(). Khi c gi b thao tc ny s thit lp rng trng l 10, chnh xc l 4, v k t lp y l *. #include <iostream> using namespace std;

ostream &setup( ostream &stream) { Stream.width(10); Stream.precision(4); Stream.fill(*);

Return stream; }

int main() { Cout << setup << 123.123456;

Return 0; }

As you see, setup is used as part of an I/O expression in the same way that any of the built in manipulators would be used. Bn thy , setup c dng nh mt phn ca nhp xut theo cch m cc b thao 532

tc xy dng c s dng. Custom manipulators need not be complex to be useful. For example, the simple manipulators atn() and note(), shown here, provide a shorter way to output frequently use words or phrases. To th tc khng cn phi phc tp m phi hu dng. V d, th tc atn( ) v note() trnh by y, n gin l cung cp mt cch ngn xut m dng dng cc t hay cm t. #include <iostream> using namespace std;

// Attention: ostream &atn:ostream &stream { Stream << Attention: ; Return stream; }

// Please note: ostream &note(ostream &stream) { stream << Please note: ; return stream; }

int main() { cout << atn << High voltage circuit \n;

533

cout << note << Turn off all lights\n;

return 0; } Even though they are simple, if used frequently, these manipulators save you from some tedious typing. Mc d chng n gin nhng nu s dng nhun nhuyn th chng s gip bn trnh c nhiu phin toi. This program creates the get pass() input manipulator, which rings the bell and then prompts for a password: Chng trnh ny to ra th tc nhp getpass(), m n s rung chung bo v nhc password #include <iostream> #icnlude <cstring> using namespace std;

// a simple input manipulator istream &getpass(istream &stream) { cout << \a ; // sound bell cout << enter password: ;

return stream; }

int main()

534

{ char pw[80];

do { cin >> getpass >> pw; }while( strcmp(pw, password));

cout <<

log on complete\n;

return 0; }

EXERCISES
Create an output manipulator that displays the current system time and date. Call this manipulator td() To th tc xut ra h thng ngy gi hin hnh, mang tn td() Create an output manipulator called sethex() that sets output to hexadecimal and turns on the uppercase and showbase flags. Also, create an output manipulator called reset() that undoes the changes made by sethex(). To mt th tc xut mang tn sethex() m xut ra h thp lc phn v chuyn ch thnh hoa v xem c. ng thi to mt th tc xut mang tn reset m khng lm thay i sethex() Create an input manipulator called skipchar() that read and ignores the next ten characters from the input stream. To mt th tc nhp mang tn skipchar() m c v b qua 10 k t tip theo ca chui nhp.

535

9.2. FILE I/O BASICS NHP XUT FILE C BN


It is now time to turn our attention to file I/O. As mentioned in the preceding chapter, file I/O and console I/O are closely related. In fact, the same class hierarchy that supports console I/O also supports file I/O. Thus, most of what you have already learned about applies to files as well. Of course, file handling makes use of several new features. By gi hy hng s quan tm n nhp xut file. Nh cp trong chng trc, nhp xut file v bn iu khin nhp xut gn ging nhau. Thc ra, lp phn cp h tr cho bn iu khin th cng h tr cho file. V vy, hu nh nhng g bn hc c u p dng tt cho file. Tt nhin, kim sot file cn s dng them mt vi tnh nng mi. To perform file I/O, you must include the header <fstream> in your program. It defines several classes, including ifstream, ofstream, and fstream. These classes are derived from istream and ostream. Remember, istream and ostream are derived from ios, so ifstream, ofstream, and fstream also have access to all operations defined by ios (disccused in the preceding chapter). x l trn file bn cn thm vo header <fstream> trong chng trnh ca mnh. N nh ngha mt vi lp, bao gm c ifstream, ofstream, and fstream. Nhng lp ny xut pht t istream v ostream. . Nh l, istream v ostream xut pht t ios, nh vy ifstream, ofstream, and fstream cng c th truy xut n bt k phng thc no c nh ngha bi ios ( bn trong chng trc). In C++, a file is opened by linking it to a stream. There are three types of streams: input, output, and input/output. Before you can open a file, you must first obtain a stream. To create an input stream declare an object of type ifstream. To create an output stream declare an object of type ofstream. Streams that will be performing both input and output operations must be declared objects of type fstream. For example, this fragment creates one input stream, one output stream, and one stream capable of both input and output. Trong C++, mt file c m bng lin kt n 1 lung. C ba loi lung l: nhp, xut hay c hai. Trc khi bn c th m 1 file, bn phi quan st 1 lung trc tin. to 1 lung nhp th t 1 i tng kiu ifstream. to 1 lung xut th t 1 i tng kiu ofstream. Nhng lung ny s thc thi trn c hai loi phng thc phi c nh ngha nh mt i tng kiu fstream. V d, on sau to ra 1 lung nhp v 1 lung xut v mt ci tch hp c hai loi nhp v xut.

536

Istream in; Ostream out; Fstream io;

// input // output // input and output

Once you have created a stream, one way to associate it with a file is by using the function open(). This function is a member of each of the three file stream classes. The prototype for each is shown here: Mt khi bn to ra mt lung, mt cch tip cn file bng hm open(). Hm ny l mt thnh vin ca 1 trong 3 lp file lung. Cch thc lm nh sau: void ifstream::open(const char* filename, openmode mode=ios::In; void ofstream::open(const char* filename, openmode mode=ios::out | ios::trunc); void fstream::open(const char* filename, openmode mode= ios::in | ios::out); Here filename is the name of the file, which can include a path specifier. The value of mode determines how the file is opened. It must be a value of type openmode, which is an enumeration defined by that contains the following values: y, filename l tn file, bao gm ng dn phi c ch r. Gi tr ca mode quyt nh file c m ra sao. N phi l gi tr dng openmode, m c lit k theo sau y: ios::app ios::ate ios::binary ios::in ios::out ios::trunc You can combine two or more of these values by ORing them together. Lets see what each of these values means.

537

Bn c th kt hp 2 hay nhiu gi tr ny vi nhau. Hy xem ngha ca cc gi tr ny. Including ios::app causes all output to that file to be appended the end. This value can be used only with files capable of output. Including ios::ate causes a seek to the end of the file to occur when the file is opened. Although ios::ate causes a seek to end-of-file, I/O operations can still occur anywhere within the file. Vic bao hm ios::app lm cho vic xut vo 1 file s c thm vo cui file. Gi tr ny c th dng khi c kh nng xut file. Bao hm ios::ate gy ra mt s tm kt thc file ngay khi m file. Mc d ios::ate gy ra 1 s tm kt thc file nhng cc thao tc xut nhp vn c th thc thi ti bt k u bn trong file.

The ios::in value specifies that the file is capable of input. The ios::out value specifies that the file is capable of output. Gi tr ios::in ch ra file c kh nng nhp. Gi tr ios::out ch ra file c kh nng xut. The ios::binary value causes a file to be opened in binary mode default, all files are opened in text mode. In text mode, various character translations might take place, such as carriage return/linefeed sequences being converted into newlines. However when a file is opened in binary mode, no such character translation will occur. Keep in mind that any file, whether it contains formatted text or raw data, can be opened in either binary or text mode. The only difference is whether character translations take place. Gi tr ios::binary khin cho 1 file b m trong ch nh phn, mc nh tt c cc file c m dng text. Trong dng text, nhiu s chuyn m k t c th xy ra, nh dy cc dng tr v s b chuyn thnh dng mi. Tuy nhin khi 1 file c m trong dng nh phn th chuyn khng xy ra. Hy nh rng bt k file nu cha dng vn bn hay d liu th, c th m c c hai dng nh phn v text. S khc bit duy nht l s chuyn m xy ra. The ios::trunc value causes the contents of preexisting file by the same name to be destroyed and the file to be truncated to zero length. When you create an output stream using ofstream, any preexisting file with the same name is automatically truncated. Gi tr ios::trunc lm cho ni dung ca file tn ti trc mang cng tn b ph hy v ct ct chiu di thnh 0. Khi bn to 1 lung xut dng ofstream, bt k file no bn to ra trc t ng b ct ct.

538

The following fragment opens an output file called test: on sau m 1 file xut mang tn l test ofstream mystream; mystream.open(test); Since the mode parameter to open() defaults to a value appropriate the type of stream being opened, there is no need to specify its value in the preceding example. V dng tham s hm open() mc nh l 1 gi tr tng ng vi kiu lung c m, nn khng cn ch r gi tr trong v d trc. If open() fails, the stream will evaluate to false when used in a Boolean expression. You can make use of this fact to confirm that the open operation succeeded by using a statement like this: Nu open() tht bi, lung s nh gi sai khi dng 1 biu thc Boolean. Bn c th dng s thc ny kim chng vic m file thnh cng bng cu lnh sau: if(mystream) { cout << Cannot open file.\n; // handle error } In general, you should always check the result of a call to open() before attempting to access the file. You can also check to see if you have successfully opened a file by using the is_open() function, which is a member of fstream, ifstream, and ofstream. It has this protptype: Tm li bn nn kim tra xem c m c file khng trc khi th x l file. Bn cng c th kim tra xem c m c file khng bng hm is_open(), y l hm thnh vin ca of fstream, ifstream, and ofstream. N c dng sau: bool is_open(); It returns true if the stream is linked to an open file and false otherwise. For example, the following checks if mystream is currently open: N tr v true nu lin kt c n file v false khi ngc li. V d, sau khi kim tra

539

nu mystream hin c m: If ( !mystream.is_open()) { Cout << File is not open.\n; // } Although it is entirely proper to open a file by using the open() function, most of the time youll not do so because the ifstream, ofstream, and fstream classes have constructor functions that automatically open the file. The constructor functions have the same parameters and defaults as the open() function. Therefore, the most common way you will see a file opened is shown in this example: Mc d hon ton c th m 1 file bng hm open(), nhng hu ht thi gian bn s khng lm vy v cc lp ifstream, ofstream, and fstream u c hm to t ng m file. Hm to ny c nhng tham s v mc nh ging vi hm open(). V vy, cch chung l : ifstream mystream(my file); //open file for input As stated, if for some reason the file can not be opened, the stream variable will evaluate as false when used in a conditional statement. Therefore, whether you use a constructor function to open the file or an explicit call to open(), you will want to confirm that the file has actually been opened by testing the value of the stream. Nh ni, nu v l do no file khng m c, th bin ca lung s mang gi tr false khi dng trong li pht biu iu kin. V vy, nu bn dng 1 hm to m file thay cho open(), bn s mun xc nhn rng file thc s m c bng vic kim tra gi tr ca lung. To close a file, use the member function close(). For example, to close the file linked to a stream called mystream, use this statement: ng mt file, dng hm thnh vin close(). V d, ng lin kt t n lung gi mystream, theo cch sau mystream.close(); The close() function takes no parameters and returns no value. You can detect when the end of an input file has been reached by using the eof() member function of ios. It has this prototype:

540

Hm close() khng c tham s v gi tr tr v. Bn c th kim tra kt thc file nhp bng vic dng hm thnh vin eof(). N c dng sau: bool eof(); It returns true when the end of file has been encountered and false otherwise. N s tr v true khi gp kt thc file v false trong trng hp cn li. Once a file has been opened, it is very easy to read textual data from it or write formatted, textual data to it. Simply use the << and >> operators the same way you do when performing console I/O, except that instead of using cin and cout, substitute a stream that is linked to a file. In a way, reading and writing files by using >> and << is like using Cs fprintf() and fscanf() functions. All information is stored in the file in the same format it would be in if displayed on the screen. Therefore, a file produced by using << is a formatted text file, and any file ready by >> must be a formatted text file. Typically, files that contain formatted text that you operate on using >> and << operators should be opened for text rather than binary mode. Binary mode is best used on unformatted files, which are described later in this chapter. Mt khi file c m, th d dng c c d liu nguyn vn trong n hay vit d liu c nh dng vo n. on gin vi ton t >> v << ging nh cch m bn thao tc vi bng iu khin nhp xut, ngoi tr thay v dng cin v cout, thay th cho lung m lin kt 1 file. Theo cch ny, c v vit files dng >> hay << ging nh trong cc hm ca C l fprintf() v fscanf(). Tt c d liu lu tr trong file ging nh dng m n c in ra mn hnh. V vy, vit vo file bng << th phi l file dng text chun, v bt k file no dng >> th phi l dng file chun. in hnh l, cc file m cha vn bn c nh dng m bn thao tc bng cc ton t >> v << nn c m dng text tt hn m bng dng nh phn. Dng nh phn ch dng ti u cho cc file cha r nh dng s c ni n trong chng sau.

EXAMPLES
Here is a program that creates an output file, writes information to it, closes the file and opens it again as an input file, and reads in the information: y l mt chng trnh to mt file xut, ghi thng tin ln , ng file li ri m li file mt ln na nh l mt file nhp, v c d liu trong file ny. #include <iostream> 541

#include <fstream> using namespace std;

int main() { ofstream fout(test) ; //create output file

if (!fout)

cout << Can not open output file. \n; return 1; } fout << Hello! \n; fout << 100 << << hex << 100 << endl;

fout.close();

ifstream fin(test); // open input file

if (!fin) { cout << Can not open input file.\n; return 1; }

char str[80]; int i, j;

542

fin >> str >> i >> j; cout << str << << i << << j << endl;

fin.close();

return 0; }

After you run this program, examine the content of test. It will contain the following: Sau khi bn chy chng trnh, kim tra ni dung ca vic chy ny. N s c kt qu nh sau: Hello! 100 64 As stated earlier, when the << and >> operators are used to perform file I/O, information is formatted exactly as it would appear on the screen. Trong trng thi trc, khi m ton t << v >> c dng thc hin file I/O, thng tin c nh dng chnh xc nh l khi chng xut hin trn mn hnh sau . Following is another example of disk I/O. This program reads strings entered at the keyboard and writes them to disk. The program stops when the user enters as $ as the first character in a string. To use the program, specify the name of the output file on the command line. Di y l mt v d khc ca i/O. Chng trnh ny c nhng chui nhp vo t bn phm v ghi chng vo d liu. Chng trnh dng khi ngi dng nhn $ nh l k t u tin trong mt chui. Khi s dng chng trnh, ch r tn ca file xut trn dng lnh bt u. #include <iostream> #include <fstream> using namespace std;

543

int main(int argc, char *argv[]) { if (argc!=1) {

cout << Usage: WRITE <filename> \n; return 1; }

ofstream out(argv[0]) ; // output file

if (!out) { cout << Can not open output file. \n; return 1; }

char str[80]; cout << Write strings to disk, $ to stop\n;

do

{ cout << : ; cin >> str; out << str << endl;

}while(*str != $);

out.close();

544

return 0; }

Following is a program that copies a text file and, in the process, converts all spaces into | symbols. Notice how eof() is used to check for the end of the input file. Notice also how the input stream fin has its skipws flag turned off. This prevents leading spaces from being skipped. Chng trnh di y sao chp mt file vn bn, v trong qu trnh thc thi, chuyn i tt c cc ch trng thnh k t |. Ghi ch: hm eof() dng kim tra xem c c ht file cha. them l c fin v skipws trong dng thc thi c tt nh th no. S ngn cch bi khong trng c b qua. // Convert space to |s. #include <iostream> #include <fstream> using namespace std; int main(int argc, char *argv[]) { if (argc!=3) {

cout <<Usage: CONVERT <input> <output> \n; return 1; }

ifstream fin(argv[1]) ; // open input file ofstream fout(argv[2]) ; // create output file

if (!fout)

cout << Can not open output file. \n; return 1;

545

if (!fin) { cout << Can not open input file. \n; return 1; }

char ch;

fin.unsetf(ios::skipws); // do not skip spaces

while (!fin.eof()) fin >> ch; if(ch == )

ch = |;

if( !fin.eof()) fout << ch; }

fin.close(); fout.close();

return 0; }

There are a few differences between C++s original I/O binary and the modern Standard C++ library that you should be aware of, especially if you are converting older code. First, in the original I/O library, open() allowed a third parameter, which specified the files protection mode. This parameter defaulted to a normal 546

file. The modern I/O library does not support this parameter. C mt vi s khc bit gia th vin I/O nh phn gc v th vin chun hin i trong C++ m bn nn ch , c bit l khi bn chuyn i cc on m vit theo phong cch c. Trc ht, trong th vin I/O gc, hm open() ph thuc vo tham s th 3, tham s ny s ch r trng thi bo v ca file( trng thi thc thi). Tham s ny c xc lp mc nh cho file bnh thng. Cn trong th vin I/O mi th hon ton khng h tr cho tham s ny. Second, when you are using the old library to open a stream for input and output using fstream, you must explicitly specify both the ios::in and the ios::out mode values. No default value for mode supplied. This applies to both the fstream constructor and to its open() function. For example, using the old I/O library you must use a call to open() as shown here to open a file for input and output: iu th 2 l khi bn bn s dng th vin c m mt dng thc thi nhp v xut s dng dstream, bn phi ch r gi tr m t ca c ios::in v ios::out. Khng c gi tr mc nh no c cung cp. p dng ny c dng cho c phng thc khi to fstream v hm open() ca n. V d nh, s dng th vin I/O c bn phi gi hm open() ch r u l file nhp u l file xut. fstream mystream; mystream.open(test, ios::in | ios::put); In the modern I/O library, an obj of type fstream automatically opens files for input and output when the mode parameter is not supplied. Trong th vin I/O mi, mt i tng ca fstream t ng m file nhp v xut khi m i s trng thi khng c h tr. Finally, in the old I/O system, the mode parameter could also include either ios::nocreate, which causes the open() function to fail if the file does not already exist, or ios::noreplace, which causes the open() function to fail if the file does already exist. These values are not supported by Standard C++. Cui cng, trong h thng I/O c, tham s trng thi cng ch c th bao gm ios::nocreate- l nguyn nhn lm cho hm open() bo li khi m file khng thc s tn ti- hoc l ios::noreplace- l nguyn nhn lm cho hm open() bo li khi m file thc s tn ti. Nhng gi tr ny khng c h tr trong th vin C++ chun.

EXERCISE

547

Write a program that will copy a text file. Have this program count the number of characters copied and display this result. Why does the number displayed differ from thet shown when you list the output file in the directory? Vit chng trnh sao cho ni dung ca file vn bn. m s lng cc k t sao chp v hin th kt qu. Gii thch ti sao s lng hin th li khc so vi khi bn duyt file xut trong th mc. Write a program that writes the following table of information to a file called phone: Vit mt chng trnh ghi d liu trong bng di vo file cc cuc gi. Isaac Newton, 415 555-3423 Robert Goddard, 213 555-2312 Enrico Fermi, 202 555-1111 Write a program that counts the number of words in a file. For simplicity, assume that anything surrounded by whitespace is a word. Vit chng trnh n s lng t trong file. n gin, gi s rng mi t cch nhau mt khong trng.

What does is_open() do? Hm is_open() thc thi ci gi?

9.3. UNFORMATTED, BINARY I/O - NHP XUT NHI PHN KHNG INH DANG
Although formatted text files such as those produced by the preceding examples are useful in a variety of situations, they do not have the flexibility of unformatted, binary files. Unformatted files contain the same binary representation of the data as that used internally by your program rather than the human-readable text that data is translated into by the << and >> operators. Thus, unformatted I/O is also referred to as raw I/O. C++ supports a wide range of unformatted file I/O functions. The unformatted functions give you detailed control over how files are written and read. Mc d file vn bn c nh dng trong c v di trong nhng v d trc th rt hiu dng trong nhiu trng hp, nhng n li khng c s uyn chuyn khi m vn 548

bn khng c nh dng, chng hn nh file nh phn.File khng nh dng cha d liu c biu din dng nh phn ging nh d liu c c bi chng trnh kh hn l cch ngi c vn bn m s dng ton t >> v << dch d liu. V vy, Vic I/O khng nh dng cng c quy vo nh l nguyn gc. C++ h tr cch thc gii quyt cc file khng nh dng I/O bng nhiu hm. Hm khng nh dng s cho bn s iu khin chi tit bit cch c v ghi mt file. The lowest-level unformatted I/O function are get() and put(). You can read a byte by using get() and write a byte by using put(). These functions are members of all input and output stream classes, respectively. The get() function has many form, but the most commonly used version is shown here, along with put(): Cp thp nht ca cc hm khng nh dng I/O l get() v put(). Bn c th c mt byte bng cch dng hm get() v ghi mt byte bng hm put().Nhng hm l thnh phn ca cc lp nhp xut theo dng, theo th t nh sn. Hm get() c nhiu hnh thc th hin, nhng c mt hnh thc c dng ph bin nht c th hin di y, i km l hm put(): istream &get(char &ch); ostream &put(char ch); The get() function reads a single charater from the associated stream and puts that value in ch. It returns a reference to the stream. If a read is attempted at end-of-file, on return the invoking stream will evaluate to false when used in an expression. The put() function writes ch to the stream and returns a reference to the stream. Hm get() c mt k t t dng thc thi v t gi tr vo ch. Hm tr v mt tham chiu ca dng thc thi. Nu n c n ht file, n s yu cu dng thc thi tr v gi tr sai khi nm trong mt biu thc. Hm put() ghi ch vo dng thc thi v tr v mt tham chiu cho dng thc thi. To read and write blocks of data, use the read() and write() function, which are also members of the input and output stream classes, respectively. Their prototypes are shown here: c v ghi mt khi d liu, dng hm read() v write(), l hm thnh vin ca lp nhp xut theo dng. V d nh: istream &read(char *buf, streamsize num); ostream &write(const char *buf, streamsize num); The read function reads num bytes from the invoking stream and puts them in the

549

buffer pointed to by buf. The write() function writes num bytes to the associated stream from the buffer pointed to by buf. The streamsize type is some form of interger. An obj of type streamsize is capable of holding the largest number of bytes that will be transferred in any one I/O operation. Hm read() c mt s byte t dng thc thi v t chng vo vng m c tr n bi buf. Hm write() ghi mt s byte vo dng thc thi kt hp t vng m c tr n bi buf. Loi streamsize th tr v mt gi tr nguyn, mt i tng ca streamsize c kh nng lu gi mt lng ln cc byte m c vn chuyn trong bt k thao tc I/O no. If the end of the file is reached before num characters have been read, read() simply stops, and the buffer contains as many characters as were available. You can find out how many charaters have been read by using the member function gcount(), which has this prototype: Khi n cui file trc khi s lng k t c c, hm read() s dng li, v vng m s ch cc k t c ngha. Bn c th tm ra nhiu k t c c bng cch s dng hm thnh vin gcount, c minh ha di y: streamsize gcount(); It returns the number of characters read by the last unformatted input operation. N tr v s lng cc k t c c bi thao tc c khng nh dng. When you are using the unformatted file functions, most often you will open a file for binary rather than text operations. The reason for this is easy to understand: specifying ios::binary prevents any character translations from occurring. This is important when the binary representations of data such as integers, floats, and pointers are stored in the file. However, it is perfectly acceptable to use the unformatted functions on a file opened in text mode-as long as that file actually contains only text. But remember, some character traslations may occur. Khi m bn s dng hm c file khng nh dng, hu ht l dng m file nh phn hn l m file vn bn. L do tht l d hiu: chi ro ios::binary ngn s xut hin cua bt ki ki t dich nao. iu nay la quan trong khi dang nhi phn cua d liu nh la s nguyn, s thc, va con tro c lu tr trong tp tin. Tuy nhin, co th chp nhn c khi dung ham khng inh dang trong mt tp tin dang vn ban-u dai tp tin o cha duy nht vn ban. Nhng nh rng, mt s ki t dich co th xut hin.

550

EXAMPLES
The next program will display the contents of any file on the screen. It uses the get() function. Chng trinh tip theo se trinh bay ni dung cua bt ki tp tin trn man hinh. No dung ham get(). #include <iostream> #include <fsream> using namespace std;

int main(int argc, char *argv[]) { char ch;

if(argc!=2) { cout<<Usage: PR <filename>\n; return 1; }

ifstream in(argv[1], ios::in | ios::binary); if(!in) { cout<<Cannot open file.\n; return 1; }

551

while(!in.eof()) { in.get(ch); cout<<ch; }

in.close();

return 0; } This program uses put() to write characters to a file until the user enters a dollar sign: Chng trinh nay s dung put() ghi ki t cho tp tin cho n khi gp ki hiu la: #include <iostream> #include <fstream> using namespace std;

int main(int argc, char *argv[]) { char ch;

if(argc!=2) { cout<<Usage:WRITE <filename>\n; return 1; }

552

ofstream out(argv[1], ios::out | ios::binary);

if(!out) { cout<<Cannot open file.\n; return 1; }

cout<<Enter a $ to stop.\n; do { cout<<;; cin.get(ch); out.put(ch); }while(ch!=$);

out.close();

return 0; }

Notice that the program uses get() to read characters from cin. This prevents leading spaces from being discarded. Chu y rng chng trinh nay dung get() oc ki t t cin. iu nay ngn khoang u bi loai bo. Here is a program that uses write() to write a double and a string to a file called test:

553

y la mt chng trinh s dung write() ghi double va mt chui vao mt file goi la test: #include <iostream> #include <fstream> #include <cstring> using namespace std;

int main() { ofstream out(test, ios::out | ios::binary);

if(!out) { cout<<Cannot open output file.\n; return 1; }

double num=100.45; char str[]=This is a test;

out.write((char *) &num, sizeof(double)); out.write(str, strlen(str));

out.close();

554

return 0; }

NOTE: The type cast to (char *) inside the call to write() is necessary when outputting a buffer that is not defined as a character array. Because of C++s strong type checking, a pointer of one type will not automatically be converted into a pointer of another type. Loai phu hp (char *) trong li goi write() la cn thit khi xut ra mt b nh trung gian khng c inh nghia nh la mt mang ki t. Bi vi loai kim tra manh cua C++, mt con tro cua mt loai se khng t ng chuyn thanh con tro cua loai khac. This program uses read() to read the file created by the program in Example 3: Chng trinh nay s dung read() ghi file c tao bi chng trinh trong vi du 3:

#include <iostream> #include <fstream> using namespace std;

int main() { ifstream in(test, ios::in | ios::binary);

if(!in) { cout<<Cannot open input file.\n; return 1; }

555

double num; char str[80];

in.read((char *)&num, sizeof(double)); in.read(str, 14); str[14]=\0; // null terminate str

cout<<num<< <<str;

in.close();

return 0; } As is the case with the program in the preceeding example, the type cast inside read() is necessary because C++ will not automatically convert a pointer of one type to another. Khi trng hp vi chng trinh vi du trc, loai phu hp bn trong read() la cn thit vi C++ se khng t ng chuyn i mt con tro cua mt loai sang caii khac. The following program first writes an array of double values to a file and then reads them back. It also reports the number of characters read. Chng trinh di y u tin se ghi mt mang cua gia tri double vao mt tp tin va oc no tr lai. No cung bao cao s ki t oc. // Demonstrate gcount(). #include <iostream> #include <fstream> using namespace std;

556

int main() { ofstream out(test, ios::out | ios::binary);

if(!out) { cout<<Cannot open output file.\n; return 1; }

double num[4]={1.1, 2.2, 3.3, 4.4};

out.write((char *) nums, sizeof(nums)); out.close();

ifstream.in(test, ios::in | ios::binary);

if(!in) { cout<<Cannot open input file.\n; return 1; }

in.read((char *) &nums, sizeof(nums));

557

int i; for(i=0; i<4; i++) cout<<nums[i]<< ; cout<<\n;

cout<<in.gcount()<<characters read\n; in.close();

return 0; }

EXERCISES (BAI TP)


Rewrite your answers to exercises 1 and 3 in the preceeding section (Section 9.2) so that they use get(), put(), read(), and/or write().(Use whichever of these function you deem most appropriate.) Vit lai cu tra li cua bai tp 1 va 3 trong phn trc(9.2) dung get(), put(), read(), va/hay write().(Dung bt c ham nao ban cho la thich hp nht.) Given the following class, write a program that outputs the contents of the class to a file. Create an inserter function for this purpose. Cho lp di y, vit mt chng trinh ma xut ra ni dung cua lp ra tp tin. Tao mt ham chen cho muc ich nay. class account { int custnum; char name[80]; double balance;

558

public account(intc, char *n, double b) { custnum=c; strcpy(name, n); balance=b; } // create inserter here };

9.4. MORE UNFORMATTED I/O FUNCTIONS - THM MT S HM NHP/XUT KHNG C NH DNG


In addition to get the form shown earlier, there are several different ways in which the gets() function is overloaded. The prototypes for the three most commonly used overloaded forms are shown here: Thm vo mt s hnh thc c a ra trc y, c mt vi cch khc trong , chc nng gets() c np chng. u tin l 3 dng np chng ph bin c a ra y: istream&get(char*buf, streamsize num); istream &get(char* buf, streamsize num, char delim); int get(); The first form reads characters into the array pointed to by buf until either num-l characters have been read, a new line is found, or the end of the file has been encountered. The array pointed to by buf will be null terminated by get(). If the new line character is encountered in the input stream, it is not extracted. Instead, it is remains in the stream until the next input operation. Dng u tin c nhng k t vo trong mt mng c ch nh bi buf cho n khi

559

num-l c c, hoc gp mt dong mi hay l kt thc mt file. Mng c tr ti bi buf s c vch gii hn kt thc bi get(). Nu nh pht hin k t ca dng mi trong stream input (tra cu trong MSDN hiu thm), th n khng c trch ra. Thay vo , n c lu li cho n khi gp thao tc nhp tip theo. The second form reads characters into the array pointed to by buf until either num-I characters have been read the character specified by delim has been found, or the end of the file has been encountered. The array pointed to by buf will be null terminated by get(). If the delimiter character is encountered in the input stream, it is not extracted. Instead, it remains in the stream until the next input operation. Dng th 2 c k t vo trong mt mng c tr bi buf cho n khi num-l k t c c cho n khi gp k t c xc nh bi delim, hoc l kt thc mt file. Mng c tr n bi buf s c vch gii hn kt thc bi get(). Nu nh bt gp k t phn cch trong stream input, th n khng c trch xut na. Thay vo , n c duy tr trong chui cho n khi gp thao tc input k tip. The third overloaded form of get() returns the next character from the stream. It returns EOF if the end of the file is encountered. This form of get() is similar to Cs getc() function. Dng th 3 ca get(), tr v k t k tip t trong stream. N tr v EOF nu nh n gp kt thc mt file. Dng ny ca get() th ging nh hm getc() trong C. Another function that performs input is getline(). It is a member of each input stream class. Its prototypes are shown here: Hm khc m thc thi input l getline(). N l mt thnh phn ca mi lp stream input. Nguyn mu ca n th c a ra y: istream &getline(char *buf, streamsize num); istream &getline(char*buf,streamsize num, char delim); The first form reads characters into the array pointed to by buf until either num-i characters have been read, a new line character has been found, or the end of the file has been encouraged. The array pointed to by buf until be null terminated getline(). If the new line character is encountered in the input stream, it is extracted, but it is not put into buf. Dng u tin c nhng k t vo trong mng c tr bi buf cho n khi hoc l num-i k t c c, hoc l gp mt k t xung dng hay l kt thc mt file.

560

Mng c tr bi buf c vch gii hn kt thc bi getline(). Nu nh bt gp k t xung dng trong stream input th n c trch xut nhng n khng c a vo trong buf. The second form reads characters into the array pointed to by buf until either num-I characters have been read, the character specified by delim has been found, or the end of the file has been encountered. The array pointed to by buf will be null terminated be getline(). If the delimiter character is encountered in the input stream, it is extracted, but is not put into buf. Dng th 2 c cc k t vo trong mng c tr n bi buf cho n khi hoc l c num-i k t c c, hoc l gp k t c xc nh bi delim, hoc l gp k t kt thc file. Mng c tr n bi buf n s c vch gii hn kt thc bi getline(). Nu nh bt gp k t phn cch trong stream input th n c trch xut, tuy nhin khng c a vo trong mng buf. As you can see, the two versions of getline() are virtually identical to the get(buf, num) and get(buf, num, delim) versions of get(). Both read characters from input and put them into the array pointed to by buf until either num-I characters have been read or until the delimiter character or the end of the file is encountered. The difference between get() and getline() is that getline() reads and removes the delimiter from the input stream; get() does not. Nh bn c th thy, 2 phin bn ca getline() l hu nh ging ht vi get(buf, num) v get(buf, num, delim) ca get(). C hai u c k t t trong input v a chng vo trong mng c tr bi buf cho n khi num-i k t c c, hoc l cho n khi bt gp k t phn cch, hoc l khi kt thc file. S khc bit gia get() v getline() l getline() c v di chuyn du tch ra khi stream input; cn get() th khng. It returns the next character in the input stream without removing it from that stream by using peek(). This function is a member of the input stream classes and has this prototype: int peek(); It returns the next character in the stream; it returns EOF if the end of the file is encountered. N tr v mt k t k tip trong stream input nhng khng di di chng ra khi stream nu s dng peek(). Hm ny l mt thnh phn trong lp stream input v c dng nguyn mu: int peek();

561

N tr v mt k t k tip trong stream; n s tr v EOF nu nh gp k t kt thc file. You can return the last character read from a stream to that stream by using putback(), which is a member of the input stream classes. Its prototype is shown here:

istream &putback(char c);

where c is the last character read. Bn c th tr v k t cui cng c t mt stream n stream nu s dng putback(), n l mt thnh phn ca lp stream input. Nguyn mu ca n l: istream &putback(char c); Ni m c l k t cui cng c c. When output is performed, data is not immediately written to the physical device linked to the stream. Instead, information is stored in an internal buffer until the buffer is full. Only then are the contents of that buffer written to disk. However, you can force the information to be physically written to disk before the buffer is full by calling flush(). Flush() is a member if the out put stream classes and has this prototype: ostream &flush();

Calls to flush() might be warranted when a program is going to be used in adverse environments( in situations where power outages occur frequently, for example). Khi m output c thc thi, d liu khng c vit ngay lp tc n link thit b vt l. M thay vo , thng tin c lu tr trong mt b nh m bn trong cho n khi b nh n khi b nh m y. Ngay sau , ni dung ca b nh m mi c vit ln disk. Tuy nhin, bn c th tc ng n thng tin c vit ln disk mt cch ngu nhin trc khi b nh m y c gi l fflush().Flush() l mt thnh phn trong lp stream output v c dng nguyn mu: ostram &fflush(); Vic gi fflush() c th c xc nhn, khi m mt chng trnh d nh c s dng trong mi trng bt li. (trong tnh hung ni cung cp lc xut hin tnh trng thiu in thng xuyn, v d)

562

EXAMPLES (V d)
As you know, when you use >> to read a string, it stops reading when the first whitespace character is encountered. This makes is useless for reading a string containing spaces. However, you can overcome this problem by using getline(), as this program illustrates: In this program, the delimiter used by getline() is the newline. This makes getline() act much like the standard gets() function. 1. Nh bn bit, khi m bn s dng >> c mt chui, n dng c khi gp k t trng u tin. Phng php ny th v dng cho vic c mt chui bao gm khong trng. Tuy nhin, bn c th khc phc li ny bng cch s dng getline(), chng trnh sau y s chng t n: //use getline() to read a string that contains spaces #include <iostream> #include <fstream> Using namespace std; int main() { char str[80];

cut <<Enter your name:; cin.getline(str, 79); cut <<str<<\n; return 0; }

2.In real programming situations, the functions peek() and putback() are especially useful because they let you more easily handle situations in which you do not know what type of information is being input at any point in time. The following program gives the flavor of this. It reads either strings or integers from a file. The strings

563

and integers can occur in any order. 2. Trong nhng tnh hung trong chng trnh tht s, hm peek() v putback() th c bit hu dng bi v chng gip bn d dng trong cc tnh hung x l m bn khng bit loi thng tin ang c a vo ti mt thi im bt k. Chng trnh sau minh ha cho iu ny. N c mt chui hoc nhng s nguyn t mt file. Nhng chui, v nhng s nguyn c th xut hin vi th t bt k. //demonstrate peek() #include <iostream> #include <fstream> #include <cctype> using namespace std; int main(int argc, char* argv[]) { char ch; ofstream out ("test", ios::out | ios::binary); if(!out) { cout <<"cannot open output file.\n"; return 1; } char str[80], *p; out <<123<<"This is a test" <<23; out << "Hello there!" << 99<< "sdf" <<endl;

ifstream in ("test", ios::in | ios::binary);

if (!in) 564

{ cout <<"Cannot open input file"; return 1; }

do { p= str; ch = in.peek(); //see what type of char is text if (isdigit(ch)) { if (isdigit (*p=in.get())) p++; //read integer in.putback (*p); //return char to stream *p= '\0'; //Null-terminate the string cout <<"integer: "<<atoi(str); } else if (isalpha (ch)) //read a string { while (isalpha (*p=in.get())) p++; in.putback(*p); //return char to stream *p= '\0'; //NUll-terminate the string in.get(); //ignore cout << "String: "<<str;

565

} else in.get(); //ignore cout <<'\n'; } while (!in.eof()); in.close();

return 0; }

EXERCISES: (Bi tp)


Rewrite the program in Example 1 so it uses get() instead of getline(). Does the program function differently? Write a program that reads a text file one line at a time and displays each line on the screen. Use getline(). On your own, think about why there may be cases in which a call to flush() is appropriate. Vit li chng trnh v d 1 m s dng get() thay v getline(). Chc nng ca chng trnh khc nhau phi khng? Vit mt chng trnh c 1 file text mt ln mt dng, v xut mi dng ra mn hnh. S dng getline(). Theo bn, ngh ti sao c th c trng hp m mt lnh gi fflush() l c th.

9.5. RANDOM ACCESS - TRUY XUT NGU NHIN

566

In C++s I/O system, you perform random access by using the seekg() and seekp() functions, which are members of the input and out put stream classes, respectively. Their most common forms are shown here: istream &seekg(off_type offset, seekdir origin); ostream &seekp(off_type offset, seekdir origin); Here off_type is an integer type defined by ios that is capable of containing the largest valid value that offset can have. seekdir is an enumeration defined by ios that has these values. Value ios::beg ios::cur ios::end Meaning seek from beginning seek from current location seek from end

Trong h thng I/O ca C++, bn c th thc hin vic truy cp ngu nhin bng hm seekg(), v seekp(), chng l thnh phn ca lp stream input, output, theo mt th t nh sn. Nhng dng ph bin ca chng c biu din di y: istream &seekg(off_type offset, seekdir origin); ostream &seekp(off_type offset, seedier origin); y, off_type l mt s nguyn c nh ngha bi ios, n th c nhng gi tr ny. Gi tr ios::beg ios::cur ios::end ngha: seek from beginning seek from current location seek from end

The C++ I/O system manages two pointers associated with a file. One is the get pointer, which specifies where in the file the next input operation will occur. The other is the put pointer, which specifies where in the file the next output operation will occur. Each time an input or output operation takes place, the appropriate pointer is automatically sequentially advanced. However, by using the seekg() and seekp() functions, it is possible to access the file in a nonsequential fashion. H thng I/O trong C++ qun l 2 con tr kt hp vi 1 file. Mt l con tr get, n xc

567

nh ni trong file m thao tc input tip theo s xy ra. Mt loi khc l con tr put, n xc nh v tr trong file xy ra thao tc output k tip xy ra. Mi thi im th mt thao tc output hay input din ra, con tr thch hp th t ng di chuyn lin tc. Tuy nhin, bng cch s dng hm seekg() v seekp(), n c th truy cp file mt cch khng lin tc. The seekg() fuction moves the associated files current get pointer offset number of bytes from the specified origin. The seekp() function moves the associated files current put pointer offset number of bytes from specified origin. In general, files that will be accessed via seekg() and seekp() should be opened for binary file operations. This prevents character translations from occurring which may affect the apparent position of an item within a life. You can determine the current position if each file pointer by using these member functions: pos_type tellg(); pos_type tellp(); Hm seekg() di chuyn con tr get hin thi ca file offset byte bt u t v tr c ch nh bi origin. Hm seekp() di chuyn con tr put hin thi trong file offset byte bt u t v tr c xc nh bi origin. Nhn chung, nhng file m c truy xut qua seekg() v seekp() nn c m cho thao tc tp nh phn. Bn c th xc nh v tr hin ti nu nh con tr mi file s dng nhng hm thnh phn ny: Pos_type tellg(); Pos_type tellp(); Here pos_type is an integer type defined by ios is capable of holding the largest value that defines a file position. There are overloaded versions of seekg() and seekp() that move the file pointers to the location specified by the return values of tellg() and tellp(). Their prototypes are shown here: istream &seekg(pos_type position) ostream &seekp(pos_type position)

568

y pos_type l mt s nguyn c xc nh bi ios th c kh nng gi gi tr ln nht xc nh v tr ca mt file. C nhng phin bn trng ca seekg() v seekp() di chuyn con tr file n v tr c xc nh bi gi tri tr v ca tellg() v tellp(). Dng nguyn mu ca n nh sau: istream &seekg(pos_type position) ostream &seekp(pos_type position)

EXAMPLES: (Bi tp)


The following program demonstrate the seekp() function. It allows you to change a specific character in a file. Specific a file name on the command line, followed by the number of the byte in the file you want to change, followed by the new character. Notice that the file is opened for read/write operations. 1. Chng trnh sau y chng minh hm seekp(). N cho php bn chuyn i mt k t trong file. Xc nh mt tn file trn mt dng lnh, sau l s byte m ban mun chuyn i, sau l k t mi. Ch rng, file c m cho thao tc c hoc l vit. #include <iostream> #include <fstream> #include <cstdlib> using namespace std; int main(int argc, char *argv[]) { if (argc!=4) { cout << "Usage: CHANGE<filename><byte><char>\n"; return 1; }

569

fstream out (argv[1], ios::in | ios::out | ios::binary); if(!out) { cout <<"cannot openfile"; return 1; } out.seekp(atoi(argv[2]), ios::beg); out.close();

return 0; }

The next program uses seekg() to position the get pointer into the middle of a file and then displays the contents of that file from that point. The name of the file and the location to begin reading from are specified on the command line. 2. Chng trnh tip theo s dng hm seekg() t con tr get vo gia mt file v sau xut ni dung ca file ra mn hnh t v tr con tr. Tn ca file v v tr bt u c c xc nh trn mt dng lnh. #include <iostream> #include <fstream> #include <cstdlib> using namespace std;

int main(int argc, char* argv[]) { char ch;

570

if(argc !=3) { cout << "Usage: LOCATE <filename> <loc>\n"; return 1; } ifstream in(argv[1], ios::in |ios::binary);

if (!in) { cout <<"Cannot open input file.\n"; return 1; }

in.seekg(atoi (argv[2]), ios::beg);

while (!in.eof()) { in.get(ch); cout <<ch; } in.close();

return 0; }

571

EXERCISES:
Write program that displays a text file backwards. Hint: think about this before creating your program. The solution is easier than you might imagine. Write a program that swaps each character pair in a text file. For example, if the file contains 1234, then after the program is run, the file will contain 2143. (for simplicity, you may assume that the file contains an even number of characters.) 1. Vit mt chng trnh biu din mt file text ngc. Gi : Ngh v iu ny trc khi to chng trnh ca bn. Phng n gii quyt th d dang hn bn tng tng. 2. Vit mt chng trnh sp xp mi cp k t trong mt file text. V d, nu file gm c 1234, sau khi chng trnh chy, th file bao gm 2143, ( n gin, bn c th cho rng file bao gm s cc k t).

9.6. CHECKING THE I/O STATUS - KIM TRA TRNG THI I/O
The C++ I/O system maintains status information about the outcome of each I/O operation. The current status of an I/O stream is described in an object of type iostate, which is an enumeration defined by ios that includes these members: Name goodbit eofbit failbit badbit Meaning no errors occurred End of-file has been encountered. a nonfatal I/O error has occurred a fatal I/O error has been occurred

H thng I/O trong C++ duy tr thng tin trng thi v kt qu ca mi thao tc I/O. Trng thi hin ti ca mt stream I/O c th hin trong mt i tng loi iostate, n l mt bng lit k c nh ngha bi ios bao gm cc thnh phn sau y: 572

Tn goodbit eofbit failbit badbit

Ngha Khng c li xut hin Kt thc mt file. Xut hin mt li khng I/O khng nghim trng. Xut hin mt li nghim trng.

For older compliers, the I/O status flags are held in an int rather than an object of type iostate. There are two ways in which you can obtain I/O status information. First, you can call the rdstate() function, which is a member of ios. It has this prototype: i vi mt chng trnh bin dch c, trng thi c c gi nh l mt int th tt hn l mt i tng iostate. C 2 cch m bn c th thu c cc thng tin trng thi. u tin, bn c th gi hm rdstate(), n l mt thnh phn ca ios. N c dng nguyn mu l: Iostate rdstate(); It return the current status of the error flags. As you can probably guess from looking at the preceding list of flags, sdstate() returns goodbit when no error has occurred. Otherwise, an error flag is returned. The other way you can determine whether an error has occurred is by using one or more of these ios member functions. bool bad(); bool eof(); bool fail(); bool good(); N tr v mt trng thi hin thi ca nhng c bo li. Nhng hu nh chc chn rng bn c th on c t vic quan st danh sch cc c c trc, rdstate() tr v goodbit khi m khng c li no xut hin. Ngc li, mt c bo li c tr v. Cch khc m bn c th xc nh c li hay khng l s dng mt hoc nhiu hn mt 573

hm thnh phn ca ios. bool bad(); bool eof(); bool fail(); bool good(); The eof() function was discussed earlier. The bad() function returns true badbit is set. The good() function returns true if there are no errors. Otherwise they return false. Once an error has occurred it might need to be cleared before your program continues. To do this, use the ios member function lear(), whose prototype is shown here: void clear(iostate flags =ios::goodbit); If flags is goodbit (as it is by default), all error flags are cleared. Otherwise, set flags to the settings you desire. Hm eof() c tho lun trc y. Hm bad() tr v true nu nh badbit c thit lp. Hm good() tr v gi tr true nu nh khng c li no. Ngc li tr v false. Mt khi m c li xut hin, c th n cn c xa trc khi chng trnh ca bn tip tc. lm iu ny, s dng hm thnh phn ios l clear(), dng nguyn mu ca n l: void clear(iostate flags=ios::goodbit); Nu c l goodbit (nh mc nh), tt c nhng c li u c xa. Ngc li, thit lp c bn chnh sa theo mun.

EXAMPLES: (V D)
The following program illustrates rdstate(). it displays the contents of a text file. If an error occurs, the function reports it by using checkstatus(). Chng trnh sau y chng minh rdstata(). N xut ra mn hnh ni dung ca mt file text. Nu c mt li, th hm thng bo n bng cch s dng checkstatus(). #include <iostream>

574

#include <fstream> using namespace std; void checkstatus(ifstream &in);

int main(int argc, char* argv[]) { if (argc !=2) { cout << "Usage: DISPLAY <filename>\n"; return 1; }

ifstream in(argv[1]); if(!in) { cout <<"Cannot open input file.\n"; return 1; }

char c; while (in.get(c)) { cout <<c; checkstatus (in); }

575

checkstatus (in); //Check final status in.close();

return 0; }

void checkstatus (ifstream &in) { ios::iostate i; i=in.rdstate(); if(i&ios::eofbit) cout << "EOF encountered\n"; else if (i&ios::failbit) cout << "Non-Fatal I/O error\n"; else if (i&ios::badbit) cout <<"Fatal I/O error \n"; }

The preceding program will always report at least one error. After the while loop ends, the final call to checkstatus() reports, as expected, that an EOF has been encountered. Chng trnh bn trn s lun thng bo t nht mt li. Sau khi kt thc lp, cui cng gi checkstatus() thng bo l cui file.

576

This program displays a text file. It uses good() to detect a file error: Chng trnh ny xut mt file text ra ngoi mn hnh. N s dng good() pht hin li file: #include <iostream> #include <fstream> using namespace std;

int main(int argc, char* argv[]) { char ch; if(argc!=2) { cout <<"PR:<filename>\n"; return 1; } ifstream in(argv[1]); if (!in) { cout <<"Cannot open input file.\n"; return 1; }

while (!in.eof()) { in.get(ch);

577

//check for error if(!in.good() && !in.eof()) { cout <<"I/O Error ... terminating\n"; return 1; } cout <<ch; } in.close();

return 0; }

EXERCISES:
Add error checking to your answers to the exercises from the preceding section. Thm vo phn kim tra li trong cu tr li ca bn phn trc.

9.7. CUSTOMIZED I/O AND FILES - FILE V I/O THEO YU CU


In the preceding chapter you learned how to overload the insertion and extraction operators relative to your own classes. In that chapter, only console I/O was performed. However, because all C++ streams are the same, the same overloaded inserter function, for example, can be used to output to the screen or to a file with no changes whatsoever. This is one of the most important and useful features of C++s approach to I/O.

578

chng trc bn hc cch np chng ton c chn v rt trch lin quan n nhng bi hc ca bn. Trong chng , ch c phn I/O c biu din. Tuy nhin, bi v tt c cc stream trong C++ th tng t nhau, hm np chng tng t lng vo nhau, v d, c th c s dng xut ra ngoi mn hnh hoc n file m khng thay i n. iu ny l mt trong nhng c im quan trng nht v hu dng nht phng php C++ i vi I/O.

As stated in the previous chapter, overloaded inserters and extractors, as well as I/O manipulators, can be used with any stream as long as they are written in a general manner. If you hard-code a specific stream into an I/O function, its uses is,of sourse, limited to only that stream. This is why you were urged to generalize your I/O functions whenever possible. Nh trng thi ca chng trc, np chng chn v trch xut, l cng c I/O tt nht c th, c th c s dng vi bt k stream no c di ging nh chng c vit cch thng thng. Nu bn vit mt stream c th vo trong mt hm I/O, th n c s dng, tt nhin n ch gii hn trong stream . y l l do ti sao bn c xut khi qut ha hm I/O bt c khi no c th.

EXAMPLES:
In the following program, the coord class overloads the << and >> operators. Notice that you can use the operator functions to write both to the screen and to a file. Trong chng trnh sau y, lp coord np chng ton t << v >>. Ch rng bn c th s dng hm ton t vit c ra mn hnh v file. #include "stdafx.h" #include <iostream> #include <fstream> using namespace std;

class coord { 579

int x, y; public: coord (int i, int j) { x= i; y= j; } friend ostream &operator <<(ostream &os, coord ob); friend istream &operator >>(istream &is, coord &ob); };

ostream &operator <<(ostream &os, coord ob) { os <<ob.x<<' '<<ob.y<<'\n'; return os; }

istream &operator >>(istream &is, coord &ob) { is >>ob.x >>ob.y; return is; }

580

int main() { coord o1(1,2), o2(3, 4); ofstream out ("test");

if(!out) { cout <<"Cannot open output file.\n"; return 1; } out <<o1 <<o2; out.close();

ifstream in("test"); if(!in) { cout <<"Cannot openfile input file.\n"; return 1; } coord o3(0,0), o4 (0,0); in>>o3 >>o4;

cout <<o3 <<o4; in.close(); return 0;

581

} All of the I/O manipulators can be used with files. For example, in this reworked version of a program presented earlier in this chapter, the same manipulator that writes to the screen will also write to a file: 2. Tt c cc b phn I/O u c th c s dng vi file. V d, trong bi lm li ca chng trnh c biu din chng trc, on iu khin tng t vit ra mn hnh cng nh vit ra mt file: #include <iostream> #include <fstream> #include <iomanip> using namespace std;

//Attention: ostream &atn (ostream &stream) { stream <<"Attention:"; return stream; }

//Please note: ostream &note(ostream &stream) { stream <<"Please Note:"; return stream; }

582

int main() { ofstream out ("Test.txt"); if (!out) { cout <<"Cannot open output file.\n"; return 1; }

//Write to screen cout <<atn <<"High voltage circuit.\n"; cout <<note <<"Turn off all lights\n";

//write to file out <<atn <<"High voltage circuit.\n"; out <<note <<"Turn off all lights\n";

out.close(); return 0; }

EXERCISES: On your own, experiment with the programs from the preceding chapter, trying each on a disk file. T bn lm th nghim vi nhng chng trnh t cc chng trc, th to mi ci

583

vi mt file trn disk.

SKILLS CHECK: (KIM TRA CC K NNG) At this point you should be able to perform the following exercises and answer the questions. Create an output manipulator that outputs three tabs and then sets the field width to 20. demonstrate that your manipulator works. Create an input manipulator that reads and discards all nonalphabetical characters. When the first alphabetical character is read, have the manipulator return it to the input stream and return. Call this manipulator findalpha. Write a program that copies a text file. In the process, reverse the case of all letters. write a program that reads a text file and then reports the number of times each letter in the alphabet occurs in the file. if you have not done so, add complete error checking to your solutions to Exercises 3 and 4 above. What function positions the get pointer? What function positions the put pointer? Vo thi im ny, bn nn lm nhng bi tp sau y v tr li cc cu hi: To mt on chng trnh xut 3 du tab, v sau m rng ln 20. Chng minh rng chng trnh ca bn hot ng. To mt on chng trnh input c v loi b tt c cc k t khng nm trong bng ch ci alpha. Khi nhng k t alpha u tin c c, c mt on chng trnh tr n v stream input v tr v. Gi on chng trnh l findalpha. Vit mt chng trnh copy mt file text. Trong qu trnh thc hin, o ngc tt c cc k t. Vit mt chng trnh c mt file text v sau thng bo s ln xut hin ca mi k t (trong bng ch ci alpha) trong file. Nu ban cha lm, th hy thm hm kim tra li vo bi 3 v bi 4 bn trn. Hm no dng xc nh v tr ca mt con tr get? Hm no xc nh v tr ca con tr put.

584

CHAPTER 10 VIRTUAL FUNCTIONS - HAM AO


Chapter objective
10.1. POINTER TO DERIVED CLASSES. Con tro tro ti lp dn xut 10.2. INTRODUCE TO VIRTUAL FUNCTIONS. Tng quan v ham ao 10.3. MORE ABOUT VIRTUAL FUNCTIONS. Noi thm v ham ao. 10.4. APPLYING POLYMORPHISM. Ap dung a hinh

This chapter examines another important aspect of C++: the virtual function. What makes virtual function important is that they are used to support runtime polymorphism. Polymorphism is supported by C++ in two ways. First, it is supported at compile time, through the uses of overloaded operators and function. Second, it is supported at run time, through the use of virtual function. As you will learn, run-time polymorphism provides the greatest flexibility. At the foundation of virtual function and run-time polymorphism are pointers to derived classes. For this reason this chapter begins with a discussion of such pointers. Chng nay nghin cu vn quan trong khac cua C++: ham ao. Cai ma lam cho ham ao tr nn quan trong do no c dung h tr chay ch a hinh. a hinh c C++ h tr bng 2 cach. Th nht, no c dung bin dich thi gian, xuyn sut cach dung toan t nap chng va ham. Th hai, no c h tr trong mi trng chay, thng qua cach dung cua ham ao. Khi ban hoc, chay ch a hinh cung cp s uyn chuyn nht. Tai s thit lp cua ham ao va chay ch a hinh la con tro tro ti lp dn xut. 585

o la ly do chng nay bt u vi s thao lun v con tro.

REVIEW SKILL CHECK (Kim tra ki nng n tp) Before proceeding, you should be able to correctly answer the following questions and do the exercises. Create a manipulator that causes numbers to be displayed in scientific notation, using a capital E. Write a program that copies a text file. During the copy process, convert all tabs into the correct number of spaces. Write a program that searches a file for a word specified on the command line. Have the program display how many times anything surrounded by whitespace is a word. Show the statement that sets the put pointer to the 234th byte in a file linked to a stream called out. What function report status information about the C++ I/O system? Give one advantage of using the C++ I/O functions instead of the C-like I/O system. Trc khi tip tuc, ban nn tra li chinh xac nhng cu hoi di y va lam nhng bai tp sau. 1. Thc hin mt thao tac tao nn nhng con s c hin thi trong ki hiu khoa hoc, dung ting Anh chu yu. 2. Vit 1 chng trinh sao chep mt tp tin vn ban. Trong sut qua trinh sao chep, chuyn i tt ca tab thanh con s chinh xac cua khoang trng. 3. Vit mt chng trinh tim kim mt tp tin cho mt t theo ly thuyt trn dong lnh. Co mt chng trinh hin thi bao nhiu ln bt c cai gi bao quanh bi khoang trng la mt t. 4. Ch ra cu lnh m thit lp con tr tr ti byte th 234 trong mt tp tin lin kt vi mt lung gi l out. 5. Ham nao bao cao trang thai thng tin v h thng nhp xut C++. 6. Nu ra s thun li cua vic s dung ham nhp xut trong C++ thay cho Cging nh h thng nhp xut.

586

10.1. POINTERS TO DERIVED CLASSES - CON TRO TRO TI LP DN XUT

Although Chapter 4 discussed C++ pointers at some length, one special aspect was deferred until now because it relates specifically to virtual functions. The feature is this: A pointer declared as a pointer to a base class can be used to point to any class derived from that base. For example, assume two classes called base and derived, where derived inherits base. Given this situation, the following statements are correct: Mc du chng 4 thao lun con tro C++ tai mt vai dai, mt din mao c bit bi hoan lai cho n by gi bi vi no lin quan ro rang ti ham ao. c trng la iu nay: mt con tro hin thi nh mt con tro tro ti lp c s co th c dung tro ti bt ki lp dn xut nao t c s o. Vi du, tha nhn 2 lp goi la base va derived, ni ma derived tha k base. Trong tinh hung nay, nhng cu lnh di y la ung: base *p; // base class pointer base base_ob; // obj of type base derived derived_ob; // obj of type

// p can, of course, point to base obj p=&base_ob; // p points to base obj

// p can also point to derived obj without error p=&derived_ob; // p points to derived obj As the comments suggest, a base pointer can point to an obj of any class derived from that base without generating a type mismatch error. Although you can use a base pointer to point to a derived obj, you can access only those members of the derived obj that were inherited from the base. This is because the base pointer has knowledge only of the base class. It knows nothing about the members added by the derived class. While it is permissible for a base pointer to point to a derived obj, the reverse is not true. A pointer of the derived type cannot be used to access an obj of the base class. (A type cast can be used to overcome this restriction, but its use is not recommended 587

practice.) One final point: Remember that pointer arithmetic is relative to the data type the pointer is declared as pointing to. Thus, if you point a base pointer to a derived obj and then increment that pointe, it will not be pointing to the next derived obj. It will be pointing to(what it thinks is) the next base obj. Be careful about this. Nh li nhn xet gi y, mt con tro c s co th tro ti mt i tng cua lp dn xut bt ki nao t c s o khng co phat sinh loai li khng khp. Tuy ban co th dung con tro c s tro n i tng dn xut, ban chi co th truy xut nhng thanh phn cua i tng dn xut ma tha k t c s. iu nay bi vi con tro c s chi co thng tin cua lp c s. No khng bit v nhng thanh vin thm vao bi lp dn xut. Trong khi no dung c cho con tro c s tro ti i tng dn xut, s ao ngc thi khng ung. Mt con tro cua loai dn xut khng th c dung truy xut mt lp i tng cua lp c s. (Mt loai phu hp co th c dung vt qua s han ch nay, nhng cach dung cua no khng cp ti l thng.) Mt im cui cung: nh rng con tro s hoc loai d liu ma con tro hin thi nh la tro n. Nh vy, nu no khng tro n i tng dn xut tip theo. No se tro n (iu no nghi la) i tng tip theo. Hay cn thn v iu nay.

EXAMPLE (VI DU)


1. Here is a short program that illustrates how a base class pointer can be used to access a derived class: 1. y la chng trinh ngn minh hoa mt lp c s co th c dung truy xut lp gc nh th nao: // Demonstrate pointer to derrived class. #include <iostream> using namespace std;

class base {

588

int x; public: void setx(int i) x=i; int getx() return x; };

class derived:public base { int y; public: void sety(int y) y=i; int gety() return y; };

int main() { base *y; // pointer to bse type base b_ob; // obj of base derived d_ob; // obj of derived

// use p to access base obj

589

p=&b_ob; p->setx(10); //access base obj cout<<Base obj x:<<p->getx()<<\n;

// use p to access derived obj p=&d_ob; // point to derived obj p->setx(99); // access derived obj

// cant use p to set y, so do it directly d_ob.sety(88); cout<<Derived obj x:<<p->getx()<<\n; cout<<Derived obj y:<<d_ob.gety()<<\n;

return 0; }

Aside from illustrating pointers to derived classes, there is no value in using a base class pointer in the way shown in this example. However, in the next section you will see base class pointers to derived obj are so important. Ngoai vic minh hoa con tro tro ti lp dn xut, khng co gia tri nao trong cach s dung con tro lp c s cach c trinh bay trong vi du nay. Tuy nhin, trong oan tip theo ban se thy lp con tro c s tro ti i tng dn xut quan trong nh th nao.

EXERCISE (BAI TP)


On your own, try the preceding example and experiment with it. For example, try declaring a derived pointer and having it access an obj of the base class. Ban hay t th vi du trc and hoc hoi kinh nghim t no. Vi du, th trinh bay mt con

590

tro gc va yu cu no truy xut mt i tng cua lp c s.

10.2. INTRODUCTION TO VIRTUAL FUNCTIONS TNG QUAN V HM O


A virtual function is a member function that is declared within a base class redefined by a derived class. To create a virtual function, precede the functions declaration with the keyword virtual. When a class containing a virtual function is inherited, the derived class redefines the virtual function relative to the derived class. In essence, virtual function implement the one interface, multiple methods philosophy that underlies polymorphism. The virtual function within the base class defines the form of the interface to that function. Each redefinition of the virtual function by a derived class implements its operation as it relates specifically to the derived class. That is, the redefinition creates a specific method. When a virtual is redefined by a derived class, the keyword virtual is not needed. Mt hm o l mt hm thnh phn ma c hin thi trong vong mt lp c s c inh nghia lai bi mt lp dn xut. tao mt ham ao, n trc s khai bao cua ham ao vi mt t khoa ao. Khi ma mt lp cha mt ham ao c tha k, lp dn xut inh nghia lai ham ao lin quan ti lp dn xut. Thc cht, ham ao thc thi trit ly mt giao din, nhiu phng phap tn tai di dang a hinh. Ham ao trong vong lp c s inh nghia dang cua giao din i vi ham o. Mi s inh nghia lai cua ham ao bi mt lp dn xut thc thi thao tac cua no nh la no lin quan c bit ti lp dn xut. iu o la, s inh nghia lai tao mt phng phap ro rang. Khi ma ham ao c inh nghia lai bi mt lp dn xut, t khoa ao la khng cn thit. A virtual function can be called just like any other member function. However, what makes a virtual function interesting-and capable of supporting run-time polymorphismis what happens when a virtual function is called through a pointer. From the preceding section you know that a base class pointer can be used to point to a derived class obj. When a base pointer points to a derived obj that contains a virtual function and that virtual functions is called through that pointer, C++ determines which version of that function will be executed based upon the type of obj being pointed to by the pointer. And, this determination is made at run time. Put differently, it is the type of the obj pointed to at the time when the call occurs that determines which version of the virtual function will be executed. Therefore, if two or more different classes are derived from a base class that contains a virtual function, then when different obj are pointed to by a base pointer, different versions of the virtual function are executed. This processs is the 591

way that run-time polymorphism is achieved. In fact, a class that contains a virtual function is referred to as a polymorphic class. Mt ham ao co th c goi nh la bt ki ham thanh phn nao khac. Tuy nhin, iu lam ham ao thu vi-va co kha nng h tr chay ch a hinh-la iu xay ra khi ham ao c goi thng qua con tro. T phn trc ban bit rng mt con tro lp c s co th tro n i tng lp dn xut. Khi ma mt con tro c s tro n i tng dn xut cha mt ham ao va ham ao o c goi thng qua con tro o, C++ xac inh ro phin ban nao cua ham o se c chay da trn loai cua i tng ang c tro n bi con tro. Va s xac inh nay c lam trn mi trng chay. t khac nhau, no la loai cua i tng tro n tai thi im ma li goi xut hin xac inh phin ban nao cua ham ao se c thc hin. Vi th, nu 2 hay nhiu lp khac nhau c dn xut t mt lp c s, phin ban khac nhau cua ham ao c thc hin. Qua trinh nay la cach chay ch a hinh at c. Tht s, mt lp cha mt ham ao c cho la nh lp a hinh.,

EXAMPLES (VI DU)


Here is a short example that uses a virtual function: y la mt vi du ngn ma s dung ham ao: // A simple example using a virtual function. #include <iostream> using namespace std;

class base { public: int i; base(int x) i=x; virtual void func() { 592

cout<<Using base version of func():; cout<<i<<\n; } };

class derived1:public base { public: derived1(int x):base(x) void func() { cout<<Using derived1s version of func():; cout<<i*i<<\n; } };

class derived2:public base { public: derived2(int x):base(x) void func() { cout<<Using derived2s version of func():; cout<<i+i<<\n; }

593

};

int main() { base *p; base ob(10); derived1 d_ob1(10); derived2 d_ob2(10);

p=&ob; p->func(); // use bases func()

p=&d_ob1; p->func(); // use derived1s func()

p=&d_ob2; p->func(); // use derived2s func()

return 0; }

This program displays the following output: Chng trinh nay trinh bay xut: Using base version of func():10 Using derived1s version of func():100

594

Using derived2s version of func():20 The redefinition of a virtual function inside a derived class might, at first, seem somewhat similar to function overloading. However, the two processes are distinctly different. First, an overloaded function must differ in type and/or number of parameters, while a redefined vrtual function must have precisely the same type and number of parameters and the same return type. (In fact, if you change either the number or type of parameters when redefining and its virtual nature is lost.) Further, virtual functions must be class members. This is not the case for overloaded functions. Also, while destructor functions can be virtual, constructors cannnot. Because of the differences between overloaded functions are redefined virtual functions, the term overriding is used to describe virtual function redefinition. S inh nghia lai cua ham ao bn trong lp dn xut co th, trc tin, dng nh iu gi o ging vi nap chng ham. Tuy nhin, hai qua trinh khac nhau ro rt. Th nht, nap chng ham phai khac loai va/hay s cua tham s, trong khi ham ao c inh nghia lai phai co cung loai va s cua tham s va cung loai tra v. (Tht s, nu ban thay i ca s hay loai cua tham s khi inh nghia lai va ao t nhin mt i.) Rng hn, ham ao phai la lp thanh phn. y khng la trng hp cho nap chng ham. Cung vy, trong khi ham huy co th la ao, ham thit lp thi khng. Bi vi s khac nhau gia nap chng ham va ham ao inh nghia lai, s hang ghi e c dung din ta ham ao c inh nghia lai. As you can see, the example program creates three classes. The base class defines the virtual function func(). This class is then inherited by both derived1 and derived2. Each of these classes overrides func() with its individual implementation. Inside main(), the base class pointer p is declared along with obj of type base, devired1, and devired2. First, p is assigned the address of ob (an obj of type base). When func() is called by using p, it is the version in base that is used. Next, p is assigned the address of d_ob1 and func() is called again. Because it is the type of the obj pointed to that determines which virtual function will be called, this time it is the overridden version in derived1 that is executed. Finally, p is assigned the address of d_ob2 and func() defined inside derived2 that is executed. Nh ban thy, chng trinh vi du tao ra 3 lp. Lp c s inh nghia ham ao func(). Lp nay c tha k bi ca hai derived1 va derived2. Mi lp nay ghi e func()s thc thi ring le cua no. Trong ham main(), con tro lp c s p c hin thi song song vi i tng cua loai c s, derived1, va derived2. Th nht, p c gan ia chi cua ob (mt i tng cua loai c s). Khi func() c goi bi dung p, no la phin ban c s c dung. Tip theo, p c gan ia chi cua d_ob1 va func() va c goi ln na. Bi vi no la loai cua i tng tro ti xac inh rng ham ao nao se c goi, ln nay no se c ghi e phin ban derived1 ma c thi hanh. Cui cung, p c gan ia chi cua d_ob2 va func() inh nghia trong derived2 ma c thi hanh. 595

The key points to understand from the preceding example are that the type of the obj being pointed to determines which version of an overridden virtual function will be executed when accessed via a base class pointer, and that this decision is made at run time. T khoa tro ti hiu t vi du trc la loai cua i tng c tro ti xac inh phin ban nao cua ham ao ghi e se thc thi khi truy xut qua con tro lp c s, va quyt inh nay c lam trong mi trng chay.

Virtual functions are hierarchical in order of inheritance. Futher, when a derived class does not override a virtual function, the function defined within its base class is used. For example, here is a slightly different version of the preceding program: Ham ao co th t tha k. Rng hn, khi mt lp dn xut khng ghi e ln ham ao, ham inh nghia trong vong lp c s cua no c dung. Vi du, y la phin ban khac nhau nho cua chng trinh trc: // Nhng hm o th c th bc. #include <iostream> using namespace std;

class base { public: int i; base(int x) { i = x; } virtual void func() { cout << Using base version of func(): ; cout << i << \n; } };

596

class derived1 : public base { public: derived1(int x) : base(x) {} void func() { cout << Using derived1s version of func(): ; cout << i * i << \n; } };

class derived2 : public base { public: derived2(int x) : base(x) {} // derived2 khng np chng hm func() };

int main() { base *p; base ob(10); derived1 d_ob1(10); derived2 d_ob2(10); p = &ob; p->func(); // s dng hm func() ca lp base

597

p->&d_ob1; p->func(); // s dng hm func() ca lp derived1 p->&d_ob2; p->func(); // s dng hm func() ca lp derived2

return 0; }

This program displays the following output: Chng trinh nay trinh bay xut:

Using base version of func(): 10 Using derived1 version of func(): 100 Using derived2 version of func(): 20 In this version, derived2 does not override func(). When p is assigned d_ob2 and func() is called, bases version is used because it is next up in the class hierarchy. In general, when a derived class does not override a virtual function, the base class version is used. Trong phin ban nay, derived2 khng ghi e ln func(). Khi p c gan d_ob2 va func() c goi, phin ban c s c dung vi no tip theo trong lp co th t. Noi chung, khi lp dn xut khng ghi e ln ham ao, phin ban lp c s c dung. The next example shows how a virtual function can respond to random events that occur at run time. This program selects between d_ob1 and d_ob2 based upon the value returned by the standard random number generator rand(). Keep in mind that the version of func() executed is resolved at run time. (Indeed, it is impossible to resolve the calls to func() at compile time.) Vi du tip theo cho thy ham ao co th phan hi s kin ngu nhin ma xay ra tai mi trng chay nh th nao. Chng trinh nay chon gia d_ob1 va d_ob2 da trn gia tri tra v bi s khi xng ngu nhin chun tai mi trng chay. (Thc vy, bt kha thi giai quyt li goi func() tai thi im bin dich.)

598

/* This example illustrates how a virtual function can be used to respond to random events occurring at run time. */

/* V d ny l in hnh nh th no l mt hm o c th c s dng p tr li nhng s kin ngu nhin xy ra lc thc thi. */

#include <iostream> using namespace std;

class base { public: int i; base(int x) { i = x; } virtual void func() { cout << Using base version of func(): ; cout << i << \n; } };

class derived1 : public base { public: derived1(int x) : base(x) {}

599

void func() { cout << Using derived1s version of func(): ; cout << i*i << \n; } };

class derived2 : public base { public: derived2(int x) : base(x) {} void func() { cout << Using derived2s version of func(): ; cout << i+i << \n; }};

int main() { base *p; base ob(10); derived1 d_ob1(10); derived2 d_ob2(10); int i, j;

for(i=0; i<10; i++) {

600

j = rand(); if((j%2)) p = &d_ob1; // nu d_ob1 l else p = &d_ob2; // nu d_ob2 chn p->func(); // gi hm thch hp }

return 0; }

Here is a more practical example of show a virtual function can be used. This program creates a generic base class called area that holds two dimensions of a figure. It also declares a virtual function called getarea() that, when overridden by derived classes, returns the area of the type of figure defined by the derived class. In this case, the declaration of getarea() inside the base class determines the nature of the interface. The actual implementation is left to the classes that inherit it. In this example, the area of a triangle and a rectangle are computed. Thm vi du thc tp cho thy ham ao co th c s dung nh th nao. Chng trinh nay tao mt lp c s co c im chung goi la khu vc gi hai kich thc cua hinh dang. No cung hin thi mt ham ao goi la getarea(), khi ghi e bi lp dn xut, tra v cung cua loai cua getarea() bn trong lp c s xac inh ro s t nhin cua giao din. S thi hanh tht s ma ri khoi lp ma tha k no. Trong vi du nay, vung cua mt tam giac va mt hinh ch nht va mt tam giac c tinh toan. // S dng hm o nh ngha giao din #include <iostream> using namespace std;

class area { double dim1, dim2; // nhng kch thc hnh public: void setarea(double d1, double d2)

601

{ dim1 = d1; dim2 = d2; } void getdim(double &d1, double &d2) { d1 = dim1; d2 = dim2; } virtual double getarea() { cout << You must override this fuction\n; return 0.0; } };

class rectangle : public area { public: double getarea() { double d1, d2; getdim(d1, d2); return d1 * d2; } };

602

class triangle : public area { public: double getarea() { double d1, d2;

getdim(d1, d2); return 0.5 * d1 * d2; } };

int main() { area *p; rectangle r; trisngle t;

r.setarea(3.3, 4.5); r.setarea(4.0, 5.0);

p = &r; cout << Rectangle has area: << p->getarea() << \n;

603

p = &t; cout << Triangle has area: << p->getarea() << \n;

return 0; }

Notice that the definition of getarea() inside area is just a placeholder and perform no real function. Because area is not linked to any specific type of figure, there is no meaningful definition that can be given to getarea() inside area. In fact, getarea() must be overridden by a derived class in order to be useful. In the next section, you will see a way to enforce this. Chu y rng s inh nghia cua getarea() bn trong vung la chi la trinh gi ch va khng thc hin ham thc s. Bi vi vung khng lin kt ti bt ki loai ro rang nao cua hinh dang, khng co inh nghia y u ma co th c getarea() bn trong vung. Tht s, getarea() phai c ghi e bi lp dn xut hu dung. Trong phn tip theo, ban se thy cach khac thi hanh iu nay.

EXERCISES (BAI TP)


1. Write a program that creates a base class called num. Have this class hold an integer value and contain a virtual function called shownum(). Create two derived classes called outhex and outoct that inherit num. Have the derived classed override shownum() so that it displays the value in hexadecimal and octal, respectively. Vit mt chng trinh tao mt lp c s goi la num. Co lp nay gi mt gia tri nguyn va cha mt ham ao goi la shownum(). Tao 2 lp dn xut goi la outhex va outoct ma tha k num. Co lp dn xut ghi e shownum() no trinh bay gia tri trong h thp luc phn va h bat phn, theo th t inh sn. 2. Write a program that creates base class called dist that stores the distance between two points in a double variable. In dist, create a virtual function called trav_time() that outputs the time it takes to travel that distance, assuming that the distance is in miles and the speed is 60 miles per hour. In a derived class called metric, override trav_time() so that it outputs the travel time assuming that the distance is in 604

kilometers and the speed is 100 kilometers per hour. Vit mt chng trinh ma tao lp c s goi la dist ma lu tr khoang cach gia 2 im trong mt cp gia tri. Trong dist, tao mt ham ao goi la trav_time() ma xut thi gian no ly vt qua khoang cach o, cho rng khoang cach la dm va tc la 60 dm/gi. Trong lp dn xut goi la h met, ghi e trav_time() vi th no xut thi gian di chuyn cho rng khoang cach la km va tc la 100km/h.

10.3. MORE ABOUT VIRTUAL FUNCTIONS - NOI THM V HAM AO


As Example 4 from the preceding section illustrates, sometimes when a virtual function is declared in the base class there is no meaningful operation for it to perform. This situation is common because often a base class does not define a complete class by itself. Instead of, it simply supplies a core set of number functions and variables to which the derived class supplies the remainder. When there is no which the derived class supplies the remainder. When there is no meaningful action for derived class virtual function to perform. The implication is that any derived class must override this function. To ensure that this will occur, C++ supports pure virtual functions. A pure virtual function has no definition relative to the base class. Only the functions prototype is included. To make a pure virtual function, use this general form: Nh vi du 4 t phn trc a minh hoa, thinh thoang khi mt ham ao c minh hoa trong lp c s khng co thao tac nao co y nghia thc hin. Tinh hung nay la ph bin vi thng mt lp c s khng inh nghia mt lp y u bi chinh no. Thay vao o, no chi n gian cung cp mt loi thit lp cua ham s va bin s cho lp dn xut cung cp cho phn con lai. Khi khng co hanh ng y u cho ham ao lp dn xut thc hin. S lin quan la bt ki lp dn xut nao phai ghi e ln ham nay. Chc chn rng iu nay se xut hin, C++ h tr ham ao nguyn mu. Mt ham ao nguyn mu khng co inh nghia lin quan ti lp c s. Chi co mu ban u cua ham la bao gm ca. tao mt ham ao nguyn mu, dung dang chung nay: virtual type func-name(parameter-list) = 0 The key part of this declaration is the setting of the function equal to 0. This tells the compiler that no body exists for this function relative to the base class. When a virtual function is made pure, it forces any derived class to override it. If a derived class does

605

not, a compile-time error results. Thus, making a virtual function pure is a way to guarantee that a derived class will provide its own redefinition. T khoa cua khai bao nay la thit lp ham bng 0. iu nay bao trinh bin dich rng khng co cai nao tn tai cho ham nay lin quan ti lp c s. Khi mt ham ao c lam cho nguyn mu, no pha bt ki lp dn xut nao ghi e ln no. Nu lp dn xut khng nh vy, chay bin dich cho kt qua li. Vi vy, tao mt ham ao nguyn mu la cach am bao rng lp dn xut se cung cp inh nghia lai cua chinh no. When a class contains at least one pure virtual function, it is referred to as an abstract class. Since an abstract class contains at least one function for which no body exists, it is, technically, an incomplete type, and no objects of that class can be created. Thus, abstract classes exist only to be inherited. They are neither intended nor able to stand alone. It is important to understand, however, that you can still create a pointer to an abstract class, since it is through the use of base class pointers that run-time polymorphism is achieved. (It is also permissible to have a reference to an abstract class.) Khi mt lp cha it nht mt ham nguyn mu, no xem nh la mt lp tru tng. Khi mt lp tru tng cha it nht mt ham ma khng co cai nao tn tai, no la, mt cach ki thut, mt loai khng hoan thanh, va khng co i tng cua lp co th c tao. Vi vy, lp tru tng tn tai chi tha k. No khng d inh khng th ng mt minh. Tht la quan trong hiu, tuy nhin, ban co th cung tao mt con tro cho lp tru tng , t khi no thng qua cach dung cua con tro lp c s chay ch a hinh c hoan thanh. (No cung chp nhn c khi co mt tham chiu cho mt lp tru tng.) When a virtual function is inherited, so is its virtual nature. This means that when a derived class inherits a virtual function from a base class and then the derived class is used as base for yet another derived class, the virtual function can be overridden by the final derived class (as well as the first derived class). For example, if base class B contains a virtual function called f(). And D1 inherits B and D2 inherits D1, both D1 and D2 can override f() relative to their respective classes. Khi mt ham ao c tha k, vi th la ao t nhin cua no. iu nay nghia la khi mt lp dn xut tha k mt ham ao t mt lp c s va sau o lp dn xut c dung nh c s cho lp dn xut khac, ham ao co th ghi e bng lp dn xut cui cung (tt nh la lp dn xut u tin). Vi du, nu lp c s B cha mt ham ao goi la f(). Va D1 tha k B va D2 tha k D1, ca hai D1 va D2 co th ghi e f() lin quan tng lp tng ng.

EXAMPLES (VI DU)

606

Here is an improved version of the program shown in Example 4 in the preceding section. In this version, the function getarea() is declared as pure in the base class area. y la phin ban cai tin cua chng trinh a trinh bay vi du 4 trong phn trc. Trong phin ban nay, ham getarea() c hin thi nh la nguyn mu trong vung lp c s. // To ra mt lp tru tng #include <iostream> using namespace std;

class area { double dim1, dim2; // Ni cha kch thc cc hnh public: void setarea(double d1, double d2) { dim1 = d1; dim2 = d2; } void getdim(double &d1, double &d2) { d1 = dim1; d2 = dim2; } virtual double getarea() = 0; // Hm o thun ty };

class rectangle : public area {

607

public: double getarea() { double d1, d2; getdim(d1, d2); return d1 * d2; } };

class triangle : public area { public: double getarea() { double d1, d2;

getdim(d1, d2); return 0.5 * d1 * d2; } };

int main() { area *p; rectangle r; trisngle t;

608

r.setarea(3.3, 4.5); r.setarea(4.0, 5.0);

p = &r; cout << Rectangle has area: << p->getarea() << \n;

p = &t; cout << Triangle has area: << p->getarea() << \n;

return 0; }

Now that getarea() is pure, it ensures that each derived class will override it. By gi getarea() la nguyn mu, bao am rng mi lp dn xut se ghi e ln no. The following program illustrates how a functions virtual nature is preserved when it is inherited: Chng trinh di y minh hoa ham ao t nhin c gi nh th nao khi no c tha k: // Nhng hm o gi li bn cht o khi c tha k. #include <iostream> using namespace std;

class base {

609

public: virtual void func() { cout << Using base version of func(): ; } };

class derived1 : public base { public: void func() { cout << Using derived1s version of func(): ; } };

// derived2 tha k derived1. class derived2 : public derived1 { public: void func() { cout << Using derived2s version of func(): ; } };

int main()

610

{ base *p; base ob; derived1 d_ob1; derived2 d_ob2;

p = &ob; p->func(); // s dng hm func() ca lp base

p->&d_ob1; p->func(); // s dng hm func() ca lp derived1

p->&d_ob2; p->func(); // s dng hm func() ca lp derived2

return 0; }

In this program, the virtual function func() is first inherited by derived1, which overrides it relative to itself. Next, derived2 inherits derived1. In derived2, func() is again overridden. Trong chng trinh nay, ham ao func() c tha k u tin bi derived1, ghi e ln no lin quan ti chinh no. Tip theo, derived2 tha k derived1. Trong derived2, func() c ghi e ln na. Because virtual functions are hierarchical, if derived2 did not override func(), when d_ob2 was accessed, derived1s func() would have been used. If either derived1 nor derived2 had overridden func(), all references to it would have been routed to the one defined in base. 611

Bi vi ham ao co th bc, nu derived2 khng ghi e func(), khi d_ob2 c truy xut, func() cua derived1 se c dung. Nu ca hai derived1 va derived2 ghi e func(), tt ca s tham khao cho no sec gi thng ti mt inh nghia c s.

EXERCISES (BAI TP)


1. On your own, experiment with the two example programs. Specifically, try creating an object by using area from Example 1 and observe the error message. In Example 2, try removing the redefinition of func() within derived2. Confirm that, indeed, the version inside derived1 is used. Ban hay t rut ra kinh nghim vi hai chng trinh vi du. Mt cach ro ran, th tao mt i tng bng cach s dung vung t vi du 1 va quan sat li. Trong vi du 2, th bo s inh nghia lai cua func() trong vong derived2. Xac nhn rng, tht vy, phin ban bn trong derived1 c dung. 2. Why cant an object be created by using an abstract class? Tai sao mt i tng co th c tao bng cach s dung lp tru tng? 3. In Example 2, what happens if you remove only the redefinition of func() inside derived1? Does the program still compile and run? If so, why? Trong vi du 2, iu gi xay ra khi ban bo duy nht s inh nghia lai cua func() bn trong derived1? Co phai chng trinh tip tuc bin dich va chay? Nu co, tai sao?

10.4. APPLYING POLYMORPHISM - AP DUNG A HINH


Now that you know how to use a virtual function to achieve run-time polymorphism, it is time to consider how and why to use it. As has been stated many time in this book, polymorphism is the process by which a common interface is applied to two or more similar (but technically different) situation, thus implementing the one interface, multiple methods philosophy. Polymorphism is important because it can greatly simplify complex systems. A single, well-defined interface is used to access a number of different but related actions, and artificial complexity is removed. In essence, polymorphism allows the logical relationship of similar actions to become apparent.

612

Thus, the program is easier to understand and maintain. When related actions are accessed through a common interface, you have less to remember. By gi ban a bit s dung ham ao chay ch a hinh nh th nao, o la thi im nhn ra dung no nh th nao va tai sao phai dung no. Nh a phat biu nhiu ln trong cun sach nay, a hinh la qua trinh bi mt din mao chung c ap dung hai hay nhiu tinh hung ging nhau (nhng ki thut thi khac nhau), vi vy thi hanh trit ly mt giao din, nhiu phng phap. a hinh la quan trong vi no co th n gian hoa h thng phc tap. Mt n th, giao din inh nghia tt c dung truy xut s cua s khac nhau nhng hanh ng lin quan nhau, va s phc tap nhn tao c bo. Thc cht, a hinh cho phep mi quan h logic cua hanh ng ging nhau tr nn ro rang. Vi vy, chng trinh n gian hn hiu va duy tri. Khi hanh ng lin quan c truy xut thng qua giao din chung, ban it phai nh. There are two terms that are often linked to OOP in general and to C++ specifically. They are early binding and late binding essentially refers to those events that can be known at compile time. Specifically, it refers to those function calls that can be resolved during compilation. Early bound entities include normal functions, overloaded functions, and non-virtual member and friend functions. When these types of functions are compiled, all address information necessary to call them is known at compile time. The main advantage of early binding (and the reason that it is so widely used) is that it is very efficient. Calls to function bound at compile time are the faster types of function calls. The maintain disadvantage is lack of flexibility. Co hai dang ma thng lin kt vi hng i tng noi chung va C++ noi ring. No sm kt ni va kt ni sau cung v c ban xem nh nhng s kin co th c bit thi gian bin dich. Mt cach ro rang, no xem nhng ham o goi rng co th giai quyt trong sut s bin dich. u tin gii han thc th bao gm ham thng thng, nap chng ham, va thanh phn khng ao va ham ban. Khi nhng loai cua ham c bin dich, tt ca nhng thng tin ia chi cn thit goi no c bit tai thi im bin dich. S thun li chinh cua ni kt sm nay (va ly do ma no c s dung rng rai) la no rt co hiu qua. Nhng gii han cua li goi ham tai thi im bin dich la loai nhanh hn cua li goi ham. S khng thun tin la thiu s uyn chuyn. Late binding refers to events that must occur at run time. A late bound function call is one in which the address of the function to be called is not known until the program runs. In C++, a virtual function is a late bound object. When a virtual function is accessed via a base class pointer, the program must determine at run time what type of object is being pointed to and then select which version of the overridden function to execute. The main advantage of late binding is flexibility at run time. Your program is free to respond to random events without having to contain large amounts of contingency code. Its primary disadvantage is that there is more overhead 613

associated with a function call. This generally makes such calls slower than those that occur with early binding. K thut kt ni tr l l k thut cho php s kin ch xy ra khi chng trnh thc thi. Mt hm kt ni tr s gi mt a ch ca hm c gi- hm ny ch c nhn dng trong khi chng trnh chy. Trong C++, mt hm o l mt i tng nh th. Khi hm o c truy xut qua con tr lp c s, chng trnh s quyt nh loi i tng no c tr n v la chn n thc thi. im thun chnh ca k thut kt ni tr l s uyn chuyn trong khi chy. Chng trnh ca bn s c thi gian p ng cc s kin ngu nhin m khng phi cha mt lng ln cc on m thc thi khng cn thit. im bt tin nht l c nhiu kt ni hn khi hm ny c gi. Chnh v iu ny m lm cho vic gi tr nn chm hn so vi vic kt ni sm.

EXAMPLES
Here is a program that illustrates one interface, multiple methods. It defines an abstract list class for integer values. The interface to the list is defined by the pure virtual functions store() and retrieve(). To store a value, call the store() function. To retrieve a value from the list, call retrieve(). The base class list does not define any default methods for these actions. Instead, each derived class defines exactly what type of list will be maintained. In the program, two lists operate completely differently, each is accessed using the same interface. You should study this program carefully. y l mt chng trnh minh ha mt hm, nhiu chc nng. N nh ngha mt lp danh sch tru tng cho s nguyn. B mt ca danh sch c nh ngha bi cc hm o nguyn gc l store() v retrieve(). ch d liu, gi hm store(). ly d liu gi hm retrieve(). Lp c s ca danh sch khng nh ngha bt k mt phng thc mc nh no cho nhng hnh ng ny. Thay vo , mi lp dn xut s nh ngha chnh xc loi danh sch s c duy tr. Trong chng trnh, c 2 danh sch cc thao tc hon ton khc nhau, mi ci c truy xut ging nhau v hnh thc. Bn hy xem xt k chng trnh. // Gii thch cc ham o. #include <iostream> #include <cstdlib> #include <cctype> using namespace std;

614

class list { public: list *head; // con tr bt u danh sch. list *tail; // con tr kt thc danh sch. list *next; // con tr ch mc tp theo. list num; // gi tr c lu tr.

list() { head = tail = next = NULL; } virtual void store(int i) = 0; virtual int retrieve() = 0; };

// To mt danh sch kiu hng i. class queue : public list { public: void store(int i); int retrieve(); };

void queue::store(int i) { list *item;

item = new queue; if(!item) {

615

cout << Allovation error.\n; exit(1); } item->num = i;

// t kt thc danh sch. if(tail) tail->next = item; tail = item; item->next = NULL; if(!head) head = tail; }

int queue::retrieve() { int i; list *p;

if(!head) { cout << List empty.\n; return 0; }

// Ly ra t v tr bt u ca danh sch. i = head->num; p = head;

616

head = head->next; delete p;

return i; }

// To ra mt danh sch ngn xp. class stack : public list { public: void store(int i); int retrieve(); };

void stack::store(int i) { list *item;

item = new stack; if(!item) { cout << Allovation error.\n; exit(1); } item->num = i;

// t trn pha trc danh sch tnh ton ngn xp.

617

if(head) tail->next = item; head = item; if(!tail) tail = head; }

int stack::retrieve() { int i; list *p;

if(!head) { cout << List empty.\n; return 0; }

// Ly ra t v tr bt u ca danh sch. i = head->num; p = head; head = head->next; delete p;

return i; }

int main()

618

{ list *p;

// Khai bo hng i. queue q_ob; p = &q_ob; // Tr n hng i.

p->store(1); p->store(2); p->store(3);

cout << Queue: ; cout << p->retrieve(); cout << p->retrieve(); cout << p->retrieve();

cout << \n;

// Khai bo ngn xp. stack s_ob; p = &s_ob; // Tr n ngn xp.

p->store(1); p->store(2); p->store(3);

619

cout << Stack: ; cout << p->retrieve(); cout << p->retrieve(); cout << p->retrieve();

cout << \n;

return 0; }

The main() function in the list program just shown simply illustrate that the list classes do, indeed, work. However, to begin to see why run-time polymorphism is so powerful, try using this main() instead: Hm main() trong chng trnh ch minh ha n gin lp cc danh sch lm g. Tuy nhin, bn cng c th thy c ti sao s kt ni a hnh li rt mnh, th s dng hm main ny thay th: int main() { list *p; stack s_ob; queue q_ob;

char ch; int i;

620

for(i=0; i<10; i++) { cout << Stack or Queue? (S/Q): ; cin >> ch; ch = tolower(ch); if(ch==q) p = &q_ob; else p = &s_ob; p->store(i); }

cout << Enter T to terminate\n; for(;;) { cout << Remove from Stack or Queue? (S/Q): ; cin >> ch; ch = tolower(ch); if(ch==t) break; if(ch==q) p = &q_ob; else p = &s_ob; cout << p->retrieve() << \n; }

cout << \n; return 0; } This main() illustrate how random events that occur at run time can be easily handled by using virtual functions and run-time polymorphism. The program executes a for loop 621

running from 0 to 9. Each iteration through the loop, you are asked to choose into which type of list the stack or the queue you want to put a value. According to your answer, the base pointer p is set to point to the correct object and the current value of i is stored. Once the loop is finished, another loop begins that prompts you to indicate from which list to remove a value. Once again, it is your response that determines which list is selected. Hm main ny minh ha lm th no m cc s kin ngu nhin xy ra trong lc chy c th d dng c qun l bng cch s dng hm o v tnh a hnh trong khi chy. Chng trnh ny thc thi mt vng lp t 0 n 9. Mi ln lp, bn c yu cu l chon loi danh sch no- ngn xp hay l hng i- m bn mun t mt gi tr vo. Ty vo cu tr li ca bn, con tr c s p s ch vo ng i tng v gi tr hin thi ca I c lu d. mi khi mt vng lp kt thc,mt vng lp khc bt u yu cu bn ch ra gi tr no c loi b ra khi loi danh sch no. Mt ln na, bn s thy c chng trnh la chn loi danh sch nh th no. While this example is trivial, you should be able to see how run-time polymorphism can simply a program that must respond to random event. For instance, the Windows operating system interfaces to a program by sending it messages. As far as the program is concerned, these messages are generated at random, and your program must respond to each one as it is received. One way to respond to these messages is through the use of virtual functions. Khi m v d n gin, bn co th d dng xem xt lm th no m tnh a hnh trong khi chy c th d dng cho mt chng trnh thc thi cc s kin ngu nhin. Mi trng hp, h thng thao tc windows s gi n chng trnh mt thng ip khc nhau. Khi m chng trnh c kt ni, nhng thng ip ny c pht sinh ngu nhin, v chng trnh ca bn s phi hi p mi ln m n nhn c thng ip. Mt cch thc hin iu ny l s dng hm o.

EXERCISES
1. Add another type of list to the program in Example 1.Have this version maintain a sorted list (in ascending order). Call this list sorted. Thm vo mt loi danh sch khc vo trong chng trnh v d 1. c mt hm sp sp danh sch ( theo chiu tng). Gi hm sp xp ny. 2. On your own,think about ways in which you can apply run-time polymorphism to simplify the solutions to certain types of problems.

622

Theo bn, hy ngh cch bn c th cung cp cch thc gii quyt vn a hnh trong khi chy cho mi loi vn .

SKILL CHECK (KIM TRA KI NNG)

Mastery At this point you should be able to perform the following exercises and answer the questions. 1. What is a virtual function? 2. What types of functions cannot be made virtual? 3. How does a virtual function help achieve run-time polymorphism? Be specific 4. What is a pure virtual function? 5. What is an abstract class? What is a polymorphism class? 6. Is the following fragment correct? If not, why not? Nm vng Ti thi im ny bn c th lm nhng bi tp di y v tr li mt s cu hi. 1. Hm o l g? 2. Loi hm no khng th lm hm o? 3. Lm th no m hm o c th thc thi trong ch a hnh? Gii thch r 4. Th nao la ham ao c ban? 5. Th no l lp tru tng ( lp o)? th no l lp a k tha? 6. on no sau y ng? Nu khng ng, gii thch?

class base { public: virtual int f(int a) = 0;

623

// };

class derived : public base { public: int f(int a, int b) { return a*b; } // };

7. Is the virtual quality inherited? 8. On your own, experiment with virtual functions at this time. This an important concept and you should master the technique. 7. Th no l k tha o? 8. Theo bn, kinh nghim x l hm o ti thi im ny. N l khi nim quan trng v bn nn lm ch k thut ny. Cumulative This section checks how well you have integrated material in this chapter with that from the preceding chapters. 1. Enhance the list example from Section 10.4, Example 1, so that it overloads the + anf operators. Have the + store an element and the retrieve an element. 2. How do virtual functions differ from overloaded functions? 3. On your own, reexamine some of the function overloading examples presented earlier in this book. Determine which can be converted to virtual functions. Also, think about ways in which a virtual function can solve some of your own programming problems. Nng cao Phn ny kim tra xem bn c thc s nm vng v kt hp ni dung chng ny vi nhng chng trc hay khng

624

1. Nng cao thm danh sch v d trong phn 10.4 v d 1, lm th no np chng + v -. Ton t + cha thnh phn v dung loi tr thnh phn. 2. Hm o khc vi hm np chng nh th no? 3. Theo bn, kim tra li xem mt vi hm np chng c gii thiu trc trong cun sch ny. Quyt nh xem c nn chuyn sang hm o hay khng? Ngh cch dng hm o gii quyt mt s vn trong chng trnh ca bn. This section checks how well you have integrated material in this chapter with that form the preceding chapters. Following is a reworked version of the inventory class presented in the preceding chapter. Write a program that fills in the functions store() and retrieve(). Next, create a small inventory file in disk containing a few entries. Then, using random I/O, allow the user to display the information about any item by specifying its record number. Phn ny dng kim tra cch tt nht kt hp nhng th c trong chng ny vi cc chng trc. 1. Sau y l mt bn c lm li ca lp inventory c gii thiu chng trc. Vit mt chng trnh in y vo hm store() v retrieve(). K tip, to mt file kim tra nh trn disk bao gm mt vi s ghi chp. Sau , s dng I/O ngu nhin cho php ngi s dng xut thng tin v bt c mt vt no c xc nh bi s mu tin ca n. #include <fstream> #include<cstring> using namespace std; #define size 40

class inventory { char item[size]; //Name of item int onhand; //number on hand double cost; //cost of item

625

public: inventory (char*i, int o, double c) { strcpy (item, i); onhand = o; cost = c; }

void store (fstream &stream); void retrieve (fstream &stream); friend ostream &operator<<(ostream &stream, inventory ob); friend istream &operator >>(istream &stream, inventory &ob); };

ostream &operator<<(ostream &stream, inventory ob) { stream <<ob.item <<":" <<ob.onhand; stream <<"on hand at $" <<ob.cost <<'\n'; return stream; }

istream &operator>>(istream &stream, inventory &ob) {

626

cout << "Enter item name:"; stream >>ob.item; cout <<"Enter number on hand:"; stream>>ob.onhand; cout <<"Enter cost:"; stream >>ob.cost;

return stream; }

int main(int argc, char* argv[]) { return 0; } As a special challenge, on your own, create a stack class for characters that stores them in a disk file rather than in an art in memory. 2. Khi gp mt th thch c bit, th to mt lp stack cho nhng k t lu chng ln disk file th tt hn l trnh by trong b nh.

627

CHAPTER 11 TEMPLATES AND EXCEPTION HANDLING NHNG BIU MU V TRNH IU KHIN BIT L
Chapter objectives
11.1. GENERIC FUNCTIONS Nhng hm tng qut 11.2. GENERIC CLASSES Nhng lp tng qut 11.3. EXCEPTION HANDLING iu khin ngoi l 11.4. MORE ABOUT EXCEPTION HANDLING iu khin ngoi l (tt) 11.5. HANDLING EXCEPTIONS THROWN BY NEW Nhng iu khin nm bng new

This chapter discusses two of C++s most important high-level features: templates and exception handling. While neither was part of the original specification for C++, both were added several years ago and are defined by Standard C++. They are supported by all modern C++ compilers. These two features help you achieve two of the most elusive goals in programming: the creation of reusable and resilient code. Chng ny tho lun v 2 tnh nng quan trng ca C++ l: nhng biu mu v iu khin ngoi l. Khng nm trong C++ ban u v c hai c pht trin thm cch y vi nm v c nh ngha bi C++ chun. Chng c h tr bi tt c cc trnh bin dch C++ hin i. Hai tnh nng ny gip bn t c nhng thng li trong lp trnh: bng vic to ra cc on m mang tnh linh hot v c kh nng ti s dng.

628

Using templates, it is possible to create generic functions and classes. In a generic function or class, the type of data that operated upon is specified as a parameter. This allows you to use one function or class with several different types of data without having to explicitly recode a specific version for each different data type. Thus, templates allow you to create reusable code. Both generic functions and generic classes are discussed here. Bng vic dung biu mu, hon ton to c nhng hm v lp tng qut. Trong mt hm hay lp tng qut, loi d liu m c dung th c ch nh nh mt tham s. iu ny cho php bn dng mt hm hay lp vi vi kiu d liu khc nhau m khng phi vit li code cho mi loi d liu. V vy, cc biu mu cho php bn to ra on code ti s dng. C hm v lp tng qut s c ni y. Exception handling is the subsystem of C++ that allows you to handle errors that occur at run time in a structured and controlled manner. With C++ exception handling your program can automatically invoke an error handling routine when an error occurs. The principle advantage of exception handling is that it automates much of the error handling code that previously had to be coded by hand in any large program. The proper use of exception handling helps you to create resilient code iu khin ngoi l l h thng ph ca C++ m cho php bn iu khin cc li xy ra trong khi chy trong mt cu trc hay mt loi kim sot. Vi iu khin ngoi l chng trnh ca bn c th t ng gi mt th tc iu khin li khi li xy ra. Thun li chnh ca iu khin ngoi l l t ng ha m iu khin li m c code bng tay trong bt k mt chng trnh ln no trc

Before proceeding, you should be able to correctly answer the following questions and do the exercises. What is a virtual function? What is a pure virtual function? If a class declaration contains a pure virtual function, what is that class called, and what restrictions apply to its usage? Run-time polymorphism is achieved through the use of ___________ functions and _____________ class pointers. (Fill in the missing words.) If, in a class hierarchy, a derived class neglects to override a (non-pure) virtual function, what happens when an object of that derived class calls that function?

629

What is the main advantage of run-time polymorphism? What is its potential disadvantage? Trc khi tip tc bn nn tr li ng cc cu hi v lm cc bi tp sau: Hm o l g? Hm nguyn o l g? Nu mt khai bo lp cha mt hm o, lp c gi ci g v s gii hn khi ng dng a x c to ra qua vic s dng ca_________________ cc hm v __________ cc con tr lp. (in vo ch trng) Nu trong mt lp c tn ti, S b qua mt lp c xut pht vt qua mt hm o, iu g xy ra khi mt i tng m lp xut pht gi hm ? 5. Thun li chnh ca a x l g? Nhng bt li tim n l g?

11.1. GENERIC FUNCTIONS NHNG HM TNG QUT


A generic function defines a general set of operations that will be applied to various types of data. A generic function has the type of data that it will operate upon passed to it as a parameter. Using this mechanism, the same general procedure can be applied to a wide range of data. As you know, many algorithms are logically the same no matter what type of data is being operated upon. For example, the Quicksort algorithm is the same whether it is applied to an array of integers or an array of floats. It is just that the type of data is being sorted is different. By creating a generic function, you can define, independent of any data, the nature if the algorithm. Once this is done, the compiler automatically generates the correct code for the type of data that is actually used when you execute the function. In essence, when you create a generic function you are creating a function that can automatically overload itself. Hm tng qut nh ngha mt b cc x l m s dung cho nhiu kiu d liu. Mt hm tng qut c dng d liu m x l thng qua mt tham s. S dng k thut ny, th tc tng qut c th dng cho mt vng d liu rng. Nh bn bit, nhiu gii thut th logic m khng c x l trn loi d liu. V d, QuickSort cng dng nh nhau i vi mng s nguyn hay s thc. N ch khc nhau kiu d liu. Bng cch to ra cc hm chung, bn c th nh ngha, khng ph thuc vo kiu d liu, t nhin vi gii thut. Mt khi c dng, trnh bin dch t ng sinh ra m chun cho kiu d liu thc s c s dng khi bn thc thi chng trnh. Thc cht, khi bn to ra hm chung 630

thi bn ang to ra hm t ng ha qu ti hm chnh bn thn n. A generic function is created using the keyword template. The normal meaning of the word template accurately reflects the keywords use in C++. It is used to create a template (or framework) that describes what a function will do, leaving it to the compiler to fill in the details as needed. The general form of a template function definition is shown here: Mt hm chung c to bng t kha template. Ngha thng ca n l biu mu word mt cch rt xt ngha vi t kha c dng trong C++. N dng to ra mt biu mu( hay mt khung sn) m m t mt hm thc hin ci g, trnh bin dch s in cc chi tit cn. Hnh dng tng qut ca mt biu mu chung c nh ngha nh sau: Template <class Ttype> ret-type func-name( parameter list) { // body of function }

Here Ttype is a placeholder name for a data type used by the function. This name can be used within the function definition. However, it is only a placeholder: the compiler will automatically replace this placeholder with an actual data type when it creates a specific version of the function. Although the use of the keyword class to specify a generic type in a template declaration is traditional, you can also use the keyword typename. y Ttype th cho tn kiu d liu dng cho hm. Tn ny c th dng m khng c nh ngha hm. Tuy vy, ch l thay th, trnh bin dch s t ng thay vi tr ny vi kiu d liu thc s m n c nh r trong tng hm. Mc d s dng t kha class ch r loi tng qut trong mt khai bo biu mu l truyn thng, bn cng c th dng t kha typename.

Examples
The following program creates a generic function that swaps the values of the two variables it is called with. Because the general process of exchanging two values is independent of the type of the variables, this process is good choice to 631

be made into a generic function. Chng trnh sau to mt hm chung m hon chuyn gi tr ca 2 bin m n c gi cng. Bi v tin trnh chung ca s trao i 2 gi tr ny l khng ph thuc vo kiu d liu ca bin, tin trnh ny l s chn la tt lm mt hm chung. // Function on temple example #include <iostream> using namespace std;

// this is a function template template <class X> void swapargs(X &a, X &b) { X temp;

temp = a; a = b; b = temp; }

int main() { int i = 10, j = 20; float x = 10.1, y = 23.3;

cout << Original i, j: << i << endl; cout << Original x, y: << x << endl; 632

<< j << << y <<

swapargs(i, j); // swap integers swapargs(x, y); // swap floats

cout << Swapped i, j: << i << endl; cout << Swapped x, y: << x << endl;

<< j << << y <<

return 0; }

The keyword template is used to define a generic function. The line template <class X> void swapargs(X &a, X &b) tells the compiler two things: that a template is being created and that a generic definition is beginning. Here X is a generic type that is used as a placeholder. After the template portion, the function swapargs() is declared, using X as the data type of the value that will be swapped. In main(), the swapargs() function is called using two different types of data: integers and floats. Because swapargs()-one that will exchange integer values and one that will exchange floating-point values. You should compile and try this program now. T kha template dng nh ngha hm chung. Dng trn ni vi trnh bin dch 2 vic: mt biu mu ang c to ra v hm chung ang bt u. y X l kiu chung m dng thay th. Phn biu mu sau, hm swaparg() c khai bo, dng X nh kiu d liu m s c hon chuyn. Trong main(), hm swaparg() c gi vi hai kiu dng l s nguyn v s thc. Bi v swaparg()- mt mt s thay i gi tr nguyn v mt khc s thay i cc gi tr s chm ng. Bn nn bin dch v th chng trnh ngay. Here are some other terms that are sometimes used when templates are discussed and that you might encounter in other C++ literature. First, a generic function (that is, a function definition preceded by a template statement)

633

is also called a template function. When the compiler creates a specific version of this function, it is said to have created a general function. The act of generating a function is referred to as instantiating it. Put differently, a generated function is a specific instance of a templates function. y c mt s thut ng m thnh thong c dng khi cc biu mu c tho lun v bn c th gp trong cc vn phong C++ khc. u tin, mt hm chung( l mt hm c s nh ngha bng mt cu pht biu template) cng cn c gi l hm biu mu(template). Khi trnh bin dch to ra mt phin bn c nh r ca hm ny, n s to ra mt hm chung. Hm ng tng qut ha mt hm c quy vo vic nh th thuyt minh n. S khc bit l mt hm chung l mt v d xc nh ca mt hm biu mu. The template portion of a generic function definition does not have to be on the same line as the functions name. For example, the following is also a common way to format the swapargs() function: Phn biu mu ca mt nh ngha hm chung khng phi nm trn mt dng i vi tn hm. V d, nh sau: template <class X> void swapargs(X &a, X &b) { X temp; temp = a; a=b b=temp; }

If you use this form, it is important to understand that no other statements can occur between the templates statement and the start of the generic function definition. For example, the following fragment will not compile: Nu bn s dng theo kiu ny, th gia cu template v ni bt u nh ngha hm khng c c bt k cu khai bo no ht. V d, on sau s khng c bin dch //This will not compile 634

template <class X> int i; // this is an error

void swapargs(X &a, X &b) { X temp;

temp = a; a = b; b = temp; } As the comments imply, the template specification must directly precede the rest of the function definition. Nh ch thch gi , s ch r biu mu phi trc tip trc phn nh ngha hm As mentioned, instead of using the keyword class, you can use the keyword typename to specify a generic type in a template definition. For example, here is another way to declare the swapargs() function. Nh cp, thay v dng t kha class, bn c th dng t kha typename ch ra kiu chung trong nh ngha biu mu. V d, y c cch khc khai bo hm swapargs(). // Use typename template <typename X> void swapargs(X &a, X &b) { X temp;

temp = a; a = b; b = temp;

635

} The typename keyword can also be used to specify an unknown type within a template, but this use is beyond the scope of this book. T kha typename cng c th dng ch r kiu cha bit bn trong mt biu mu, nhng cch dng ny nm ngoi phm vi cun sch. You can define more than one generic data type with the template statement, using a comma-seperated list. For example, this program creates a generic function that has two generic types: Bn c th nh ngha nhiu hn mt kiu d liu chung vi pht biu template, vic dng mt danh sch phn cch bi du phy. V d, chng trnh sau to ra mt hm chung m c hai kiu d liu chung: #include <iostream> using namespace std;

template <class type1, class type2> void myfunc(type1 x, type2 y) { cout << x << << y << endl; } int main() { myfunc(10, hi);

myfunc(0.23, 10L);

return 0; }

636

In this example, the placeholder, types type1 and type2 are replaced by the compiler with the data types int and char and double and long, respectively, when the compiler generates the specific instances of myfunc().

When you create a generic function, you are, in essence, allowing the compiler to generate as many different versions of that function as necessary to handle the various ways that your program calls that function. Trong v d ny, phn thay th, type1 v type2 c trnh bin dch thay bng kiu d liu int v char, double v long, khi trnh bin dch tng qut ha nhng th d xc nh ca hm myfunc(). Nh rng khi bn to ra hm chung, th bn cht ca n l bn cho php trnh bin dch tng qut ha nhiu hm cng loi li nh cn thit kim sot nhiu cch khc nhau khi chng trnh gi hm. Generic functions are similar to overloaded functions except that they are more restrictive. When functions are overloaded you can have different actions performed within the body of each function. But a generic function must perform the same general action for all versions. For example, the following overloaded functions cannot be replaced by a generic function because they do not do the same thing: Hm chung cng ging nh cc qu ti hm ngoi tr chng gii hn hn. Khi cc hm c qu ti bn c th c nhiu hnh ng khc nhau c thi hnh bn trong thn hm. Nhng hm tng qut th ch c nhng hnh ng chung cho tt c cc kiu. V d, cc hm qu ti sau khng th thay th bi hm chung bi v chng khng thc hin cng vic ging nhau: void outdata(int i) { cout << i; }

void outdata(double d) {

637

cout << setprecision(10) << setfill(#); cout << d; cout << setprecision(6) << setfill( ); }

Even though a template function overloads itself as needed, you can explicitly overload one, too. If you overload a generic function, that overloaded function overrides (or hides) the generic function relative to that specific version. For example, consider this version of Example 1: Mc d mt hm chung qu ti chnh bn thn n, bn cng c th qu ti r rang mt hm thi. Nu bn qu ti hm chung, hm qu ti s ghi (hay n) hm chung c cng quan h. V d, : //Overloading a template function #include <iostream> using namespace std;

template <class X> void swapargs(X &a, X &b) { X temp;

temp = a; a = b; b = temp; }

// This overrides the generic version of swapargs() void swapargs(int a, int b) { 638

cout << This is inside swapargs(int, int) \n; }

int main() { int i = 10, j = 20; float x = 10.1, y = 23.3;

cout << Original i, j: << i << << j << endl; cout << Original x, y: << x << <<y << endl;

swapargs(i, j); // calls overloaded swapargs() swapargs(x, y); // swap floats

cout << Swapped i, j: endl; cout << Swapped x, y: endl;

<< i << << j << << x << << y <<

return 0; }

As the comments indicate, when swapargs(i, j) is called, it invokes the explicitly overloaded version of swapargs() defined in the program. Thus, the compiler does not generate this version of the generic swapargs() function because the generic function is overridden by the explicit overloading.

639

Nh ch thch ch ra, khi swapargs(i, j) c gi, chng hm swapargs() c qu ti trong chng trnh. V vy, trnh bin dch khng tng qut ha hm swapargs() bi v chng c chp bi hm qu ti. Manual overloading of a template, as shown in this example, allows you to tailor a version of a generic function to accommodate a special situation. However, in general, if you need to have different version of a function for different data types, you should use overloaded functions rather than templates. Vic qu ti th cng ca mt biu mu, trong v d ny, cho php bin i mt hm chung cho ph hp vi tnh hung c bit. Tuy nhin, trong tng qut nu bn cn mt hm cho nhiu kiu d liu khc nhau, bn nn dng qu ti hm hn l biu mu.

Exercises
If you have not done so, try each of the preceding examples. Nu bn cha lm, th vi nhng v d trc. Write a generic function, called min(), that returns the lesser of its two arguments. For example, min(3, 4) will return 3 and min(c, a) will return a. Demonstrate your function in a program. Vit mt hm chung, tn l min(), m tr v gi tr thp hn. V d, min(3, 4) s tr v 3 v min(c, a) s tr v a. Th nghim hm ca bn trong mt chng trnh A good candidate for a template function is called find(). This function searches an array for an object. It returns either the index of the matching object (if one is found) or -1 if no match is found. Here is the prototype for a specific version of find(). Convert find() into a generic function and demonstrate your solution within a program. (The size parameter specifies the number of elements in the array.) Mt ng c tt cho biu mu l hm mang tn find(). Hm ny tm trong mng mt i tng. N tr v ch s ca i tng (nu c) v -1 nu khng. y l cch thit k hm find(). Chuyn find() thnh hm chung v thi trin gii php ca bn trong 1 chng trnh. (Tham s size ch ra s lng phn t trong mng).

int find(int object, int *list, int size) {

640

// } 4. In your own word, explain why generic functions are valuable and may help simplify the source code to programs that you create. Theo bn, gii thch ti sao hm chung c gi tr v c th gip n gin ha m chng trnh.

11.2.

GENERIC CLASSES LP TNG QUT

In addition to defining generic functions, you can also define generic classes. When you do this, you create a class that defines all algorithms used by that class, but the actual type of the data being manipulated will be specified as a parameter when objects of that class are created. Thm vo nh ngha cc hm tng qut, bn cng c th nh ngha cc lp tng qut. Khi bn lm iu ny, bn to ra mt lp m nh ngha tt c cc gii thut c dng bi lp , nhng kiu d liu thc s c thao tc s c ch r cc tham s khi i tng ca lp c to ra. Generic classes are useful when a class contains generalizable logic. For example, the same algorithm that maintains a queue of integers will also work for a queue of characters. Also, the same mechanism that maintains a linked list of mailing addresses will also maintain a linked list of auto part information. By using a generic class, you can create a class that will maintain a queue, a linked list, and so on for any type of data. The compiler will automatically generate the correct type of object based upon the type you specify when the object created. Cc lp chung th rt hu dng khi mt lp cha cc c tnh logic chung. V d, gii thut ging nhau duy tr hang i cc s nguyn s hot ng tng t vi hang i cc k t. Cng nh vy, cc k thut m duy tr mt danh sch lin kt ia ch email cng duy tr mt danh sch lin kt t ng cc thng tin. Bng vic dng cc hm tng qut, bn c th to ra mt lp m s duy tr hng i, mt danh sch lin kt v v.v cho bt k kiu d liu no. Trnh bin dch s t ng tng qut ha chnh xc kiu d liu da trn kiu i tng khi i tng c to ra. The general form of a generic class declaration is shown here: (dng chung ca lp

641

tng qut c khai bo nh sau) template <class Ttype> class class-name{ . }

Here Ttype is the placeholder type name that will be specified when a class is instantiated. If necessary, you can define more than one generic data type by using a comma-seperated list. y, Ttype l thay th cho tn kiu m s c ch r khi mt i tng c thuyt minh. Nu cn thit bn c th nh ngha nhiu hn mt kiu d liu chung bng cch dng danh sch lien kt bi du phy, Once you have created a generic class, you create a specific instance of that class by using the following general form: (Mt khi bn to ra mt lp chung bn to ra mt v d xc nh ca lp bng cch dng dng sau) class-name <type> ob; Here type is the type name of the data that the class will be operating upon. Member functions of a generic class are themselves, automatically generic. They need not be explicitly specified as such using template. y, type l tn kiu ca d liu m lp x l trn . Hm thnh vin ca lp chung l chnh bn thn chng, tng qut ha t ng. Chng khng cn ch r nh vic dng template. As you will see in Chapter 14, C++ provides a library that is built upon template classes. This library is usually referred to as the Standard Template Library, or STL for short. It provides generic versions of the most commonly used algorithms and data structures. If you want to use the STL effectively, youll need a solid understanding of template classes and their syntax. Bn s gp trong chng 14, C++ cung cp mt th vin m xy dng trn cc lp biu mu. Th vin ny thng dng tham kho nh th vin biu mu chun, hay STL ngn gn. N cung cp cc bn tng qut cho hu ht cc cu trc d liu v gii thut thng dng. Nu bn mun dng STL tc ng, bn s cn hiu v cc lp biu mu v c php ca chng

642

EXAMPLE This program creates a very simply generic singly linked list class. It then demonstrates the class by creating a linked list that stores characters. Chng trnh ny to ra mt danh sch lin kt n tng qut. N minh ha cho lp m to ra mt danh sch lin kt cc k t. // A simple generic linked list. #include <iostream> using namespace std;

template <class data_t> class list { data_t data; list public: list(data_t d); void add(list *node) {node->next = this; next = 0; } list *getnext() data_t getdata() }; { return next; } { return data; } *next;

template <class data_t> list<data_t>::list(data_t d) { data = d; next = 0; }

643

int main() { list<char> start(a); list<char> *p, *last; int i;

// build a list last = &start; for(i = 1; i < 26; i++) { p = new list<char> (a + i); p->add(last); last = p; }

// follow the list p = &start; while(p) { cout << p->getdata(); p = p->getnext(); }

return 0; }

644

As you can see, the declaration of a generic class is similar to that of a generic function. The actual type of data stored by the list is generic in the class declaration. It is not until an object of the list is declared that the actual data type is determined. In this example, objects and pointers are created inside main() that specify that the data type of the list will be char. Pay special attention to this declaration: Nh bn thy , s khai bo lp tng qut cng ging nh hm tng qut. Kiu d liu thc s c lu tr bi danh sch l chung trong s nh ngha lp. N khng cn l mt i tng ca danh sch khai bo m kiu d liu thc s mi quyt nh. Trong v d ny, cc i tng v con tr c to ra bn trong thn main() m ch r kiu i tng ca danh sch l k t. Ch vo s khai bo ny: list<char> start(a);

Notice: how the desired data type is passed inside the angle brackets. Ch : lm sao ra lnh kiu d liu c t trong du <>
You should enter and execute this program. It builds a linked list that contains the characters of the alphabet and then displays them. However, by simply changing specified when list objects are created, you can change the type of data stored by the list. For example, you could create another object that stores integers by using this declaration: Bn nn g v thc thi chng trnh. N to ra mt danh sch lin kt m cha cc k t ca bng alphabet v in ra mn hnh chng. Tuy nhin, bng s thay i n gin khi i tng danh sch c to, bn c th thay i kiu d liu cha bi danh sch. V d, bn c th to ra i tng khc m cha s nguyn bng khai bo sau: list<int> int_start(1); You can also use list to store data types that you create. For example, if you want to store address information, use this structure: Bn cng c th dng danh sch lin kt kiu d liu m bn to ra. V d, nu bn mun lu li thng tin a ch, dng cu trc sau: struct addr { char name[40];

645

char street[40]; char city[30]; char state[3]; char zip[12]; } Then, to use list to generate objects that will store objects of type addr, use a declaration like this (assuming that structvar contains a valid addr structure): Khi , dng danh sch tng qut ha cc i tng m s cha kiu i tng addr, dng khai bo nh th ny ( gi nh rng bin cu trc cha cu trc a ch hp l) list<addr> obj(structvar); Here is another example of generic class. It is a reworking of the stack class first introduced in Chapter1. However, in this case, stack has been made into a template class. Thus, it can be used to store any type of object. In the example shown here, a character stack and a floating-point stack are created: y l mt v d khc ca lp chung. N lm li lp ngn xp m gii thiu trong chng u. Tuy vy, trong trng hp ny, ngn xp s chuyn thnh lp chung. V vy, n c th c dng cha bt k kiu i tng no. Trong v d sau, ngn xp k t v ngn xp s chm ng s c to ra: // This function demonstrates a generic stack #include <iostream> using namespace std;

#define SIZE 10

// Create a generic stack class template <class StackType> class stack { StackType stck[SIZE]; //holds the stack int tos; //index of top of stack

646

public: void init() stack StackType pop(); }; // pop object from stack { tos = 0; } //initialize stack // push object on

void push (StackType ch);

//Push an object template <class StackType> void stack<StackType>::push(StackType ob) { if(tos == SIZE) {

cout << Stack is full. \n; return; } stck[tos] == ob; tos ++; }

// Pop an object. template <class StackType> StackType stack<StackType>::pop() { if(tos == 0) {

cout << Stack is empty.\n; 647

return 0; // Return Null or empty stack } tos--; return stck[tos]; }

int main() { // Demonstrate character stacks. stack<char> s1, s2; //Create two stacks int i;

// Initialize the stacks s1.init(); s2.init();

s1.push(a); s2.push(x); s1.push(b); s2.push(y); s1.push(c); s2.push(z);

for(i = 0; i < 3; i++) {

648

cout << Pop s1: << s1.pop() << \n; } for(i = 0; i < 3; i++) { cout << Pop s2: << s2.pop() << \n; }

//Demonstrate double stacks stack<double> ds1, ds2; //create two stacks

// initialize the stacks ds1.init(); ds2.init();

ds1.push(1.1); ds2.push(2.2); ds1.push(3.3); ds2.push(4.4); ds1.push(5.5); ds2.push(6.6);

for(i = 0; i < 3; i++) { cout << Pop ds1: << ds1.pop() << \n; }

649

for(i = 0; i < 3; i++) { cout << Pop ds2: << ds2.pop() << \n; }

return 0; }

As the stack class (and the preceding list class) illustrates, generic functions and classes provide a powerful tool that you can use to maximize your programming time because they allow you to define the general form of an algorithm that can be used with any type of data. You are saved from the tedium of creating separate implementations for each data type that you want the algorithm to work with. Nh lp ngn xp ( v lp danh sch trc ) minh ha, hm tng qut v lp tng qut cung cp mt cng c hiu qu m bn c th dng tng thi gian chng trnh bi v chng cho php nh ngha dng chung ca mt gii thut m c th dng cho bt k loi d liu no. Nh th bn b sung kiu d liu m bn mun dng bng chung gii thut. A template class can have more than one generic data type. Simply declare all the data types required by the class in a comma-separated list within the template specification. For example, the following short example creates a class that uses two generic data types: Mt lp biu mu c th c hn mt kiu d liu chung. Tht n gin khai bo tt c kiu d liu yu cu bi lp bng danh sch phn cch bi du phy bn trong s ch nh biu mu. V d, v d ngn sau to ra mt lp m dng hai kiu d liu: // This example uses two generic data types in a class definition. #include <iostream> using namespace std;

650

template <class Type1, class Type2> class myclass { Type1; Type2; public: myclass(Type1 a, Type2 b) { i = a; j = b; } void show() {cout << i << << j << \n; } };

int main() { myclass<int, double> ob1(10, 0.23); myclass<char, char *> ob2(X, This is a test);

ob1.show(); ob2.show();

//show int, double //show char, char *

return 0; }

This program produces the following output: 10 x 0.23 This is a test

The program declares two types of objects. ob1 uses integer and double data.

651

ob2 uses a character and a character pointer. For both cases, the compiler automatically generals the appropriate data and functions to accommodate the way the objects are created. Chng trnh khai bo 2 kiu i tng ob1 dng d liu l s nguyn v s thc, ob2 dng mt k t v mt con tr chui k t. C hai trng hp, trnh bin dch t ng tng qut ha d liu v hm thch hp cung cp cch thc m i tng c to ra.

Exercises
If you have not yet done so, compile and run the two generic class examples. Try declaring lists and/or stacks of different data types. Nu bn cha lm, hy bin dch v chy th v d hai lp trn. Th khai bo cc danh sch v/hay cc ngn xp ca nhiu kiu d liu khc nhau. Create and demonstrate a generic queue class. To v minh ha lp chung hng i. Create a generic class, called input, that does the following when its constructor is called: prompts the user for input. inputs the data entered by the user, and reprompts if the data is not within a predetermined range. To mt lp chung, mang tn input, m c nhng phng thc thit lp sau: Nhc ngi dng nhp Nhp d liu bi ngi dng v Nhc li rng d liu khng nm trong vng nh ngha li Objects of type input should be declared like this: ( Hm c dng sau: ) input ob(prompt message, min-value, max-value) Here prompt message is the message that prompts for input. The minimum and maximum acceptable values are specified by min-value and max-value, respectively. (Note: the type of the data entered by the user will be the same as the type of min-value and max-value.) y, li nhc l nhc nhp. Cc gi tr chp nhn ti thiu v cc i l minvalue v max-value. (Ch : kiu d liu m nhp bi ngui dng s cng kiu vi min-value v max-value.

652

11.3.

EXCEPTION HANDLING- IU KHIN NGOI L

C++ provides a built-in error handling mechanism that is called exception handling. Using exception handling, you can more easily manage and respond to run-time errors. C++ exception handling is built upon three keywords: try, catch and throw. In the most general terms, program statements that you want to monitor for exceptions are contained in a try block. If an exception (i.e., an error) occurs within the try block, it is thrown (using throw). The exception is caught, using catch, and processed. The following elaborates upon this general description. C++ cung cp mt k thut iu kim li gn lin m c gi l iu khin ngoi l. Dng iu khin ny, bn d dng kim sot v phn ng li cc li trong khi chy. iu khin ngoi l C++ c xy dng trn ba t kha: try, catch and throw. Trong hu ht cc trng hp tng qut, li pht biu chng trnh m bn mun gim st cc ngoi l th cha trong mt kha try. Nu mt ngoi l( v d nh mt li) xy ra bn trong kha try, n c nm i (dng throw). Ngoi l l bt, dng catch, v c x l. Nhng chi tit sau da trn mt s m t tng qut. As stated, any statement that throws an exception must have been executed from within a try block. (A function called from within a try block can also throw an exception.) Any exception must be caught by a catch statement that immediately follows the try statement that throws the exception. The general from of try and catch are shown here: Nh ni, bt k pht biu no m nm mt ngoi l phi c thc thi trong mt kha try. (Mt hm gi t trong mt kha try cng c th nm mt ngoi l.) Bt k mt ngoi l no phi b bt bi pht biu catch ngay sau li pht biu try m nm mt ngoi l. Dng chung ca try v catch c th hin di y. try{ // try block } catch (type1 arg) { // catch block } catch (type2 arg) { 653

// catch block } catch (type3 arg) { // catch block } . . . catch (typeN arg) { // catch block }

The try block must contain the portion of your program that you want to monitor for errors. This can be as specific as monitoring a few statements within one function or as all-encompassing as enclosing the main() function code within a try block (which effectively causes the entire program to be monitored). Mt kha try phi cha phn chng trnh m bn mun kim sot cc li. N c th c ch nh nh s kim sot vi pht biu bn trong mt hm hay nh hang ro bao bc hm main() bn trong mt kha try (m gy nh hng cho chng trnh c kim sot) When an exception is thrown, it is caught by its corresponding catch statement, which processes the exception. There can be more than one catch statement associated with a try. The catch statement that is used is determined by the type of the exception. That is, if the data type specified by a catch matches that of the exception, that catch statement is executed (and all others are bypassed). When an exception is caught, arg will receive its value. If you dont need access to the exception itself, specify only type in the catch clause-arg is optional. Any type of data can be caught, including classes that you create. In fact, class types are frequently used as exceptions. Khi mt ngoi l c nm i, n b bt li bi li pht biu catch tng ng m x l ngoi l. C th c nhiu pht biu catch kt hp vi mt try. Li pht biu catch dng quyt nh kiu ngoi l. Nu kiu i tng c ch r bi mt catch ng vi ngoi l 654

, th li pht biu catch c thi hnh ( v tt c ci khc s b b qua). Khi mt ngoi l b bt, arg s nhn c gi tr ca n. Nu bn khng cn truy xut vo n, th vic ch r kiu mnh catch l khng bt buc. Bt k kiu d liu no u c th b bt, bao gm cc lp m bn to ra. Thc t, kiu lp th c dng nh nhng ngoi l. The general form of the throw statement is shown here: (Dng chung ca cu lnh nm c trnh by y ) throw exception; throw must be executed either from within the try block proper or from any function that the code within the block calls (directly or indirectly), exception is the value thrown. Nm phi c thi hnh bn trong mt kha try ng cch hay t bt k hm no m m bn trong kha gi (trc tip hay gin tip), ngoi l l gi tr c nm i. If you throw an exception for which there is no applicable catch statement, an abnormal program termination might occur. If your compiler compiles with Standard C++, throwing an unhandled exception causes the standard library function terminate() to be invoked. By default, terminate() calls abort() to stop your program, but you can specify your own termination handler, if you like. You will need to refer to your compilers library reference for details. Nu bn nm i mt ngoi l m khng c mt pht biu catch no ph hp, s kt thc chng trnh bt thng c th xy ra. Nu trnh bin dch ca bn bin dch vi C++ chun, vic nm mt ngoi l v kim sot lm cho hm terminate() trong th vin hm chun s c triu hi. Mc nh, hm terminate() gi hm abort() kt thc chng trnh ca bn, nhng bn c th ch ra iu khin kt thc ca chnh mnh, nu mun. Bn s cn tham kho thm chi tit hng dn trong th vin trnh bin dch ca mnh.

Examples -- V d
Here is a simple example that shows the way C++ exception handling operates: y l v d n gin v cch m iu khin ngoi l C++ lm vic: // A simple exception handling example. #include <iostream>

655

using namespace std;

int main() { cout << start\n;

try { // start a try block cout << Inside try block\n; throw 10; // throw an error cout << This will not execute; } catch (int i) { // catch an error

cout << Caught One! Number is: ; cout << i << \n; cout << end; }

return 0; }

This program displays the following output: [ y l ni dung xut ra]

start Inside try block Caught One! Number is: 10

656

end Look carefully at this program. As you can see, there is a try block containing three statements and a catch(int i) statement that processes an integer exception. Within the try block, only two of the three statements will execute: the first cout statement and the throw. Once an exception has been thrown, control passes to the catch expression and the try block is terminated. That is, catch is not called. Rather, program execution is transferred to it. (The stack is automatically reset as needed to accomplish this.) Thus, the cout statement following the throw will never execute. Nhn tht k chng trnh. Bn c th thy l, c mt kha try cha ba cu lnh v mt pht biu catch(int i) m x l mt ngoi l s nguyn. Bn trong mt kha try, ch c hai trong ba dng pht biu c thi hnh: u tin l lnh cout v throw. Mt khi mt ngoi l c nm i, iu khin nhy n biu thc catch v kha try kt thc. V lc khng c catch no c gi th l chng trnh thc thi i qua. ( Ngn xp s t ng khi ng li hon tt n.) V vy, cu lnh cout tip theo throw s khng bao gi c thc thi. After the catch statement executes, program control continues with the statements following the catch. Often, however, a catch block will and with a call to exit(), abort(), or some other function that causes program termination because exception handling is frequently used to handle catastrophic errors. Sau mt pht biu catch thi hnh, iu khin chng trnh tip tc vi cc pht biu sau catch. Tuy nhin, thng l mt kha catch s v gi hm exit(), abort() hay mt hm no m gi c ch ngt bi iu khin ngai l thng dng cho cc li nguy him. As mentioned, the typed of the exception must match the type specified in a catch statement. For example, in the preceding example, if you change the type in the catch statement to double, the exception will not be caught and abnormal termination will occur. This change is shown here: Nh cp, kiu ngoi l phi gn vi kiu ca pht biu catch. V d, v d trc, nu bn thay i pht biu catch thnh kiu double, th ngoi l s khng c bt v kt thc bt thng s xy ra. S thay i nh sau: // This example will not work #include <iostream> using namespace std;

657

#include <iostream> using namespace std;

int main() { cout << start\n;

try { // start a try block cout << Inside try block\n; throw 10; // throw an error cout << This will not execute; } catch(double i){//wont work for an int exception cout << Caught One! Number is: ; cout << i << \n; } cout << end;

return 0; } This program procedures the following output because the integer exception will not be caught by a double catch statement. Chng trnh xut ra kt qu sau bi v mt ngoi l s nguyn (integer) khng th dng pht biu catch vi s dng double. start Inside try block

658

Abnormal program terminataion //Bo li kt thc bt thng

An exception can be thrown from a statement that is outside the try block as long as the statement is within a function that is called from within the try block. For example, this is a valid program: Mt ngoi l c th c nm t mt pht biu bn ngoi kha try nh li pht biu bn trong mt hm m c gi bn trong mt kha try. V d, chng trnh sau l hp l: /* Throwing an exception from a function outside the try block */ #include <iostream> using namespace std;

void Xtest( int test) { cout <<Inside Xtest, test is: << test << \n; if(test) throw test; }

int main() { cout << start\n;

try { // start a try block cout << Inside try block\n; Xtest(0);

659

Xtest(1); Xtest(2); } catch(int i){//catch an error cout << Caught One! Number is: ; cout << i << \n; }

cout << end;

return 0; }

This program produces the following output: [ Xut ra nh sau] start Inside try block Inside Xtest, test is: 0 Inside Xtest, test is: 1 Caught One! Number is: 1 end Va nm 1 l lp tc thot ra hm try bi khng c pht biu catch no tip sau -> Xtest(3) khng c thi hnh A try block can be localized to a function. When this is the case, each time the function is entered the exception handling relative to that function is reset. For example, examine this program: Mt kha try c th cc b ha trong mt hm. Khi l trng hp sau, mi ln hm i vo, iu khin ngoi l lin quan vi hm b khi ng li. V du nh sau: 660

#include <iostream> using namespace std;

// A try catch can be inside a function other than main() void Xhandler( int test) { try { if(test) throw test; } catch(int i) { cout << Caught One! Ex. #: << i << \n; } }

int main() { cout << start\n;

Xhandler(0); Xhandler(1); Xhandler(2); Xhandler(3);

cout << end;

661

return 0; } This program displays this input: start Caught One! Ex. #: 1 Caught One! Ex. #: 2 Caught One! Ex. #: 3 end

As you can see, three exceptions are thrown. After each exception, the function returns. When the function is called again, the exception handling is reset. Nh bn thy , ba ngoi l u c nm. Sau mi ngoi l hm tr v. Khi hm gi li, iu khin ngoi l b khi ng li. As stated earlier, you can have more than one catch associated with a try. In fact, it is common to do so. However, each catch must catch a different type of exception. For example, the following program catches both integers and strings: Nh ni t sm, bn c th c nhiu catch tng ng vi mt try. Trong thc t, lm vy l ph bin. Tuy th, mi catch phi bt cc ngoi l khc kiu. V d, chng trnh sau bt c s nguyn v chui: #include <iostream> using namespace std;

// Different types of exceptions can be caught. void Xhandler( int test) { try { if(test) throw test; 662

else throw Value is zero; } catch(int i) { cout << Caught One! Ex. #: << i << \n; } catch(char *str) { cout << Caught a string: } << str << \n;

int main() { cout << start\n;

Xhandler(0); Xhandler(1); Xhandler(2); Xhandler(3);

cout << end;

return 0; }

663

This program displays this input:[Xut ra nh sau] start Caught One! Ex. #: 1 Caught One! Ex. #: 2 Caught a string: Value is zero

Caught One! Ex. #: 3 end As you can see, each catch statement responds only to its own type. In general, catch expressions are checked in the order in which they occur in a program. Only a matching statement is executed. All other catch blocks are ignored. Bn thy y, mi pht biu catch ch p li vi cng li. Trong tng qut, biu thc catch th kim tra theo trt t xy ra. Ch pht biu tng ng c thi hnh. Tt c kha catch th b l qua.

Exercises - Bi tp
1. By far, the best way to understand how C++ exception handling works is to play with it. Enter, compile, and run the preceding example programs. Then experiment with them, altering pieces of them and observing the results. Cch hiu tt nht iu khin ngoi l C++ l chi vi n. ng li hy th g vo, bin dch v chy th l hiu thi. Sau nng cao ln lm vi cc kiu khc v xem kt qu v rt ra suy ngh. 2. What is wrong with this fragment? C g sai trong on sau int main() { throw 12.23; }

664

3.

What is wrong with this fragment? C g sai trong on sau

try { // . throw a; // } catch(char *) //. } 4. What will happen if an exception is thrown for which there is no corresponding catch statement? iu g s xy ra khi mt ngoi l c nm m khng c pht biu catch no p ng li {

11.4. MORE ABOUT EXCEPTION HANDLING - TRNH BY THM V IU KHIN NGOI L


There are several additional features and nuances to C++ exception handling that can make it easier and more convenient to use. Trong vic iu khin ngoi l C++ cn nhiu c tnh v nt tinh t gip cho vic s dng n c d dng v tin li hn. In some circumtances you will an exception handler to catch all exceptions instead of just a certain type. This is easy to accomplish. Simply use this form of catch: Trong mt s trng hp, bn s cn mt b iu khin ngoi l c th bt c tt c

665

cc kiu ca ngoi l thay v ch mt kiu no . iu ny c thc hin d dng bng cch s dng mnh catch theo dng nh sau: catch() { //process all exceptions } Here the ellipsis matches any type of data. y, du ba chm bt c tt c cc kiu. You can restrict the type of exceptions that a function can throw back to its caller. Put differently, you can control what type of exceptions a function can throw outside of itself. In fact, you can also prevent a function from throwing any exceptions whatsoever. To apply these restrictions, you must add a throw clause to the function definition. The general form of this is shown here: Khi mt hm c gi t bn trong khi block, bn c th hn ch kiu d liu no ca ngoi l m hm c th nm c. Thc vy, bn c th cn tr hm nn ra bt k kiu d liu no. thc hin cc hn ch, bn phi thm throw vo nh ngha hm. Dng tng qut nh sau: ret-type func-name(arg-list) throw(type-list) { // } Here only those data types contained in the comma-separated type-list may be thrown by the function. Throwing any other type of expression will cause abnormal program termination. If you dont want a function to be able to throw any exceptions, use an empty list. y, ch c cc ngoi l c kiu trong danh sch type_list (phn cch bng du phy) l c nm ra t hm. Ngc li, vic nm ra cc ngoi l thuc kiu khc l chng trnh kt thc bt thng. If your compiler complies with Standard C++, when a function attempts to throw a disallowed exception the standard library function unexpected() is call. By default, this causes the terminate() function to be called, which causes abnormal program 666

termination. However, you can specify your own termination handler, if you like. You will need to refer to your compilers documentation for directions on how this can be accomplished. Nu trnh bin dch ca bn tun theo chun tr nh ca tiu chun C++, th vic th nm ra mt ngoi l m khng c mnh catch no bt c s lm gi hm unexpected(). Theo mc nh, hm unexpected() s gi tip hm abort() gy kt thc chng trnh mt cch bt thng. Tuy nhin, bn hon ton c th nh li hm unexpected() gi n mt hm x l kt thc khc nu bn thch. Mun bit chi tit, bn hy kim tra li ti liu hng dn s dng th vin ca trnh bin dch. If you wish to rethrow an expression from within an exception handler, you can do so by simply calling throw, by itself, with no exception. This causes the current exception to be passed on to an outer try/catch sequence. Nu mun nm mt ln na mt ngoi l t bn trong b iu khin ngoi l, bn gi mt ln na t kha throw khng cn km theo gi tr ca ngoi l. Cch ny cho php ngoi l hin hnh c truyn n mt on try/catch khc.

EXAMPLES - V D
The following program illustrates catch(): Chng trnh sau y minh ha cch dng catch(): // This example catches all exceptions. #include <iostream> using namespace std;

void Xhandler(int test) { try { if(test==0) throw test; // throw int

667

if(test==1) throw 'a'; // throw char if(test==2) throw 123.23; // throw double } catch(...) { // catch all exceptions cout << "Caught One!\n"; } }

int main() { cout << "start\n";

Xhandler (0); Xhandler (1); Xhandler (2);

cout << "end";

return 0; }

668

This program displays the following output:

Chng trnh xut ra kt qu nh sau:

start Caught One! Caught One! Caught One! End

As you can see, all three throws were caught using the one catch statement. Trong chng trnh trn, bn c th thy l c ba ln nm ngoi l u c bt li c bi cng chung mt b iu khin ngoi l catch.

One very good use for catch() is as the last catch of a cluster of catches. In this capacity it provides a useful default or catch all statement. For example, this slightly different version of the relies upon catch() to catch all others:

Mt cch s dng catch() rt hay l a n vo cui cc b iu khin ngoi l catch khc trong chng trnh. B iu khin ngoi l catch() c kh nng bt c ngoi l thuc bt k kiu no. Trong v d sau, catch(...) c dng bt tt c cc ngoi l c kiu khc kiu nguyn, chng trnh v d ny l mt bn hiu chnh li t chng trnh v d trn:

// This example uses catch(...) as a default. #include <iostream> using namespace std;

669

void Xhandler (int test) { try { if(test==0) throw test; // throw int if(test==1) throw 'a'; // throw char if(test==2) throw 123.23; // throw double } catch (int i) { // catch an int exception cout << "Caught" << i << '\n'; } catch (...) { // catch all other exceptions cout << "Caught One!\n"; } }

int main()

670

{ cout << "start\n"; Xhandler (0); Xhandler (1); Xhandler (2);

cout << "end";

return 0; }

The output produced by this program is shown here:

Chng trnh xut ra kt qu nh sau:

start Caught 0 Caught One! Caught One! end

As this example suggests, using catch() as a default is a good way to catch all exceptions that you dont want to handle explicitly. Also, by catching all exceptions, you prevent an unhandled exception from causing an abnormal program termination.

671

V d trn cho thy catch() c dung to ra mt b x l li mc nh c th bt tt c cc ngoi l ngoi cc ngoi l m ta quan tm v x l mt cch tng minh. Hn na, cch s dng ny cn gip chng ta ngn nga c vic ngoi l nm ra m khng c b x l no bt c, lm dn n kt thc chng trnh mt cch bt thng.

The following program shows how to restrict the types of exceptions that can be thrown from a function:

Chng trnh tip theo minh ha cch hn ch cc kiu ca ngoi l c php nm ra t trong mt hm:

// Restricting function throw types. #include <iostream> using namespace std;

// This function can only throw ints, chars, and doubles. void Xhandler (int test) throw (int, char, double) { if(test==0) throw test; // throw int if(test==1) throw 'a'; // throw char if(test==2) throw 123.23; // throw double }

672

int main() { cout << "start\n";

try { Xhandler (0); // also, try passing 1 and 2 to Xhandler() } catch(int i) { cout << "Caught int\n"; } catch(char c) { cout << "Caught char\n"; } catch(double d) { cout << "Caught double\n"; }

cout << "end";

return 0; } 673

In this program, the function Xhandler() can throw only integer, character, and double exceptions. If it attempts to throw any other type off exception, an abnormal program termination will occur. (That is, unexpected() will be called.) To see an example of this, remove int from the list and retry the program.

Trong chng trnh trn, hm Xhandler() ch c th nm ra cc ngoi l thuc kiu nguyn, k t v double. Vic nm th cc ngoi l thuc kiu khc vi ba kiu ni trn s lm chng trnh kt thc bt thng. (Tc l gi hm unexpected()). th iu , hy xa t int ra khi danh sch v thc hin li chng trnh mt ln na.

It is important to understand that a function can only be restricted in what types of exceptions it throws back to the try block that called it. That is, a try block within a function can throw any type of exception so long as it is caught within that function. The restriction applies only when throwing an exception out of the function.

iu quan trng m bn cn bit l mt hm ch b hn ch kiu ngoi l c nm ra t khi try m hm ny c gi. Ni cch khc l, mt khi try t bn trong mt hm ch c th nm bt k ngoi l no m chng b bt bi cc b x l catch cng nm bn trong hm . Vic hn ch kiu ch c tc dng khi ngoi l c nm ra khi hm.

The following change to Xhandler() prevents if from throwing any exceptions:

Ni dung mi ca hm Xhandler() gip n ngn nga vic nm bt k ngoi l no

// This function can throw NO exceptions!

674

void Xhandler (int test) throw () { /* The following statements no longer work. Instead, they will cause an abnormal program termination.*/ if(test==0) throw test; if(test==1) throw 'a'; if(test==2) throw 123.23; } As you have learned, you can rethrow an exception. The most likely reason for doing so is to allow multiple handlers access to the exception. For example, perhaps one exception handler manages one aspect of an exception and a second handler copes with another. An exception can only be rethrown from within a catch block (or from any function called from within that block). When you rethrow an exception, it will not be recaught by the same catch statement. It will propagate to an outer catch statement. The following program illustrates rethrowing an exception. It rethrows a char * exception.

Nh bn hc, ta c th nm ln na mt ngoi l. Nguyn nhn thng gp ca vic nm li ny l cho php b x l truy cp nhiu ln mt ngoi l. Ly v d, c th l mt b x l thc hin mt vn no ca ngoi l, trong khi b x l th hai i ph vi mt vn khc ca ngoi l . Ngoi l ch c th b nm ra ln na t bn trong b iu khin ngoi l (hay gin tip t mt hm gi t b iu khin ). Khi ngoi l b nm ln na, n khng b bt li bi cng mt b iu khin ngoi l catch, thay vo l mt b iu khin ngoi l khc vng ngoi. Chng trnh sau y s minh ha cho vn trn, n nm li mt ngoi l kiu char*:

// Example of rethrowing- an exception. 675

#include <iostream> using namespace std;

void Xhandler() { try { throw "hello"; // throw a char * } catch (char *) { // catch a char * cout << "Caught char * inside Xhandler\n"; throw; // rethrow char * out of function } }

int main() { cout << "start\n";

try { Xhandler(); }

676

catch (char *) { cout << "Caught char * inside main\n"; }

cout << "end";

return 0; }

This program displays the following output:

Chng trnh xut ra kt qu nh sau:

start Caught char * inside Xhandler Caught char * inside main End

EXERCISES BI TP
Before continuing, compile and run all of the examples in this section. Be sure you understand why each program produces the output that it does.

Trc tin, bn hy bin dch v cho thc thi tt c cc v d trong mc ny. Hy m bo l bn hiu c hot ng ca chng trnh, v v sao chng in 677

ra cc kt xut nh vy.

What is wrong with this fragment?

C g khng ng trong on chng trnh sau?

try { //... throw 10; } catch (int *p) { //... }

Show one way to fix the preceding fragment.

Hy sa li on chng trnh trn cho ng.

What catch expression catches all types of exceptions?

Biu thc catch no bt c tt c cc kiu ca ngoi l?

Here is a skeleton for a function called divide(). 678

Di y l phn khung ca mt hm c tn divide().

double divide (double a, double b) { // add error handling return a/b; } This function returns the result of dividing a by b. Add error checking to this function using C++ exception handling. Specifically, prevent a divide-by-zero error. Demonstrate your solution in a program. Hm ny tr v gi tr ca php ton chia a cho b. Bn hy thm vo hm divide() chc nng kim tra li. c bit l li sai v chia cho gi tr 0. Trnh din gii php ca bn trong mt chng trnh.

11.5. HANDLING EXCEPTIONS THROWN - S DNG NHNG NGOI L C NM


In Chapter 4 you learned that the modern specification for the new operator states that it will throw an exception if an allocation request fails. Since in Chapter 4 exceptions had not yet been discussed, a description of how to handle that exception was deferred until later. Now the time has come to examine precisely what occurs when new fails. Trong Chng 4 bn hc c t hin i cho ton t new nhng trng thi thao tc m n s nm mt ngoi l nu mt yu cu nh v tht bi. T trong Chng 4 nhng ngoi l cha c c bn lun, s m t x l ngoi l nh th no c tr hon cho n sau ny. By gi thi gian nghin cu chnh xc ci m xy ra khi new tht bi. Before we begin, it is necessary to state that the material in this section describes the behavior of new as specified by Standard C++. As you should recall from Chapter 4, the precise action that new takes on failure has been changed several times since C++ was invented. Specifically, when C++ was 679

first invented, new returned null on failure. Later this was changed such that new caused an exception on failure. Also, the name of this exception has changed over time. Finally, it was decided that a new failure will generate an exception by default, but that a null pointer could be returned instead, as an option. Thus, new has been implemented differently, at different times, by compiler manufactures. Although all compilers will eventually implement new in compliance with Standard C++, not all currently do. If the code examples shown here do not work with your compiler, check your compilers documentation for details on how it implements new. Trc khi chng ta bt u, n tht s cn thit cho trng thi ca vt liu trong khu vc ny m t hnh vi ca new c ch r bi chun C++. Hy nh li chng 4, hot ng chnh xc ca new gp nhng tht bi c thay i vi ln t khi C++ c pht minh. c bit, khi C++ c pht minh ln u tin, new tr li gi tr null trn s tht bi. Sau y s thay i sao cho new gy ra mt ngoi l trn s tht bi. ng thi, tn ca ngoi l ny cng thay i theo thi gian. Cui cng, n c quyt nh rng new tht bi s pht sinh mt ngoi l mc nh, tr khi mt con tr null c th tr li thay vo , nh mt ty chn. Nh th, new c thi hnh khc nhau, vo nhng thi im khc nhau, bi s sn xut ca trnh bin dch. Mc d tt c cc trnh bin dch dn dn s thc thi new ng theo chun C++, khng phi nh mi th hin thi lm. Nu nhng on m v d y khng lm vic vi trnh bin dch ca bn, hy kim tra li ti liu th vin ca trnh bin dch ca bn nhng chi tit lm th no thi hnh new. In Standard C++, when an allocation request cannot be honored, new throws a bad_alloc exception. If you dont catch this exception, your program will be terminated. Although this behavior is fine for short sample programs, in real applications you must catch this exception and process it in some rational manner. To have access to this exception, you must include the header <new> in your program. Trong chun C++, khi mt yu cu nh v khng th c khen ngi, new nm mt ngoi l bad_alloc. Nu bn khng bt ly ngoi l ny, chng trnh ca bn s chm dt. Mc d hnh vi ny tt cho nhng chng trnh gn, trong nhng ng dng tht s bn phi bt ngoi l ny v x l n vo trong dng hp l. truy cp ngoi l ny, bn phi bao gm u mc <new> trong chng trnh ca bn. Note Originally this exception was called xalloc, and at the time of this writing many compilers still use the older name. However,

680

bad_alloc is the name specified by is the name that will be used in the future.

Standard C++, and it

Ch Trc y ngoi l ny c gi l xalloc, v lc cch vit ny c nhiu trnh bin dch s dng vi tn c hn. Ngy nay, bad_alloc l tn c ch r bi chun C++, v n l tn s c s dng trong tng lai. In Standard C++ it is also possible to have new return null instead of throwing an exception when an allocation failure occurs. This form of new is most useful when you are compiling older code with a modern C++ compiler. It is also valuable when you are replacing calls to malloc() with new. This form of new is shown here. Trong chun C++ n c th xy ra new tr li con tr null thay v vic nm mt ngoi l khi mt nh v tht bi xut hin. Mu ny ca new l hu ch nht khi bn l mt trnh bin dch m c hn mt trnh bin dch C++ hin i. N c thm gi tr khi bn thay th vic gi malloc() bng new. Dng ny ca new c cho thy y. p_var = new(nothrow) type; Here p_var is a pointer variable of type. The nothrow form of new works like the original version of new from years ago. Since it returns null on failure, it can be dropped into older code and you wont have to add exception handling. However, for new code, exceptions provide a better alternative. y p_var l mt bin con tr ca type. Nothrow hnh thnh t new nhng cng vic nh phin bn nguyn bn ca new cch y vi nm. T khi n tr li con tr null trn s tht bi, n c th ri vo trong m c v bn s khng phi thm x l ngoi l. Tuy nhin, cho m mi, nhng ngoi l cung cp nhng gii php tt hn.

EXAMPLES - V D
Here is an example of new that uses a try/catch block to monitor for an allocation failure.

y l mt v d ca new s dng khi try/catch theo di mt s nh v tht bi. 681

#include <iostream> #include <new> using namespace std;

int main() { int *p;

try { p = new int; // allocate memory for int } catch (bad_alloc xa) { cout << "Allocation failure.\n"; return 1; }

for (*p = 0; *p < 10; (*p)++) cout << *p << "";

delete p; // free the memory

return 0;

682

Here if an allocation failure occurs, it is caught by the catch statement. y nu mt s nh v tht bi xut hin, n s b bt bi s pht biu catch.

Since the previous program is unlikely to fail under any normal circumstance, the following program demonstrates news exceptionthrowing capability by forcing an allocation failure. It does this by allocating memory until it is exhausted. Mt khi chng trnh trc y khng chc tht bi di bt k hon cnh bnh thng no, chng trnh sau y trnh din s qung ngoi l ca new bi vic bt buc mt s nh v tht bi. N lm iu ny bi s cp pht b nh cho n khi n kit qu.

// Force an allocation failure. #include <iostream> #include <new> using namespace std;

int main() { double *p;

// this will eventually run out of memory do { 683

try { p = new double [100000]; } catch (bad_alloc xa) { cout << "Allocation failure.\n"; return 1; } cout << "Allocation OK.\n"; } while(p);

return 0; }

The following program shows how to use the new(nothrow) alternative. It reworks the preceding program and forces an allocation failure. Chng trnh sau y ch ra nh th no s dng gii php new(nothrow). N reworks c trc lp trnh v bt buc mt s nh v tht bi. // Demonstrate the new(nothrow) alternative. #include <iostream> #include <new> using namespace std;

684

int main() { double *p;

// this will eventually run out of memory do { p = new (nothrow) double [100000]; if(p) cout << "Allocation OK.\n"; else cout << "Allocation Error.\n"; } while(p);

return 0; } As this program demonstrates, when you use the nothrow approach, you must check the pointer returned by new after each allocation request. Nh chng trnh ny trnh din, khi bn s dng cch tip cn nothrow, bn phi kim tra con tr c tr li bi new sau mi yu cu nh v

EXERCISES - BI TP
Explain the difference between the behavior of new and new(nothrow) when an allocation failure occurs.

685

Gii thch s khc nhau gia hnh vi ca new v new(nothrow) khi mt s nh v tht bi xut hin. Given the following fragment, show two ways to convert it into modern C++-style code.

Cho on sau y, hy cho bit hai cch cho n chuyn i vo trong m C++ kiu hin i.

p = malloc(sizeof (int));

if(!p) { cout << "Allocation error.\n"; exit(1); }

SKILLS CHECK KIM TRA K NNG


Mastery Skills Check Kim tra k nng lnh hi At this point you should be able to perform the following exercises and answer the questions. n y bn c th lm cc bi tp v tr li cc cu hi sau: Create a generic function that returns the mode of an array of values. (The mode of a set is the value that occurs most often.)

686

Hy to ra mt hm chung tr v gi tr mode ca mt mng. (Gi tr mode l gi tr bt gp nhiu nht trong dy)

Create a generic function that returns the summation of an array of values.

Hy to hm tnh v tr v gi tr tng ca cc phn t trong mt mng.

Create a generic bubble sort (or use any other sorting algorithm you like).

Hy to hm chung thc hin gii thut sp xp ni bt (hay bt k gii thut sp xp no khc m bn thch).

Rework the stack class so that it can store pairs of different-type objects on the stack. Demonstrate your solution.

nh li lp stack n c lu tr mt cp gm hai i tng c kiu d liu khc nhau. Vit chng trnh biu din lp stack mi ny.

Show the general forms of try, catch, and throw. In your own words, describe their operation.

Trnh by dng s dng ca try, catch, v throw. Hy m t cch lm vic ca chng.

687

Again, rework the stack class so that stack over-and underflows are handled as exceptions.

Mt ln na, bn hy nh li lp stack n c kh nng x l cc li: chng b y, v chng b rng.

Check your compilers documentation. See whether it supports the terminate() and unexpected() functions. Generally, these functions can be configured to call any function you choose. If this is the case with your compiler, try creating your own set of customized termination functions that handle otherwise unhandled exceptions.

Hy kim tra cc ti liu hng dn lin quan n th vin ca trnh bin dch ca bn. tm xem n c h tr hai hm terminate() v unexpected() hay khng? Ni chung, chng ta c th nh li cho hai hm ny gi n bt k hm no bn mun. Nu c nh vy, bn hy to ra mt b cc hm kt thc chng trnh ca ring mnh x l cc li bt ng.

Thought question: Give a reason why having new generate an exception is a better approach than having new return null on failure.

Cu hi t duy: Hy gii thch ti sao new sinh ra mt ngoi l th tt hn cch tip cn new tr v mt con tr null trn s tht bi.

Cumulative Skills Check Kim tra k nng tng hp This section checks how well you have integrated material in this chapter with that from preceding chapters.

688

Phn ny kim tra xem kh nng ca bn kt hp chng ny vi chng trc nh th no. In Chapter 6, Section 6.7, Example 3.a safe array class was shown. On your own, convert it into a generic safe array.

v d 3, mc 6.7, trong chng 6 c minh ha lp ca dy c s lng hu hn cc phn t. Hy chuyn lp ny thnh mt lp chung.

In Chapter 1, overloaded versions of the abs() function were create. As a better solution, create a generic abs() function on your own that will return the absolute value of any numeric object.

Trong chng 1, c mt chng trnh qu ti hm abs(). thc hin mt cch gii tt hn, bn hy to ra mt hm chung abs(), n tnh v tr v gi tr tuyt i ca mt gi tr s thuc bt k kiu no.

CHAPTER 12 RUN-TIME TYPE IDENTIFICATION AND THE CASTING OPERATORS KIU THI GIAN THC V TON T P KHUN
Chapter Objectives
12.1 UNDERSTADING RUN-TIME TYPE IDENTIFICATION (RTTI) Tm Hiu v S Nhn Dng Thi Gian Thc 12.2 USING DYNAMIC_CAST

689

Cch Dng dynamic_cast 12.3 USING CONST_CAST, REINTERPRET_CAST, AND STATIC_CAST. Cch Dng const_cast, reinterpret_cast, v static_cast.

This chapter discusses two features that were recently added to the C++ language: run-time type identification (RTTI for short) and the new casting operators. RTTI allows you to identify the type of an object during the execution of your program. The casting operators give you safer, more controlled ways to cast. As you will see, one of the casting operators dynamic_cast, relatives directly to RTTI, so it makes sense to discuss these two subjects in the same chapter. Trong chng ny chng ta s tho lun 2 vn c thm vo trong ngn ng C++: s nhn ra kiu thi gian thc (RTTI n gin) v ton t khun mu. RTTI cho php bn xc nh kiu ca i tng ang thi hnh trong chng trnh ca bn. Ton t khun mu a cho bn mt cch an ton hn kim sot cc phng thc dng khun mu. Nh bn bit, mt trong nhng ton t khun mu l dynaminc_cast, c lin quan trc tip vi RTTI, v vy n l hai ch c tho lun trong chng ny.

Review Skills Check Kim tra K nng lnh hi


Before proceeding, you should be able to correctly answer the following questions and do the exercises. Trc khi tip tc, bn nn tr li chnh xc nhng cu hi v lm nhng bi tp sau: 1. What is a generic function and what is its general form? Hm chung l g? Dng tng qut ca n? 2. What is a generic class and what is its general form?

690

Lp chung l g? Dng tng qut ca lp chung? 3. Write a generic function called gexp() that returns the value of one of its arguments raised to the power of the other. Vit mt hm chung tn gexp() tr v gi tr l kt qu ca php tnh ly tha, trong i s ny l ly tha ca i s kia. 4. In Chapter 9, Section 9.7, Example 1, a coord class that holds integer coordinates was created and demonstrated in a program Create a generic version of the coord class that can hold coordinates of any type. Demonstrate your solution in a program. Trong v d 1 mc 9.7 trong chng 9 lp coord lu s cc ta l s nguyn c pht sinh v biu din trong mt chng trnh. Bn hy to ra lp chung coord c th lu ta ca mt kiu d liu bt k. Vit chng trnh minh ha cho lp trn. 5. Briefly explain how try, catch, and throw work together to provide C++ exception handling. Hy gii thch ngn gn v cch m try, catch, v throw c s dng x l ngoi l trong C++ 6. Can throw be used if execution has not passed through a try block? Ta c th dng throw c hay khng nu nh n khng c gi t bn trong mt khi try? 7. What purpose do terminate() and unexpected() serve? Cng dng ca hm terminate() v unexpected() l g? 8. What form of catch will handle all types of exceptions?

691

Dng s dng nh th no ca catch s bt c tt c cc kiu ca ngoi l?

13.1. UDERSTANDING RUN-TIME TYPE IDENTIFICATION (RTTI) - TM HIU V S NHN DNG THI GIAN THC
Run-time type information might be new to you because it is not found in nonpolymorphic languages such as C. In non-polymorhic languages, there is no need for run-time type information because the type of each objects is known at compile time (i.e. When the program is written). However, in polymorphic languages such as C++, there can be situations in which the type of an object is unknown at compile time because the precise nature of that object is not determined until the program is executed. As you know, C++ implements polymorphism through the use of class hierarchies, virtual functions, and base class pointers. In this approach, a base class pointer can be used to point to objects of the base class or to any object derived from that base. Thus, it is not always possible to know in advance what type of object will be pointed to by a base pointer at any given moment in time. This determination must be made at run time, using run-time type identification. Thng tin thi gian thc l iu mi m vi bn v n khng c trong ngn ng n gin nh C. Trong ngn ng khng hng i tng, thng tin thi gian thc l khng cn thit v nhng i tng chy theo thi gian ca trnh bin dch (v d khi chng trnh c to ra). Tuy nhin trong ngn ng a hnh nh C++, c trng hp kiu ca i tng khng c trnh bin dch nhn ra v s chnh xc t nhin ca n ch c bit n khi chng trnh c thc thi. Nh bn bit, C++ th hin tnh a hnh thng qua s phn cp lp, nhng chc nng thc t, v nhng lp con tr c s. Trong cch tip cn ny mt lp con tr c s c th tr ti i tng ca lp c s hay bt k lp no k tha t lp c s. Nh vy lun c trng hp c th khng bit c kiu i tng s c tr ti bi lp con tr c s vo khonh khc a ra, iu ny c th lm c trong khi chy bng cch dng nh ngha loi thi gian thc. To obtain an object's type, use typeid. You must include the header <typeinfo> in order to use typeid. The most common form of typeid is shown here: nh ngha mt i tng s dng typeid. Bn phi dng u mc

692

<typeinfo> s dng typeid. S nh ngha ca hnh thc typeid c dng nh sau: typeid(object) Here object is the object whose type you will be obtaining. typeid returns a reference to an object of type type_info that describes the type of object defined by object. The type_info class defines the following public members: y object l mt i tng bn to ra. typeid tr v dng ca i tng m type_info m t khi nh ngha loi ca i tng object. Lp type_info nh ngha hm thnh vin bool operator==(const type_info &ob); bool operator!=(const type_info &ob); bool before(const type_info &ob); const char*name(); The overloaded == and != provide for the comparison of types. The before() function returns true if the invoking object is before the object used as a parameter in collation order. This function is mostly for internal use only. Its return value has nothing to do with inheritance or class hierarchies). The name() function returns a pointer to the name of the type. Ton t chng == v != cung cp s so snh cc kiu. Hm before() tr v gi tr ng nu i tng khai bo trc c dng nh mt i s to i tng. Hm ny c s dng rng ri, n tr v gi tr ca lp k tha hay lp phn cp. Hm name() tr v con tr gi tn ca kiu While typeid will obtain the type of any object, its most important use is its application through a pointer of a polymorphic base class. In this case, it will automatically return the type of the actual object being pointed to, which can be a base class object or an object derived from that base. (Remember, a base class pointer can point to an object of the base class or of any class derived from that base). Thus, using typeid you can determine at run time the type of the object that is

693

being pointed to by a base class pointer. The same applies to references. When typeid is applied to a reference to an object of a polymorphic class, it will return the type of the object actually being referred to, which can be of a derived type. When typeid is applied to a non-polymorphic class, the base type of the pointer or reference is obtained. Trong khi typeid nh ngha kiu ca i tng, cch dng quan trng nht ca n c ng dng thng qua con tr a hnh ca lp c s. Trong trng hp ny, n t ng tr v gi tr thc ca i tng m con tr tr ti. (Nh rng mt lp con tr c s c th tr vo mt i tng ca lp c s hay bt k lp no c to ra t lp c s). iu ny c ngha s dng typeid bn c th xc nh thi gian thi hnh ca i tng ang c tr ti bi lp con tr c s. p dng tng t cho lp dn xut. Khi typeid c p dng cho mt i tng ca lp a hnh n s tr v kiu ca i tng thc s c dn xut, iu ny dng khai bo kiu. Khi typeid c dng trong 1 lp khng c tnh a hnh lp con tr c s hoc dn xut vn c s dng. There is a second form of typeid, one that takes a type name as its argument. This form is shown here: C hai hnh thc ca typeid, mt cch dng tn kiu nh mt quy tc. Hnh thc ny nh sau: typeid(type-name) The main use of this form of typeid is to obtain a type_info object that describes the specified type so that it can be used in a type comparison statement. Hnh thc chnh ca typeid c xut pht t mt i tng type_info sao cho m t ny c th dng nh mt s pht biu khi so snh kiu. Because typeid is commonly applied to a dereferenced pointer (i.e., one to which the *operator has been applied), a special exception has been created to handle the situation in which the pointer being dereferenced is null. In this case, typeid throws a bad_ typeid exception. Thng thng typeid c ng dng vo con tr ton t (v d n c p

694

dng trong ton t *operator), mt ngoi l c bit c to ra v nm gi trong hon cnh tng t th mt con tr ch ti null. Trong trng hp ny th typeid ch ti mt trng hp xu bad_typeid. Run-time type identification is not something that every program will use. However, when you are working with polymorphic types, it allows you to know what type of object is being operated upon in any given situation. Kiu nhn dng thi gian thc khng phi c s dng trong mi chng trnh. Tuy nhin khi chng ta lm vic vi cc kiu a hnh, n cho php chng ta bit c kiu ca i tng c x l trong nhng han cnh tng t.

EXAMPLES - V D
1. The following program demonstrates typeid. It obtains type information about one of C++'s built-in types, int. It then displays the types of objects pointed to by p, which is a pointer of type BaseClass. Chng trnh sau biu din kiu typeid. N dng kiu thng tin v mt trong nhng kiu xy dng sn ca C++, int. N hin th keu ca i tng c tr ti bi p, con tr ca lp c s BaseClass

// An example that uses typeid #include <iostream.h> #include <typeinfo> using namespace std;

class BaseClass { virtual void f() {

695

};

// make BaseClass polymorphic

// ... };

class Derived1:public BaseClass { //.... };

class Derived2:public BaseClass { //.... };

int main() { int i; BaseClass *p, baseob; Derived1 ob1; Derived2 ob2;

// First display type name of a built-in type. cout<<"Typeid of i is: "; cout<<typeid(i).name()<<endl;

696

// Demonstrate typeid with polymorphic types p = &baseob; cout<<"p is pointing to an object of type "; cout<<typeid(p).name()<<endl;

p = &ob1; cout<<"p is poiting to an object of type: "; cout<<typeid(p).name()<<endl;

p = &ob2; cout<<"p is pointing to an object of type: "; cout<<typeid(p).name()<<endl;

return 0; } The output produced by this program is shown here. Kt qu x l ca chng trnh nh sau: Typeid of i is int p is pointing to an object of type class Baseclass p is pointing to an object of type class Derived1 p is pointing to an object of type class Derived2 As explained, when typeid is applied to a base class pointer of a polymorphic type, the type of object pointed to will be determined at run time, as the output produced by the program shows. As an experiment, comment out the virtual function f() in BaseClass and observe the results.

697

Gii thch, khi typeid c p dng cho lp con tr c s ca kiu a hnh, kiu ca i tng c tr ti s c nh r trong khi chy, l kt qu xut ra ca chng trnh. Nh trong v d, ghi ch v hm o f() trong BaseClass quan st t kt qa. As explained, when typeid is applied to a reference to a polymorphic base class, the type returned is that of the actual object being referred to. The circumstance in which you will most often make use of this feature is when objects are passed to functions by reference. For example, in the following program, the function WhatType() declares a reference parameter to objects of type BaseClass. This means that WhatType() can be passed references to objects of type BaseClass or any class derived from BaseClass. When the typeid operator is applied to this parameter, it returns the actual type of the object being passed. Nh bit, khi typeid tham chiu ti mt lp c tnh a hnh, kiu tr v tht s ca i tng s c quy nh sn. Trong trng hp ny bn s s dng c tnh ny thng xuyn khi mt i tng tham chiu ti mt hm. V d, trong chng trnh sau, hm WhatType() s tham chiu ti i s ca i tng thuc lp BaseClass. iu ny c ngha WhatType() c th truy xut ti i s ca i tng thuc lp BaseClass hoc bt k lp dn xut no t BaseClass. Khi ton t typeid p dng cho hm ny, n tr v kiu thc s ca i tng c gi.

// Use a reference with typeid #include <iostream.h> #include <typeinfo> using namespace std;

class BaseClass { virtual void f() {}; // make BaseClass polymorphic

// ... }; 698

class Derived1:public BaseClass { //.... };

class Derived2:public BaseClass { //.... };

// Demonstrate typeid with a reference parameter void WhatType(BaseClass &ob) { cout<<"ob is referencing an object of type: "; cout<<typeid(ob).name()<<endl; } int main() { int i; BaseClass baseob; Derived1 ob1; Derived2 ob2;

WhatType(baseob);

699

WhatType(ob1); WhatType(ob2);

return 0; }

The output produced by this program is shown here. ob is referencing an object of type class BaseClass ob is referencing an object of type class Derived1 ob is referencing an object of type class Derived2 Although obtaining the type name of an object is useful in some circumstances, often all you need to know is whether the type of one object matches that of another. Since the typeid_info object returned by typeid overloads the == and != operators, this too is easy to accomplish. The following program demonstrates the use of these opeator Mc d s dng kiu tn i tng hu dng trong nhiu trng hp, thng thng bn cn hiu cch m kiu ca mt i tng kt ni vi nhng i tng khc. T khi i tng typeid_info tr v typeid bng ton t chng == v !=, iu ny tht d lm. Chng trnh sau m t cch dng ca nhng ton t . // Use a reference with typeid #include <iostream> #include <typeinfo> using namespace std;

class x { virtual void f()

700

{ } };

class y { virtual void f() { } };

int main() { x x1,x2; y y1;

if(typeid(x1) == typeid(x2)) cout<<" x1 and x2 are same types\n "; else cout<<" x1 and x2 are different types\n ";

if(typeid(x1) != typeid(y1)) cout<<" x1 and y1 are different types\n "; else cout<<" x1 and y1 are same types\n ";

701

The program displays the following output: Kt qu ca chng trnh hin th: x1 and x3 are same types x1 and y1 are different types

Althouth the preceding examples demonstrate the mechanics of using typeid, they don't show its full potential because the types in the preceding programs are knowable at compile time. In the following program, this is not the case. The program defines a simple class hierarchy that draws shapes on the screen. At the top of the hierarchy is the abstract class Shape. Four concreate sunclasses are created: Line, Square, Rectangle and NullShape. The function generator() generates an object and returns a pointer to it. The actual object created is determined randomly based upon the outcome of the random number generator rand(). (A function that produces objects is sometimes called an object factory). Inside main(), the shape of each object is displayed for all objects but NullShape objects, which have no shape. Since objects are generated randomly, there is no way to know in advance what type of object will be created next. Thus, the use of RTTI is required. Mc d v d trc gii thch v c bn cch dng ca typeid, nhng chng khng ch ra kh nng y bi kiu tr v trong chng trnh c th c bit trc khi bin dch. Chng trnh sau th khng nh th. Chng trnh s nh ngha s phn cp lp n gin v c gng th hin trn mn hnh. u ca s phn cp l lp c bn Shape. Bn lp c to ra: Line, Square, Rectangle v NullShape. Hm generator() c t ng to ra mt i tng v tr v con tr tr ti n. Gi tr thc ca i tng c to ngu nhin bng hm rand(). (mt hm khi to i tng i khi c gi l xng to i tng). Trong main(), hnh dng ca tt c cc i tng c hin th nhng NullShape th khng. Khi mt i tng c tao ngu nhin, khng th xc nh c kiu ca i tng tip theo c to ra l g. V th, hm RTTI l cn thit.

702

#include <iostream> #include <cstdlib> #include <typeinfo> using namespace std;

class Shape { public: virtual void example() = 0; };

class Rectangle: public Shape { public: void example() { cout<<"*****\n* } }; *\n* *\n*****\n";

class Triangle: public Shape { public: void example() {

703

cout<<"*\n* } };

*\n* *\n*****\n";

class Line: public Shape { public: void example() { cout<<"*****\n"; } };

class NullShape: public Shape { public: void example() { } };

// A factory for objects derived from Shape. Shape *generator() { switch (rand() %4)

704

{ case 0: return new Line; case 1: return new Rectangle; case 2: return new Triangle; case 3: return new NullShape; } return NULL; }

int main() { int i; Shape *p;

for(i=0; i<10; i++) { p = generator(); // get next object

cout<<typeid(*p).name()<<endl;

// draw object only if it is not a NullShape 705

if(typeid(*p) != typeid(NullShape)) p->example(); }

return 0; }

Sample output from the program is shown here. Kt qu ca chng trnh: Class Rectangle ***** * * ***** Class NullShape Class Triangle * * * * ****** Class Line ***** Class Rectangle ***** * 706 * * * *

* *****

Class Line ***** Class Triangle * * * * ****** *

The typeid operator can be applied to template classes. For example, consider the following program. It creates a hierarchy of template classes that store a value. The virtual function get_val() returns a value that is defined by each class. For class Num, this is the value of the number itself. For Square, it is the square of the number, and for Sqr_root, it is the square root of the number. Objects derived from Num are generated by the generator() function. The typeid operator is used to determine what type of object has been generated. Ton t typeid c th dng cho lp khun mu. V d, trong chng trnh ny, n to lp phn tng lu d liu. Hm o get_val() tr v gi tr c xc nh bi tng lp. Vi lp Num, hm tr v gi tr ca chnh n. Vi Square, n tr v s , v Sqr_root n tr v cn bc hai. i tng dn xut t Num c to bi hm generator(). Ton t typeid dng tr v kiu m i tng c to ra. // typeid can be used with templates #include <iostream> #include <typeinfo> #include <cmath> #include <cstdlib>

707

using namespace std;

template<class T> class Num { public: T x; Num(T i) { x = i; } virtual T get_val() { return x; }; };

template <class T> class Square:public Num<T> { public: Square (T i) : Num<T>(i) {} T get_val() { return x*x; }

708

}; template <class T> class Sqr_root : public Num<T> { public: Sqr_root (T i) : Num<T>(i) {} T get_val() { return sqrt((double)x); } };

// A factory for objcets derived from Num Num<double> *generator() { switch (rand()%2) { case 0: return new Square<double> (rand() %100); case 1: return new Sqr_root<double> (rand()%100); } return NULL; } 709

int main() { Num<double> ob1(10), *p1; Square<double> ob2(100.0); Sqr_root<double> ob3(999.2); int i;

cout<< typeid(ob1).name()<<endl; cout<< typeid(ob2).name()<<endl; cout<< typeid(ob3).name()<<endl;

if(typeid(ob2) == typeid(Square<double>)) cout<<"is Square<double>\n";

p1 = &ob2;

if(typeid(*p1) != typeid(ob1)) cout<<"Value is: "<<p1->get_val(); cout<<"\n\n";

cout<<"Now, generator some objects.\n"; for(i = 0;i < 10; i++) { p1 = generator(); // get next object

710

if(typeid(*p1) == typeid(Square<double>)) cout<<"Square object: "; if(typeid(*p1) == typeid(Sqr_root<double>)) cout<<"Sqr_root object: ";

cout<<"Value is: "<<p1->get_val(); cout<<endl; }

return 0; }

The output from the program is shown here. Kt qu ca chng trnh c ch ra sau y: class Num<double> class Squre<double> class Sqr_root<double> is Square<double> value is: 10000

Now, generate some objects Sqr_root object: Value is: 8.18535 Square object: Value is: 0

711

Sqr_root object: Value is: 4.89898 Square object: Value is: 3364 Square object: Value is: 4096 Sqr_root object: Value is: 6.7082 Sqr_root object: Value is: 5.19613 Sqr_root object: Value is: 9.53939 Sqr_root object: Value is: 6.48074 Sqr_root object: Value is: 6

EXERCISES - BI TP
1. Why is RTTI a necessary feature of C++? Ti sao RTTI cn thit trong C++? 2. Try the experiment described in Example 1. What output do you see? Hy lm bi trong v d 1. Kt qa bn thy l g? 3. Is the following fragment correct? Cu lnh sau ng hay sai? cout<<typeid(float).name(); 4. Given this fragment, show how to determine whether p is pointing to a D2 object. Trong on sau, ch ra lm th no p tr ti i tng D2 class B { virtual void f() {}

712

};

class D1: public B { void f() {} };

class D2: public B { void f() {} };

int main() { B *p; }

5.

Assuming the Num class from Example 5, is the following expression true or false? Vi lp Num trong v d 5, biu thc sau ng hay sai? typeid(Num<int>) == typeid(Num<double>)

6. On your own, exeriment with RTTI. Although its use might seem a bit esoteric in the context of simple, sample programs, it is a powerful construct that allows you to manage objects at run time. Vi s hiu bit ca bn, vi RTTI. Mc d dng nh n l mt ni dung 713

n gin, chng trnh n gin, n tht mnh m khi cho php bn qun l i tng khi chy.

1.2. USING DYNAMIC_CAST S DNG DYNAMIC_CAST


Although C++ still fully supports the traditional casting operator defined by C, C++ adds four new ones. They are dynamic_cast,const_cast, reiterpret_cast, and static_cast. We will examine dynamic_cast first because it relates to RTTI. The other casting operators are examined in the following section. Mc d C++ vn h tr y ton t khun trong C, n cn thm 4 ci mi. l dynamic_cast,const_cast, reiterpret_cast, v static_cast. Chng ta s tm hiu v dynamic_cast trc v n lin quan ti RTTI. Trong nhng chng sau ta s hc v cc ton t khun cn li.

The dynamic_cast operator performs a run-time cast that verifies the validety of a cast. If, at the time dynamic_cast is executed, the cast is invalid, the cast fails. The general form of dynamic_cast is shown here: Ton t dynamic_cast biu din khun mu thi gian thc c xc nh bi tnh khun mu. Nu, trong khi dynamic_cast c gi, nu khng c khun mu th li gi n tht bi. Dng chung ca dynamic_cast c ch ra di y: dynamic_cast<target-type>(expr) Here target-type specifies the target type of the cast and expr is the expression being cast into the new type. The target type must be a pointer or reference. Thus, dynamic_cast can be used to cast one type of pointer into another or one type of reference into another. y target_type ch ra kiu ch ca khun mu v expr l biu thc mu cho kiu mi. Kiu ch phi c tr ti hay c tham chiu. V th, dynamic_cast c th

714

dng lm kiu khun ca con tr tr ti i tng khc hay mt kiu tham chiu ti i tng khc. The purpose of dynamic_cast is is to perform casts on polymorphic types. For example, given the two polymorphic classes B and D, with D derived from B, a dynamic_cast can always cast a D* pointer into a B* pointer. This is because a base pointer can always point to a derived object. But a dynamic_cast can cast a B* pointer into an D* pointer only if the object being pointed to actually is a D object. In general, dynamic_cast will succeed if the pointter (or reference) being cast is a pointer (or reference) to either an object of the target type or an object derived from the target type. Otherwise, the cast will fail. If the cast fails, dynamic_cast evaluates to null if the cast involves pointers. If a dynamic_cast on reference types fails, a bad_castN exception is thrown. Mc ch ca dynamic_cast l lm khun mu cho kiu a hnh. V d, cho hai lp a hnh B v D, D k tha t B, dynamic_cast lun lm khun cho con tr D* sang con tr B*. L do v con tr lp c s c th tr ti con tr lp dn xut. Nhng dynamic_cast c th chuyn khun t B* sang D* ch khi no i tng ang c tr n thc s l i tng D. Thng thng, dynamic_cast s thnh cng nu con tr (hay tham chiu) ang lm khun cho con tr (hay tham chiu) ti mt i tng l kiu ch or i tng k tha t i tng c kiu ch. Ngai ra p khun tht bi. Nu tht bi, dynamic_cast tr v null nu khun ang l con tr. Nu dynamic_cast tham chiu tht bi, mt hm bad_cast s c gi. Here is a simple example, assume that Base is a polymorphic class and that Derived is derived from Base. Trong v d sau, Base l lp a hnh v Derived l lp k tha ca n. Base *bp, b_ob; Derived *dp, d_ob;

bp = & d_ob; // base pointer points to Derived object dp = dynamic_cast<Derived*> (dp); if(dp) cout<<"Cast Ok";

715

Here the cast from the base pointer bp to the derived pointer dp works because bp is actually pointing to a Derived object. Thus, this fragment displays Cast OK. But in the next fragment, the cast fails because bp is pointing to a Base object and it is illegal to cast a base object into a derived object. trn p khun t con tr c s bp sang con tr k tha dp lm c v bp thc ra ang tr ti lp k tha Derived. V th kt qu hin th l Cast Ok. Nhng trng hp k tip, p khun tht bi v bp ang tr n i tng lp c s Base v n ch c chp nhn p khun t i tng c s sang i tng k tha. bp = &b_ob;// base pointer points to Base object dp = dynamic_cast<Derived*> (bp); if(!dp) cout<< "Cast Fails"; Because the cast fails, this fragment displays Cast Fails. The dynamic_cast operator can sometimes be used instead of typeid in certain cases. For example, again assume that Base is a polymorphic base class for Derived. The following fragment will assign dp the address of the object pointed to bye bp if and only if the object is really a Derived object. Bi v p khun tht bi, kt qu hin Cast Fails. Ton t dynamic_cast thnh thong c th dng thay cho typeid trong vi trng hp. V d, tha nhn Base l lp a hnh c s ca lp Derived. on chng trnh sau s gn cho dp a ch ca i tng c tr n bi bp nu v ch nu i tng thc s l i tng ca lp Derived Base *bp; Derived *dp; //... if(typeid(*bp) == typeid(Derived)) dp = (Derived*) bp; In this case a C-style cast is used to actually perform the cast. This is safe because the if statement checks the legality of the cast using typeid before the cast actually occurs. However, a better way to accomplish this is to replace the typeid operators and if statement with this dynamic_cast: Trong trng hp ny mt cch p khun phong cch C c dng p khun. iu ny an ton v cu lnh if s kim tra tnh hp php ca vic p khun dng typeid trc khi vic p khun c thc thi. Tuy nhin, cch tt hn hon thnh iu 716

ny l thay th ton t typeid v cu lnh if bng dynamic_cast. dp = dynamic_cast<Derived *> (bp); Because dynamic_cast succeeds only if the object being cast is either already an object of the target type or an object derived from the target type, after this statement executes dp will contain either a null or a pointer to an object of type Derived. Since dynamic_cast succeeds only if the cast is legal, it can simplify the logic in certain situations. Bi v dynamic_cast ch ng nu i tng p khun l i tng kiu ch hoc i tng k tha t kiu ch, sau khi thc thi cu lnh dp s cha mt trong hai gi tr null hoc tr ti i tng kiu Derived. Khi dynamic_cast ng ch khi p khun hp quy tc, n rt n gin v logic

EXAMPLES - V D
The following program demonstrates dynamic_cast: Chng trnh ny gii thch v dynamic_cast: // Demonstrate dynamic_cast #include <iostream> using namespace std;

class Base { public: virtual void f() { cout<<"Inside Base \n"; } //.... };

717

class Derived: public Base { public: void f() { cout<<"Inside Derived\n"; } };

int main() { Base *bp, b_ob; Derived *dp, d_ob;

dp = dynamic_cast<Derived*> (&d_ob); if(dp) { cout<<"Cast from Derived * to Derived * Ok \n"; dp->f(); } else cout<<" Error\n";

cout<<endl; 718

bp = dynamic_cast<Base*> (&d_ob); if(bp) { cout<<"Cast from Derived * to Derived * Ok \n"; bp->f(); }

cout<<endl;

bp = dynamic_cast<Base*> (&b_ob); if(bp) { cout<<"Cast from Derived* to Derived* Ok\n; bp->f(); } else cout<<"Error\n";

cout<<endl;

dp = dynamic_cast<Derived*> (&b_ob); if(dp) cout<<"Error\n"; else

719

cout<<"Cast from Base * to Derived * not Ok\n";

cout<<endl;

bp = &d_ob;

// bp points to Derived object

dp = dynamic_cast<Derived*> (bp); if(dp) { cout<<"Casting bp to a Derived *Ok\n:"; cout<<"because bp is really pointing\n"; cout<<"to a Derived object\n"; dp->f(); } else cout<<"Error\n";

cout<<endl;

bp = &b_ob;//bp points to Base object dp = dynamic_cast<Derived*>(bp); if(dp) cout<<"Error\n"; else { 720

cout<<"Now casting bp to a Derived *\n"; cout<<"is not Ok because bp is really\n"; cout<<"pointing to a Base object\n"; } cout<<endl; dp = &d_ob; // dp points to Derived object bp = dynamic_cast<Base*> (dp); if(dp) { cout<<"Casting dp to a Base * is Ok\n"; bp->f(); } else cout<<"Error\n";

return 0; }

The program produces the following output. Kt qu chy chng trnh: Cast from Derived *to Derived * Ok Inside Derived

Cast from Base * to Base * Ok Inside Base 721

Cast from Base * to Derived * not Ok

Casting bp toa Derived *Ok because bp is really pointing to a Derived object Inside Derived

Now casting bp to a Derived * is not Ok because bp is really pointing to a Base object;

Casting dp to a Base * is Ok Inside Derived.

The following example illustrates how a dynamic_cast can be used to replace typeid V d sau ch ra cch m dynamic_cast c th b thay th bi typeid

// Use dynamic_cast to replace typeid #include <iostream> #include <typeinfo> using namespace std;

class Base {

722

public: virtual void f() {} };

class Derived :public Base { public: void derivedOnly() { cout<<"Is a Derived Object \n"; } };

int main() { Base *bp, b_ob; Derived *dp, d_ob;

//********************** // Use typeid //********************** bp = &b_ob; if(typeid(*bp) == typeid(Derived)) { dp = (Derived *) bp;

723

dp->derivedOnly(); } else cout<<"Cast from Base to Derived failed \n"; bp = &d_ob; if(typeid(*bp) == typeid(Derived)) { dp = (Derived *) bp; dp->derivedOnly(); } else cout<<"Error, cast should work! \n"; //*********************** // Use dynamic_cast //************************ bp = &b_ob; dp = dynamic_cast<Derived*> (bp); if(dp) dp->derivedOnly(); else cout<<"Cast from Base to Derived failed\n";

bp = &d_ob; dp = dynamic_cast<Derived*> (bp); if(dp) 724

dp->derivedOnly(); else cout<<"Error, cast should work!\n";

return 0; } As you can see, the use of dynamic_cast simplifies the logic required to cast a base pointer into a derived pointer. The output from the program is shown here. Nh bn thy, dng dynamic_cast th n gin v mt logic hn chuyn khun mu t con tr c s sang con tr dn xut. Kt qu chy chng trnh c ch ra di y: Cast from Base to Derived failed. Is a Derived Object. Cast from Base to Derived failed. Is a Derived Object. The dynamic_cast operator can also be used with template classes. For example, the following program reworks the template class from Example 5 in the preceding section so that it uses dynamic_cast to determine the type of object returned by the generator() function. Ton t dynamic_cast c th dng vi lp chung. V d, chng trnh sau lm vic vi lp chung t v d 5 trong phn x l m dng dynamic_cast chuyn kiu ca i tng tr v bi hm generator().

EXERCISES - BI TP
1. In your own words, explain the purpose of dynamic_cast. Vi hiu bit ca mnh, hy ch ra mc ch dng dynamic_cast. 725

2. Given the following fragment and using dynamic_cast, show how you can assign p a pointer to some object ob if and only if ob is a D2 object. C on chng trnh sau dng dynamic_cast, ch cho bn cch chuyn con tr p thnh i tng ob nu v ch nu ob l i tng D2 class B { Virtual void f() {} }; class D1:public B { void f() {} };

class D2:public B { void f() {} }; B *p;

3. Convert the main() function in Section 12.1, Exercise 4, so that it uses dynamic_cast rather than typeid to prevent a NullShape object from being displayed. Chuyn hm main() trong phn 12.1 bi tp 4, s dng dynamic_cast thay th cho typeid khng cho i tng NullShape hin th Using the Num class hierarchy from Example 3 in this section , will the following work?

726

Dng lp Num trong bi tp 3 ca phn ny, chng trnh sau c hot ng khng? Num<int> *Bp; Square<double> *Dp; //... Dp = dynamic_cast <Num<int>> (Bp);

1.3. USING CONST_CAST, REINTERPRET_CAST, AND STATIC_CAST - CCH DNG CONST_CAST, REINTEPRET_CAST V STATIC_CAST
Although dynamic_cast is the most important of the new casting operators, the other three also valuable to the programer. Their general forms are shown here: Mc d ton t dynamic_cast l quan trng nht ca ton t p khun, nhng ton t khc cng rt hu ch cho lp trnh vin. Dng chung ca chng c trnh by nh sau: const_cast<target-type>(expr) reinterpret_cast<target-type>(expr) static_cast<target-type>(expr) Here target-type specifies the target type of the cast and expr is the expression being cast into the new type. In general, these casting operators provide a safer, more explicit means of performing certain type conversions than that provided by the C-style cast. y target-type ch nh kiu ch ca p khun v expr biu th khun mu sang kiu mi. Thng thng, ba ton t cn li a ra cch an ton, r rng hn v cch chuyn i khung mu c cung cp trong phong cch lp trnh C

727

The const_cast operator is used to explicitly override const and or volatile in a cast. The target type must be the same as the source type except for the alteration of its const or volatile attributes. The most common use of const_cast is to remove const-ness. Ton t const_cast dng ghi chng const hoc volatile trong khun mu. Kiu ch phi ging nh kiu ngun cho s thay i thuc tnh t const hoc volatile. Cch dng ca const_cast l di chuyn const-ness The static_cast operator performs a non-polymorphic cast. For example, it can be used to cast a base class pointer into a derived class pointer. It can also be used for any standard conversion. No run-time checks are performed. Ton t static_cast dng cho khun mu khng a hnh. V d, n dng p khun t lp con tr c s sang con tr lp k tha. N cng dng lm dng chuyn i chun. Khng c s kim tra thi gian no c thc hin. The reinterpret_cast operator changes one pointer type into another, fundamentally different, pointer type. It can also change a pointer into an integer and an integer into a pointer. A reinterpret_cast should be used for casting inherently incompatible pointer types. Ton t reinterpret_cast chuyn mt kiu con tr sang kiu khc, kiu khc c bn, kiu con tr. N cng chuyn con tr sang s nguyn v ngc li. C th dng p khun k tha kiu con tr. Only const_cast can cast away const-ness. That is, neither dynamic_cast, static_cast, nor reinterpret_cast can alter the const-ness of an object. Ch c const_cast c th p khun const-ness. iu ny c ngha khng ci no trong dynamic_cast, static_cast, hoc reinterpret_cast c th lm c.

EXAMPLES - V D
The following program demonstrates the use of reinterpret_cast. Chng trnh m t cch dng reinterpret_cast.

728

// An example that uses reinterpret_cast #include <iostream> using namespace std;

int main() { int i; char *p = "This is a string";

i = reinterpret_cast<int> (p); // cast pointer to integer

cout<<i;

return 0; }

Here reinterpret_cast converts the pointer p into an integer. This conversion represents a fundamental type change and is a good use of reinterpret_cast. y reinterpret_cast chuyn t con tr p thnh s nguyn. S chuyn i ny thay i kiu v l cch dng hay ca reinterpret_cast The following program demonstrates const_cast. Chng trnh m t const_cast. // Demonstrate const_cast #include <iostream>

729

using namespace std;

void f(const int*p) { int *v;

// cast away const-ness v = const_cast<int*>(p);

*v=100; // now, modify object through v }

int main() { int x = 99;

cout<<"x before call: "<<x<<endl; f(&x); cout<<"x after call: "<<x<<endl;

return 0; } The output produced by this program is shown here. Kt qu x l chng trnh: x befoer call: 99

730

x after call: 100 As you can see, x was modified by f() even though the parameter to f() was spwecified as a const pointer. Nh bn thy, x b thay i bi f() d cho i s ca f() l con tr hng It must be be stressed that the use of const_cast to cast way const-nest is a pontentially dangerous feature. Use it with care. C th sai khi dng const_cast p khun. Hy cn thn khi dng n. The static_cast operator is essentially a substitute for the original cast operator. It simply performs a non-polymorphic cast. For example, the following casts a float into an int. Ton t static_cast v c bn l thay cho ton t p khun gc. N d thc hin p khun khng a hnh. V d sau chuyn t float sang int // Use static_cast #include <iostream> using namespace std;

int main() { int i; float f;

f = 199.10;

i = static_cast<int> (f);

cout<<i; 731

return 0; }

EXERCISES - BI TP
1. Explain the rationale for const_cast,reinterpret_cast, and static_cast. Ch ra nguyn nhn dng const_cast,reinterpret_cast, v static_cast. 2. The following program contains an error. Show how to fix it using a const_cast. Chng trnh sau c li. Ch li v sa n dng const_cast #include<iostream> using namespace std; void f (const double &i) { i = 100; // Error -> fix using const_cast }

int main() { double x = 98.6;

cout<<x<<endl; f(x); cout<<x<<endl;

732

return 0; } 3. Explain why const_cast should normally be resserved for special cases. Gii thch v const_cast t trng hp n gin n phc tp

SKILLS CHECK (KIM TRA K NNG) Mastery skills check At this point you should be able to perform the following exercises and answer the questions. n lc ny bn c th thc hnh v tr li cc cu hi sau: 1. Describe the operation of typeid. M t ton t typeid 2. What header must you include in order to use typeid? iu ch o cn nh khi dng typeid 3. In addition to the standard cast, C++ defines four casting operators. What are they and what are they for? Trong php cng thng thng, C++ nh ngha bn ton t chuyn kiu. Chng l ton t no v dng lm g? 4. Complete the following partial program so that it reports which type of object has been selected by the user. Hon chnh chng trnh sau, cho bit kiu i tng c chn bi ngi dng #include <iostream> #include <typeinfo>

733

using namespace std;

class A { virtual void f() {} };

class B:public A { };

class C: public A { };

int main() { A *p, a_ob; B b_ob; C c_ob; int i;

cout<<"Enter 0 for A objects: "; cout<<"1 for B objects: "; cout<<"2 for C objects: ";

734

cin>>i;

if(i==1) p=&b_ob; else if(i==2) p = &c_ob; else p=&a_ob; // report type of object selected by user return 0; } 5. Explain how dynamic_cast can somtimes be an alternative to typeid. Khi no dynamic_cast c th thay cho typeid What type of object is obtained by the typeid operator? Kiu gi tr tr v ca ton t typeid c th l g?

Cumulative Skills check This section checks how well you have integrated material in this chapter with that from the preceding chapters. Phn ny kim tra bn hiu n u v nhng iu trnh by trong chng ny 1. Rework the program in Section 12.1, Example 4 so that it uses exception handling to watch for an allocation failure within the generator() function.

735

Lm li chng trnh Chng 12.1 v d 4 tm cch gi gi tr quan st a ch m khng dng hm generator() 2. Change the generator() function from Question 1 so that it uses the nothrow version of new. Be sure to check for errors Thay hm generator() trong cu 1 bng cch dng nothrow ca new. Chc chn l khng gy li. 3. Special Challenge: on your own, create a class hierarchy that has at its top an abstract class called DataStruct. Create two concrete subclasses. Have one implement a stack, the other a queue. Create a function called DataStructFactory() that has this prototype: Bi c bit: bng hiu bit ca mnh, to lp u tin bng cch gi hm DataStruct. To 2 lp khng gi tr tr v. C th dng ti stack hoc hng i. To hm DataStructFactory() c chun sau: DataStruct *DataStructFactory(char what): Have DataStructFactory() create a stack if what is s and a queue if what is q. Return a pointer to the object created. Demonstrate that your factory function works. Vi DataStructFactory() to mt stack nu what l s v hng i nu l q. Tr v con tr ca i tng to ra. Hy lm cho xng to ca bn lm vic. ______________________________________________________________ _____________

736

CHAPTER 13 NAMESPACES, CONVERSION FUNCTIONS, AND MISCELLANEOUS TOPICS NAMESPACES, CC HM CHUYN I V CC CH KHC NHAU
CHAPTER OBJECTIVES 13.1. NAMESPACES 13.2. CREATING A CONVERSION FUNCTION 13.3. STATIC CLASS MEMBERS 13.4. CONST MEMBER FUNCTIONS AND MUTABLE 13.5. A FINAL LOOK AT CONSTRUCTORS 13.6. USING LINKAGE SPECIFIERS AND THE ASM KEYWORD 13.7. ARRAY BASED I/O

This chapter discusses namespace, conversion functions, static and const class members, and other specialized features of C++. Chng ny tho lun v namespace, cc hm chuyn i v cc hng v tnh thnh vin ca mt lp, v cc c im c bit ca C++.

REVIEW SKILLS CHECK N TP K NNG: Before proceeding, you should be able to correctly answer the following questions and do the exercises. Trc khi bt u, bn c th sa li cu tr li cc cu hi sau v lm cc bi tp. What are the casting operators and what do they do? 1. Th no l ton t sp xp v chng hot ng nh th no? 737

What is type_info? 2. Th no l type_info What operator determines the type of an object? 3. Ton t no xc nh loi ca mt i tng? Given this fragment, show how to determine whether p points to an object of Base or an object of Derived. 4. Cho cc on chng trnh sau, lm th no xc nh con tr p cho mt i tng Base hoc mt i tng ca Derived. class Base { virtual void f() {} };

class Derived : public Base {};

int main() { Bae *p, b_ob; Derived d_ob; // }

A dynamic_cast succeeds only if the object being cast is a pointer to either an object of the targettype or an object _________ from the target type. ( Fill in the blank ) 5. Mt dynaic_cast thnh cng ch mt i tng c sp xp l mt con tr i tng ca loi mc tiu hoc mt i tng __________ t loi mc tiu. ( in 738

vo ch trng. ) Can a dynamic_cast cast away const-ness? 6. dynamic_cast c th qung const-ness khng?

13.1.

NAMESPACES

Namespaces were briefly introduced in Chapter 1. Now it is time to look at them in detail. Namespace are a relatively recent addition to C++. Their purpose is to localize the names of identifiers to avoid name collisions. In the C++ programming environment, there has been an explosion of variable, function, and class names. Prior to the invention of namespaces, all of these names competed for slots in the global namespace, and many conflicts arose. For example, if your program defined a function called toupper( ), it could ( depending upon its parameter list ) override the standard library function toupper( ) because both names would be stored in the global namespace. Name collisions were compounded when two or more third-party libraries were used by the same program. In this case, it was possible - even likely that a name defined by one library would conflict with the same name defined by another library. Namespace c gii thiu ngn gn trong chng 1. By gi l lc xem xt chng mt cch chi tit. Namespace l phn thm vo ca C++. Mc ch ca n l xc nh tn trnh s trng tn. Trong mi trng chng trnh C++, c s bng n khc nhau, hm v tn lp. u tin cho s pht minh ca namespace, tt c cc tn cnh tranh cho ch trng trong ton b namespace, v rt nhiu s xung t xut hin. V d, nu chng trnh ca bn xc nh mt hm c gi toupper( ), n c th ( ph thuc danh sch tham s ca n) vt qua hm toupper( ) th vin chun, bi v c hai tn s c cha trong namespace. S va chm tn c kt hp khi hai hay nhiu th vin th ba c s dng bi cng chng trnh. Trong trng hp ny, n c th thm ch, mt ci tn c xc nh bi mt th vin c th xung t vi cng tn c xc nh bi th vin khc. The creation of the namespace keyword was a response to these problems. Because it localizes the visibility of names declared within it, a namespace allows the same name to be used in different contexts without giving rise to conflicts. Perhaps the most noticeable beneficiary of namespace is the C++ standard library. In early versions of C++, the entire C++ library was defined within the global namespace ( which was, of course, the only namespace ). Now, however, the C++ library is defined within its own namespace, std, which reduces the chance of name collisions. You can also create your own namespaces within your program to localize the visibility of any names that you think might cause conflicts. This is especially important if you are creating class or function libraries. 739

S to ca t kha namespace l mt trch nhim cho cc vn ny. Bi v n nh v cc tn c khai bo, mt namespace cho php cng tn c s dng trong cc ng cnh khc nhau m khng dn n xung t. C th cc thng bo tha k ca namespace l th vin chun C++. Trong phin bn trc ca C++, ton b th vin C++ c xc nh bn trong ton namespace ( d nhin ch namespace ). By gi, tuy nhin, th vin C++ c xc nh trong namespace, std, gim c hi ca s xung t tn. Bn cng c th to mt namespace trong chng trnh ca bn xc nh cc tn t m bn ngh c th xy ra xung t. y l iu quan trng c bit nu bn tao mt lp hoc mt th vin hm. The namespace keyword allows you to partition the global namespace by creating a declarative region. In essence, a namespace defines a scope. The general form of namespace is shown here: T kha namespace cho php bn chia ton b namespace bng cch to mt vng khai bo. Mt namespace cn thit xc nh phm vi hot ng. nh dng ca namespace l: namespace name { // declarations } Anything defined within a namespace statement is within the scope of that namespace. Here is an example of a namespace: Bt c iu g c xc nh bn trong namespace l bn trong phm vi hot ng ca namespace. y l v d ca namespace namespace MyNameSpace { int ix k; void myfunc(int j) { cout << j; } class myclass

740

{ public: void seti(int x) { i = x; } int geti() { return i; } }; }

Here i,k, myfunc( ), and the class myclass are part of the scopedefined by the MyNameSpace namespace. i, k, myfunc( ), v lp myclass l mt phn phm vi hot ng c xc nh bi namespace MyNameSpace. Identifiers declared within a namespace can be referred to directly within that namespace. For example, in MyNameSpace the return i statement uses i directly. However, since namespace defines a scope, you need to use the scope resolution operator to refer to objects declared within a namespace from out side that namespace. For example, to assign the value 10 to i from code ouside MyNameSpace, you must use this statement: Nhng phn xc nh c khai bo bn trong namespace c th truy cp trc tip bn trong namespace. V d, trong MyNameSpace tr v i, s dng i trc tip. Tuy nhin, t khi namespace c xc nh mt phm vi hot ng, bn cn s dng ton t tm vc truy cp n i tng c khai bo bn trong mt namespace t bn ngoi namespace. V d, gn gi tri 10 chi i t code bn ngoi MyNameSpace, bn phi lm nh sau: MyNameSpace::i = 10; 741

Or, to declare an object of type myclass from outside MyNameSpace, you use a statement like this: Hoc khai bo mt kiu i tng myclass t bn ngoi MyNameSpace, bn s dng cu lnh nh sau: MyNameSpace::myclass ob; In general, to access a member of a namespace from outside its namespace, precede the members name with the name of the namespace followed by the scope resolution operator. Tm li, truy cp mt thnh vin ca namespace t bn ngoi, cc tn trc ca thnh vin vi tn ca namespace sau bi ton t tm vc As you can imagine, if your program includes frequent references to the members of a namespace, the need to specify the namespace and the scope resolution operator each time you need to refer to one quickly becomes a tedious chore. The using statement was invented to alleviate this problem. The using statement has these two general forms: Nh bn on, nu chng trnh ca bn bao gm cc lnh truy xut thng xuyn n cc thnh vin ca mt namespace, cn thit ch r namespace v ton t tm vc mi ln bn cn truy cp n chng mt cch nhanh chng tr thnh mt cng vic bun t, chn ngt. Cu lnh using c lp ra lm gim nh bt vn ny. Cu lnh using c hai cch s dng: using namespace name; using name::member; In the first form , name specifies the name of the namespace you want to access. When you use this form, all of the members defined within the specified namespace are brought into the current namespace and can be used without qualification. If you use the second from, only a specific member of the namespace is made visible. For example, assuming MyNameSpace as shown above, the following busing statements and assignments are valid: Trong cch th nht, name ch r tn ca namespace m bn cn truy cp. Khi bn s dng cch ny, tt c cc thnh vin c nh ngha bn trong namespace c ch r mang n namespace hin hnh v c th c s dng m khng cn iu kin. Nu bn s dng cch th hai, ch c thnh vin c ch nh ca namespace l thy c. V d, MyNameSpace c ch ra bn trn, cu lnh theo sau v s quy cho l c gi tr.

742

using MyNameSpace::k; k = 10;

// only k is made visible

// OK because k is visible

using namespace MyNameSpace; // all members are visible i = 10; visible // OK because all members of MyNameSpace are

There can be more than one namespace declaration of the same name. This allows a namespace to be split over several files or even separated within the same file. Consider the following example: C th c nhiu hn mt namespace c khai bo cng tn. iu ny cho php mt namespace c chia thnh nhiu tp tin hoc thm ch ring r bn trong cng tp tin. Xem v d sau: namespace NS { int i; }

namespace NS { int j; } Here NS is split into two pieces. However, the contents of each piece are still within the same namespace. NS. NS c chia lm hai. Tuy nhin, ni dung trong mi phn vn cn bn trong cng namespace. NS. 743

A namespace must be declared outside of all other scopes, with one exception: a namespace can be nested within another. This means that you cannot declare namespaces that are localized to a function, for example. Mt namespace phi c khai bo bn ngoi ca tm vc khc, vi mt ngoi l: mt namespace c th c cha bn trong mt ci khc. iu ny c ngha bn khng th khai bo cc namespace c nh v cho mt hm, v du: There is a special type of namespace called an unnamed namespace. that allows you to create identifiers that are unique within a file. It has this general form: C mt kiu c bit c gi l unnamed namespace. N cho php bn to cc nh ngha duy nht bn trong mt file. N c nh dng nh sau: namespace { //declartion } Unnamed namespaces allow you to establish unique indentifiers that are known only within the scope of a single file. That is, within the file that contains the unnamed namespace, the members of that namespace can be used directly, without qualification. But outside the file, the identifiers are unknown. Unnamed namespace cho php bn to mt nh ngha thnh lp duy nht m bn bit ch bn trong tm vc mt file n. l, bn trong mt file cha unnamed namespace, cc thnh vin ca namespace c th s dng trc tip, m khng cn iu kin. Nhng bn ngoi tp tin, nh ngha khng c s dng. You will not usually need to create namespaces for most small - to medium sized programs. However, if you will be creating libraries of reusable code or if you want to ensure the widest portability, consider wrapping your code within a namespace. Bn s khng cn thng xuyn to namespace cho cc chng trnh va v nh. Tuy nhin, nu bn mun to mt th vin cho cc on m s dng li hoc nu bn mun bo m rng, xem bao trm on m ca bn bn trong mt namespace.

EXAMPLE here is an example that illustrates the attributes of a namespace. 744

y l mt v d minh ha cho thuc tnh ca mt namespace // Namespace Demo #include <iostream> using namespace std;

// define a namespace namespace firstNS { class demo { int i; public: demo(int x) { i = x; } void seti(int x) { i = x; } int geti() { return i; } };

745

char str[] = Illustrating namespaces\n; int counter; }

// define another namespace namespace secondNS { int x, y; }

int main() { // use scope resolution firstNS::demo ob(10); /* Once ob has been declared, its member functions can be used without namespace qualification. */ cout << Value of ob is: << ob.geti(); cout << endl; ob.seti(99); cout << Value of ob is now: << ob.geti(); cout << endl;

// bring all of firstNS into current scope using namespace firstNS;

746

for(counter = 10; counter; counter--) cout << counter << \n; cout << endl;

// use secondNS namespace secondNS::x = 10; secondNS::y = 20;

cout << x, y: << secondNS::x; cout << << second::y << endl;

// bring another namespace into view using namespace secondNS; demo xob(x), yob(y);

cout << xob, yob: << xob.geti << , ; cout << yob.geti() << endl;

return 0; }

The output produced by this program is shown here. Value of ob is : 10 Value of ob is now : 99 Illustrating namespaces

747

10 9 8 7 6 5 4 3 2 1 x, y: 10, 20 xob, yob: 10, 20 The program illustrates one important point: using one namespace does not override another. When you bring a namespace into view, it simply adds its names to whatever other namespaces are currently in effect. Thus, by the end of this program the std, firstNS, and secondNS namespaces have been added to the global namespace. Chn trnh minh ha mt im quan trng: s dng mt namespace khng vt qua ci khc. Khi bn mang mt namespace vo xem, n thm cc tn vo bt c namespace no khc ang hot ng. V vy, cui chng trnh std, firstNS, v secondNS namespaces c thm vo ton b namespace. As mentioned, a namespace can be split between files or within a single file; its contents are additive. Consider this example: Nh cp, mt namespace c th c chia thnh cc file bn trong mt file n, n cha cc phn thm vo. Xem v d sau: / Namespace are additive #include <iostream> using namespace std;

namespace Demo { int a; // In Demo namespace }

int x; // this is in global namespace

namespace Demo {

748

int b; // this is in Demo namespace, too }

int main() { using namespace Demo;

a = b

= x = 100;

cout << a << << b << x;

return 0; } Here the Demo namespace contains both a and b, but not x. Namespace Demo cha c a v b, nhng khng cha x. As explained, Standard C++ defines its entire library in its own namespace, std. This is the reason that most of the programs in this book have included the following statement: Nh gii thch, chun C++ nh ngha ton b th vin bn trong namespace ca n, std. y l l do hu ht chng trnh trong cun sch ny u bao gm cu lnh sau: using namespace std; This causes the std namespace to be brought into the current namespace, which gives you direct access to the names of the functions and classes defined within the library without having to qualify each one with std::. y l nguyn nhn namespace std c mang vo namespace hin hnh, n cho bn truy cp trc tip n cc tn hm v lp c nh ngha bn trong th vin m khng cn iu kin cho mi ci vi std::

749

Of course, you can explicitly qualify each name with std:: if you like. For example, the following program does not bring the library into the global namespace. D nhin, bn c th t iu kin r rng mi tn trong std:: nu bn mun. V d, chng trnh sau khng mang th vin vo ton b namespace. // use explicit std:: qualification. #include <iostream>

int main() { double val;

std::cout << Enter a number: ;

std::cin >> val;

std::cout << this is your number: ; std::cout << val;

return 0; } Here cout and cin are both explicitly qualified by their namespace. That is, to write to standard output you must specify std::cout, and to read from standard input you must use std::cin. Cout v cin u c t iu kin bi namespace ca n. l, vit mt xut chun bn phi ch r std::cout, v c t nhp chun bn phi s dng std::cin. You might not want to bring the Standard C++ library into the global namespace if your program will be making only limited use of it. However, if 750

your program contains hundreds of references to library names, including std in the current namespace is far easier than qualifying each name individually. Bn khng mun mang th vin chun C++ vo ton b namespace nu chng trnh bn cha hng trm lnh truy cp n tn th vin, bao gm std trong namespace hin hnh l sm hn nhiu so vi iu kin mi tn c chia ra. If you are using only a few names from the standard library, it might make more sense to specify a using statement for each individually. The advantage to this approach is that you can still use those names without an std:: qualification but you will not be bringing the entire standard library into the global namespace. Heres an example: Nu bn s dng nu ch mt s tn t th vin chun, n c th to nhiu ngha ch r mt cu lnh s dng cho mi phn. iu thun li cho vic tip cn l bn c th cn s dng nhng tn ny m khng cn iu kin std::qualification nhng bn s khng mang ton b th vin chun vo ton b namespace. y l mt s v d: // Bring only a few names into the global namespace. #include <iostream>

// gain access to cout and cin using std::cout; using std::cin;

int main() { double val;

cout << Enter a number;

cin >> val;

751

cout << This is your number: ; cout << val;

return 0; } Here cin and cout can be used directly, but he rest of the std namespace has not been brought into view. Cin v Cout c th c s dng trc tip, nhng phn cn li ca namespace std khng c mang vo xem. As explained, the original C++ library was defined in the global namespace. If you will be converting older C++ programs, you will need to either include a using namespace std statement or qualify each reference to a library member with std::. This is especially important if you are replacing old .h header files with the new-style headers. Remember, the old .h headers put their contents into the global namespace. The new-style headers put their contents into the std namespace. Nh gii thch, th vin gc C++ c nh ngha trong ton b namespace. Nu bn chuyn i chng trnh C++ c, bn s cn c cu lnh using namespace std hoc iu kin mi cu lnh truy cp vo mt th vin thnh vin vi std::. y l iu quan trng c bit nu bn thay .h u file vi tiu theo cch mi. Nh rng, tiu .h c t ni dung ca n vo ton b namespace. Cch tiu mi t ni dung ca n vo namespace std. In C, if you want to restrict the scope of a global name to the file in which it is declared, you declare that name as static. For example, consider the following two files that are part of the same program. Trong C, nu bn mun t chi tm vc ca mt tn chung ca mt file c khai bo, bn khai bo cc tn theo static. V d, xem hai file sau l mt phn ca cng chng trnh. File One static int conter; void f1( ) { Conter = 99; // OK File Two extern int counter; void f2( ) { counter = 10; // error

752

Because counter is defined in File One, it can be used in File One. In File Two, even though counter is specified as extern, it is still unavailable, and any attempt to use it results in an error. By preceding counter with static in File One, the programmer has restricted its scope to that file. Bi v counter c nh ngha trong file mt, n c th c s dng trong fiel mt. Trong file hai, thm ch counter c ch r nh extern, n khng c gi tr, v bt k s c gng no s dng n th kt qu u bo li. Lp trnh vin b t chi tm vc ca mt file, bi counter vi static trong file mt. Although the use of static global declarations is still allowed in C++, a better way to accomplish the same end is to use an unnamed namespace, as shown in this example: Mc d cch s dng static ton b khai bo l cn c php trong C++, mt cch tt hn hon thnh cng cch kt thc l s dng mt unnamed namespace, nh ch ra trong v d ny: File One namespace { int counter; } void f1( ) { counter = 99; // OK } } File Two extern int counter; void f2( ) { counter = 10; // error

Here counter is also restricted to File One. The use of the unnamed namespace rather than static is the method recommended by Standard C++. Counter ny cng b hn ch trong file mt. Cch s dng ca unnamed namespace hn static l phng php yu cu bi chun C++. 753

EXERCISES:
Convert the following program from Chapter 9 so that it does not use the using namespace std statement: Chuyn i chng trnh sau t chng 8 khng s dng using namespace std: // Convert spaces to d/ #include <iostream> #include <istream> using namespace std;

int main (int arge, char *argv[]) { if(argv != 3) { cout << Usage: CONVERT <input> return 1; } <output>\n;

ifstream.fin argv[1]; // open input file ofstream.fout argv[2]; // create putput file

if(!foul) { cout << Cannot open output file.\n; return 1; }

754

if(!fin) { cout << Cannot open input file.\n; return 1; }

char ch;

fin.unsetf(ios::skipws); // do not skip spaces while(!fin.eof()) { fin >> ch; if(ch == ) ch = !; if(!fin.eof()) fout << ch; }

fin.close(); fout.close();

return 0; }

755

Explain the operation of an unnamed namespace. Gii thch ton t unnamed namespace. Explain the difference between the two forms of using. Gii thch s khc nhau ca hai cch s dung using Explain why most programs in this book contain a using statement. Describe one alternative. Gii thch ti sao hu ht chng trnh trong cun sch ny s dng cu lnh using. Miu ta cch thay i. Explain why you might want to put reusable code that you create into its own namespace. Gii thch ti sao bn mun t on code s dng li m bn to trong namespace ca n.

13.2. CREATING A CONVERSION FUNCTION TO MT HM CHUYN I


Sometime it is useful to convert an object of one type into an object of another. Although it is possible to use an overloaded operator function to accomplish such a conversion, there is often an easier (and better) way: using a conversion function. A conversion function converts an object into a value compatible with another type, which is often one of the built-in C++ types. In essence, a conversion function automatically converts an object into a value that is compatible with the type of the expression in which the object is used. i khi n hu dng chuyn i mt i tng ca mt loi vo mt loi i tng khc. Mc d n c th s dng mt hm ton t np chng hon thnh mt chuyn i, c mt cch d hn ( v tt hn) : s dng hm chuyn i. Mt hm chuyn i thay i mt i tng thnh mt gi tr thch hp vi mt loi khc, iu thng l mt i tng c xy dng trong C++. iu cn thit, mt hm chuyn i t ng thay i mt i tng thnh mt gi tr thch hp vi loi c xc nh m i tng s dng. The general form of a conversion function is shown here: Kiu mu ca hm chuyn i l: operator type() 756

{ return value; } Here type is the target type you will be converting to and value is the value of the object after the conversion has been performed. Type l loi mc tiu bn s chuyn i thnh v value l gi tr ca i tng sau khi chuyn i c thc hin. Conversion functions return a value of type type. No parameters can be specified, and a conversion function must be a member of the class for which it performs the conversion. Cc hm chuyn i tr v mt gi tr ca loi type. Khng tham s no c th c ch r, v mt hm chuyn i phi l mt thnh vin ca lp m n thc hin cuc chuyn i. As the examples will illustrate, a conversion function generally provides a cleaner approach to converting an objects value into another type than any other method available in C++ because it allows an object to be included directly in an expression involving the target type. Nh cc v d trn chng minh, mt hm chuyn i c th cung cp mt cch tip cn chung chuyn i gi tr ca mt i tng thnh loi khc hn bt k phng php c gi tr no trong C++ bi v n cho php mt i tng c bao gm trc tip trong mt cch gii quyt n tng loi mc tiu.

EXAMPLES: In the following program, the coord class contains a conversion function that converts an object to an integer. In this case, the function returns the product of the two coordinates; however, any conversion appropriate to your specific application is allowed. Trong chng trnh sau, lp coord cha mt hm chuyn i thay i mt i tng thnh s nguyn. Trong trng hp ny, hm tr v sn phm ca hai hm ng nhau. Tuy nhin, bt k s chuyn i no thch hp s p dng c ch r. // A simple conversion function example. #include <iostream>

757

using namespace std;

class coord { int x, y; public: coord(int i, int j) { x = i; y = j; } operator int() { return x*y; // conversion function } };

int main() { coord o1(2,3), o2(4,3); int i;

i = o1; // automatically convert to integer cout << i << \n;

758

i = 100 o2; // convert o2 to integer cout << i << \n;

return 0; }

This program displays 6 and 112. In this example, notice that the conversion function is called when o1 is assigned to an integer and when o2 is used as part of a larger integer expression. As stated, by suing a conversion function, you allow classes that you create to be integrated into normal C++ expressions without having to create a series of complex overloaded operator functions. Trong v d ny, ch hm chuyn i c gi khi o1 c tha hng t mt s nguyn v khi o2 c s dng nh mt phn ca s nguyn di. Nh ni, bng cch s dng mt hm chuyn i, bn cho php cc lp m bn to hp th thnh C++ bnh thng m khng cn to mt s hm ton t np chng phc tp. Following is another example of conversion function. This one converts a string of type strtype into a character pointer to str. Sau y l mt v d khc ca hm chuyn i. y l mt s chuyn i mt chui loi strtype thnh mt con tr chui str.

#include <iostream> #include <cstring> using namespace std;

class strtype { char str[80];

759

int len; public: strtype(char *s) { strcpy(str, s); len = strlen(s); } operator char *() { retrun str; // convert to char* } };

int main() { strtype s(this is a test/n); char *p, s2[80];

p = s; // convert to char* cout << Here is string: << p<< \n;

//convert to char* in function call strcpy(s2,s); cout << Here is copy of string: << s2 << \n;

760

return 0; }

This program displays the following: Here is string: This is a test Here is copy of string: This is a test As you can see, not only is the conversion function invoked when object s is assigned to p (which is of type char*), but it is also called when s is used as a parameter to strcpy( ). Remember strcpy( ) has the following prototype: Nh bn thy, khng ch hm chuyn i cn thit khi i tng s tha hng p ( loi char* ) , nhng n cng c gi khi s c s dng nh mt tham s ca strcpy( ). Nh rng strcpy( ) c nh dng nh sau: char*strcpy(char*s1, const char *s2); Because the prototype specifies that s2 is of type char* , the conversion function to char* is automatically called. This illustrates how a conversion function can also help you seamlessly integrate your classes into C++s standard library functions. Bi v nh dng ch r rng s2 l mt loi char*, hm chuyn i thnh char* c t ng gi. iu ny chng minh lm th no mt hm chuyn i c th gip bn gi lp ca bn thnh cc hm th vin chuyn ca C++.

EXERCISES:
Using the strtype class from Example 2, create a conversion that converts to type int. In this conversion, return the length of the string held in str. Illustrate that your conversion function works. S dng lp strtype t bi tp 2, to mt hm chuyn i thay i thnh s nguyn. Trong chuyn i ny, tr v chiu di ca chui gi trong str. Chng minh rng hm chuyn i ca bn hot ng. Given this class: 761

Cho lp sau:

class pwr { int bse; int exp; public: pwr(int b, int e) { base = b; exp = e; // create conversion to integer here } };

create a conversion function that converts an object of type pwr to type integer. Have the function return the result of baseexp. to mt hm chuyn i thay i mt i tng loi pwr thnh loi s nguyn. C hm tr v kt qu ca baseexp.

13.3. STATIC CLASS AND MEMBERS LP TNH V CC THNH PHN


It is possible for a class member variable to be declared as static. By using static member variables, you can bypass a number of rather tricky problems. When you declare a member variable as static, you cause only one copy of that variable to exist762

no matter how many objects of that class are created. Each object simply shares that one variable. Remember, for a normal member variable, each time an object is created, a new copy of that variable is created, and that copy is accessible only by that object. (That is, when normal variables are used, each object possesses its own copies.) However, there is only one copy of a static member variable, and all objects of its classes derived from the class that contains the static member. C kh nng cho mt thnh vin lp lu ng c khai bo dng tnh. Bng cch s dng thnh vin tnh lu ng, bn c th i vng mt s vn kh phc tp. Khi bn khai bo mt thnh vin ng nh tnh, bn gy ra ch mt sao chp ca thnh vin ng khng xut hin vn , lm th no nhiu i tng ca mt lp c khi to. Mi i tng n gin chia s thnh nhiu ng. Nh rng, cho mt thnh vin ng bnh thng, mi ln mt i tng c to, mt bn copy mi ca thnh vin ng c khi to, v mt copy c truy cp ch c i tng . ( l, khi mt ng bnh thng c s dng, mi i tng chim hu copy ca n). Tuy nhin, ch c mt copy ca thnh vin tnh thay i, v tt c i tng ca cc lp dn xut t lp cha thnh vin tnh. Although it might seem odd when you first think about it, a static member variable exists before any object of its class is created. In essence, a static class member is a global variable that simply has its scope restricted to the class in which it is declared. In fact, as you will see in one of the following examples, it is actually possible to access a static member variable independent of any object. Mc d n c v d tha khi ln u bn ngh v n, mt thnh vin tnh c th thay i xut hin trc khi bt k i tng ca lp ca n c khi to. V bn cht, mt thnh vin lp tnh l hon ton thay i hn ch tm hot ng ca n lp trong c khai bo. S tht, bn s thy trong mt v d sau, n tht s c th truy cp mt thnh t tnh c th thay i ca bt k i tng c lp no. When you declare a static data member within a class, you are not defining it. Instead, you must provide a definition for it elsewhere, outside the class. To do this, you redeclare the static variable, using the scope resolution operator to identify which class it belongs to. Khi bn khai bo d liu thnh t tnh bn trong mt lp, bn khng nh ngha n. Thay v vy, bn phi cung cp mt nh ngha cho n, bn ngoi lp. lm iu ny, bn phi khai bo li bin tnh thay i, s dng ton t tm vc xc nh lp n thuc v. All static member variables are initialized to 0 by default. However, you can give a static class variable an initial value of your choosing if you like.

763

Tt c thnh t tnh c th thay i c bt u t 0 nh mc nh. Tuy nhin, bn c th cho mt lp tnh c th thay i mt gi tr khi u theo la chn ca bn. Keep in mind that the principal reason static member variables are supported by C++ is to avoid the need for global variables. As you can surmise, classes that rely upon global variables almost always violate the encapsulation principle that is so fundamental to OOP and C++. Nh rng l do chnh thnh vin tnh c th thay i c cung cp bi C++ l trnh cn thit cho ton b thay i. Nh bn d on, cc lp da vo ton b thay i hu ht lun lun xm phm gi gn chnh m n c bn cho OOP v C++. It is also possible for a member function declared as static can this usage is not common. A member function to be declared as static can access only other static members of its class. (Of course, a static member function can access non-static global data and functions.) A static member function does not have a this pointer. Virtual static member functions are not allowed. Also, static member functions cannot be declared as const or volatile. A static member function can be invoked by an object of its class, or it can be called independent of any object, via the class name and the scope resolution operator. N cng c th cho mt hm thnh vin c khai bo nh tnh c th cch s dng ny khng ph bin. Mt hm thnh vin c khai bo tnh c th truy cp ch thnh t tnh ca lp. ( D nhin, mt hm thnh vin tnh c th truy cp ton b d liu v hm khng tnh.) Mt hm thnh vin tnh khng c con tr this. Cc hm thnh vin tnh o khng c php. Cc hm thnh vin tnh khng c khai bo nh const hay volative. Mt hm thnh vin tnh c th vin dn bi mt i tng ca lp, hoc n c th c gi bt k i tng c lp, theo ng tn lp v ton t tm vc.

EXAMPLES:
Here is a simple example that uses a static member variable: y l mt v d n gin s dng thnh t tnh thay i // A static member variable example. #include <iostream> using namespace std;

class myclass 764

{ static int i; public: void seti(int n) { i = n; } int geti() { return i; } };

// Definition of myclass::i. i is still private to myclass int myclass::i;

int main() { myclass o1, o2;

o1.seti(10);

cout << o1.i: << o1.get << \n; // displays 10 cout << o2.i: << o2.get << \n; // also displays 10

765

return 0; }

This program displays the following: o1.i: 10 o2.i: 10

Looking at this program, you can see that only object o1 actually sets the value of static member i. However, since i is shared by both o1 and o2 (and, indeed, by any object of type mycalss), both calls to geti( ) display the same result. Xem chng trnh ny, bn c th thy rng ch i tng tht s c gi tr ca thnh t tnh i. Tuy nhin, t khi i c chia s bi c o1 v o2 ( v, tht s, bi bt k i tng ca loi myclass), c hai cuc gi cho geti( ) u cho mt kt qu. Notice how i is declared within myclass but defined outside of the class. This second step ensures that storage for i is defined. Technically, a class declaration is just that, only a declaration. No memory is actually set aside because of a declaration. Because a static data member implies that memory is allocated for that member, a separate definition is required that causes storage to be allocated. Ch lm th no i c khai bo bn trong myclass nhng c nh ngha bn ngoi lp. Bc th hai ny bo m rng ch cha cho i c nh ngha. K thut, mt khai bo lp ch l mt khai bo. Khng c b nh tht s tao ring ra bi v mt khai bo. Bi v thnh vin d liu tnh bao hm b nh c ch nh cho thnh t, mt nh ngha ri rc c yu cu gy ra ch cha c ch nh. Because a static member variable exists before any object of that class is created, it can be accessed within a program independent of any object. For example, the following variation of the preceding program sets the value of i to 100 without any reference to a specific object. Notice the use of the scope resolution operator and class name to access i. Bi v thnh t tnh thay i xut hin trc khi bt k i tng no ca lp 766

c to, n c th truy cp bn trong mt chng trnh ca i tng c lp. V d, s thay i ca chng trnh trc t gi tr ca i l 100 m khng truy cp n gi tr thch hp. Ch rng cch s dng ca ton t tm vc v tn lp truy cp i. // Use a static member variable independent of any object. #include <iostream> using namespace std;

class myclass { public: static int i; void seti(int n) { i = n; } int geti() { return i; } };

int myclass::i;

int main() {

767

myclass o1,o2;

// set i directly myclass::i = 100; // no object is referenced

cout << o1.i: << o1.geti() << \n; // displays 100 cout << o2.i: << o2.geti() << \n; // also displays 100 return 0; }

Because i is set to 100, the following output is displayed: o1.i: 100 o2.i: 100

One very common use of a static class variable is to coordinate access to a shared resource, such as a disk file, printer, or network server. As you probably know from your previous programming experience, coordinating access to a shared resource requires some means of sequencing events. To get an idea of how static member variables can be used to control access to a shared resource, examine the following program. It creates a class called output, which maintains a common output buffer called outbuf that is, itself, a static character array. This buffer is used to receive output sent by the outbuf( ) member function. This function sends the contents of str one character at a time. It does so by first acquiring access to the buffer and then sending all the characters in str. It locks out access to the buffer by other objects until it is done outputting. You should be able to follow its operation by studying the code and reading the comments. Mt cch s dng chung ca lp tnh thay i l truy cp ngang hng n ngun c chia s, nh l file a, my in, hoc mng. Nh bn bit t kinh nghim chng trnh trc, truy cp ngang hng n ngun m yu cu vi gi tr trung bnh ca dy s. ly mt kin ca lm th no thnh t tnh thay i c th c s dng iu khin truy cp ngun m, v d chng trnh sau. N to

768

mt lp gi l output, duy tr mt xut chung vt m c gi l outbuf, d l chui k t tnh. Vt m ny c s dng nhn phn xut c gi bi hm thnh vin outbuf( ). Hm ny gi ni dung ca str mt k t mi ln. N l yu cu u tin truy cp n vt m v sau gi tt c k t vo str. N kha truy cp n vt m bi cc i tng khc n khi n thc hin phn xut. N c th theo sau ton t bi nghin cu code v c lnh. // A shared resource example. #include <iostream> #include <cstring> using namespace std; class output { static char outbuf[255]; // this is the shared resource static int inuse; // buffer available if 0; static into index; // index of outbuf char str[80]; int i; // index of next char in str int who; // identifies the object, must be > 0 public: output(int w, char *s) { strcpy(str, s); i = 0; who = w; }

/* This function returns -1 if waiting for 769

buffer it returns 0 if it is done outputting, and it returns who if it is still using the buffer. */ int putbuf() { if(!str[i]) // done putputting { inuse = 0; // release buffer return 0; // signal termination } if(!inuse) inuse = who; // get buffer if(inuse != who) return -1; // inuse by someone else if(str[i]) // still chars to output { outbuf[oindex[ = str[i]; i++; oindex++; outbuf[oindex] = \0; // always keep null-terminated return 1; } return 0; 770

} void show() { cout << outbuf << \n; } };

char output::outbuf[255]; // this is the shared resource int output::inuse = 0; // buffer available if 0 int output::oindex = 0; // index of outbuf

int main() { output o1(1, This is a test), o2(2, of statics);

while(o1.putbuf()|| o2.putbuf()); // output chars

o1.show();

return 0; }

static member functions have limited applications, but one good use for them is to preinitialize private static data before any object is actually created. For example, this is a perfectly valid C++ program:

771

Hm thnh vin tnh gii hn s p dng, nhng cch s dng hay l khi ng trc d liu tnh ring trc khi bt k i tng no tht s c tao. V d, y l mt chng trnh C++:

#include <iostream> using namespace std;

class static_func_demo { static int i; public: static void init(int x) { i = x; } void show() { cout << i; } };

int static_func_demo::i; //define i

int main() { // init static data before object creation

772

static_func_demo::init(100); static_func_demo x; x.show(); //displays 100

return 0; }

EXERCISES:
Rework Example 3 so that it displays which object is currently outputting characters and which one or ones are blocked from outputting a character because the buffer is already in use by another. Lm li v d 3 m n thc hin vi i tng hin hnh xut ra cc k t v mt hoc nhiu b chn t phn xut mt k t bi v vng m c s dng. One interesting use of a static member variable is to keep track of the number of objects of class that are in existence at any given point in time. The way to do this is to increment a static member variable each time the class constructor is called and decrement it each time the class destructor is called. Implement such a scheme and demonstrate that it works. Mt cch s dng ca thnh t tnh thay i l gi du ca mt s i tng ca lp xut hin ti bt k im no cho trc mi ln. y l cch lm ln ln mt thnh t tnh thay i mi ln phng thc khi to ca lp c gi v lm gim mt s sp xp mi ln phng thc ph hy c gi. Thc hin nh mt cch sp xp v gii thch rng cch hot ng ca n.

13.4. CONST MEMBER FUNCTIONS AND MUTABLE HM THNH PHN KHNG I V C TH THAY I
Class member function can be declared as const. When this is done, that function cannot modify the obj that invokes it. Also, a const obj cannot invoke a non-const member function. However, a const member function can be called by either const or

773

non-const obj. To specify a member function as const, use the form shown in the following example: Ham lp thanh phn co th c khai bao nh la const. Khi no c thc hin, ham o khng th thay i i tng goi no. Cung vy,mt i tng const co th khng goi mt ham thanh phn non-const. Tuy nhin, mt ham thanh phn const co th c goi bi ca i tng const hay non-const. Chi ro mt ham thanh phn nh la const, dung mt dang cho thy trong vi du di y: class X { int some_var; public: int f1() const; // const member function }; As you can see, the const follows the functions parameter declaration. Sometimes there will be one or more members of a class that you want a const function to be able to modify even though you dont want the function to be able to modify any of its other members. You can accomplish this through the use of mutable, which overrides const-ness. That is, a mutable member can be modified by a const member function. Nh ban thy, const cho theo sau ham khai bao tham s. i khi se co mt hay nhiu thanh phn cua mt lp ma ban mun mt ham const co th thay i thm chi khi ban khng mun ham co th thay i bt ki thanh phn nao khac. Ban co th hoan thanh vic nay thng qua cach s dung mutable, co th ghi e constness. Khi o, mt ham mutable co th thay i bi mt ham thanh phn.

EXAMPLES (VI DU)


1. The purpose of declaring a member function as const is to prevent it from modifying the obj that invokes it. For example, consider the following program. Muc ich cua vic khai bao mt ham thanh phn nh const bao v no khoi s thay i

774

i tng ma goi no. Vi du, xem xet chng trinh di y. /* Demonstrate compile. */ const member function.This program wont

#include <iostream> using namespace std;

class Demo { int i; public: int geti() const return i; // ok void seti(int x) const i=x; // error! };

int main() { Demo ob;

ob.seti(1900); cout<<ob.geti(); return 0; } This program will not compile because seti() is declared as const. This means that it is not allowed to modify the invoking obj. Since it attempts to change i, the program is in

775

error. In contrast, since geti() does not attempt to modify i, it is perfectly acceptable. Chng trinh nay se khng bin dich bi vi seti() c khai bao nh la const. iu nay co nghia la no se khng c cho phep thay i i tng. Khi no c thay i i, chng trinh se bi li. Ngc lai, khi geti() khng c thay i i, no co th c chp nhn hoan hao. 2. To allow selected members to be modified by a const member function, specify them as mutable. Heres an example: Cho phep nhng thanh phn c chon c thay i bi mt ham thanh phn const, chi ro no nh la mutable. Nh vi du di y: // Demonstrate mutable. #include <iostream> using namespace std; class Demo { mutable int i; int j; public: int geti() const return i; // ok void seti(int x) const i=x; // now, ok.

/* The following function wont compile. void setj(int x) const j=x; // still wrong! */ };

776

int main() { Demo ob;

ob.seti(1900); cout<<ob.geti();

return 0; } Here i is specified as mutable, so it can be changed by the seti() function. However, j is not mutable, so setj() is unable to modify its value. y i c chi ro nh mutable, vi no co th c thay i bi ham seti(). Tuy nhin, j khng la mutable, vi vy setj() khng th thay i c gia tri cua no.

EXERCISES (BAI TP)


1. The following program attempts to create a simple countdown timer that rings a bell when the time period is over. You can specify the time period and increment when a CountDown obj is created. Unfortunately, the program will not compile as a shown here. Fix it. Chng trinh di y c tao ng h m ngc thi gian n gian ma chung se reng khi ht thi gian. Ban co th chi ro khoang thi gian va s lng khi mt i tng CountDown c tao. ang tic, chng trinh se khng bin dich nh mt trinh bay nay. Hay sp xp no. // This program contains an error. #include <iostream>

777

using namespace std;

class CountDown { int incr; int target; int current; public: CountDown(int delay, int i=1) { target=delay; incr=i; current=0; } bool counting() const { current += incr; if(current >= target) { cout<<\a; return false; } cout<<current<< ; return true; }

778

};

int main() { CountDown ob(100, 2);

while(ob.counting()); return 0; } 2. Can a const member function call a non-const function? Why not? Mt ham thanh phn const co th goi nh mt ham non-const? Tai sao khng?

13.5. A FINAL LOOK AT CONSTRUCTORS - CI NHN CUI CNG V HM


Although constructors were described early on in this book, there are still a few points that need to be made. Consider the following program: Mc du ham dng c m ta sm trong cun sach nay, vn con mt vai im cn c lam. Xem xet chng trinh di y: #include <iostream> using namespace std;

class myclass { int a; public: 779

myclass(int x) a=x; int geta() return a; };

int main() { myclass ob(4);

cout<<ob.geta();

return 0; } Here the constructor for myclass takes one parameter. Pay special attention to how ob is declared in main(). The value 4, specified in the parentheses following ob, is the argument that is passed to myclass() s parameter x, which is used to initialize a. This is the form of initialization that we have been using since the start of this book. However, there is an alternative. For example, the folllowing statement also inittializes a to 4: y ham dng cho myclass nhn mt tham s. a ra mt chu y c bit la ob c khai bao trong ham main() nh th nao. Gia tri 4, a chi ro trong ngoc n cua ob di y, la i s ma vt qua tham s x cua myclass(), c dung khi tao a. y la dang cua khi tao ma chung ta a s dung t khi bt u cun sach nay. Tuy nhin, y la mt s thay th. Vi du, oan chng trinh di y cung khi tao a la 4. myclass ob=4; // automatically converts into myclass(4)

As the comment suggests, this form of initialization is automatically converted into a call to the myclass constructor with 4 as the argument. That is, the preceding statement

780

is handled by the compiler as if it were written like this: Nh li chu giai nghi, dang nay cua s khi tao c t ng chuyn i thanh mt li goi ham dng myclass vi 4 nh la mt i s. iu o la, oan chng trinh trc c vn dung bi trinh bin dich nh la no c vit nh: myclass ob(4); In general, any time that you have a constructor that requires only one argument, you can use either ob(x) or ob=x to initialize an obj. The reason for this is that whenever you create a constructor that takes one argument, you are also implicitly creating a conversion from the type of that argument to the type of the class. Noi chung, bt ky thi im nao ban cung co mt ham dng chi cn mt i s, ban co th dung ca ob(x) hay ob=x khi tao mt i tng. Ly do la bt c khi nao ban cung co th tao mt ham dng ma ly mt i s, ban cung hoan toan tao s chuyn i t dang i s nay sang dang i s khac cua lp. If you do not want implicit conversions to be made, you can prevent them by using explicit. The explicit specifier applies only to constructors. A constructor specified as explicit will be used only when an initialization uses the normal constructor syntax. It will not perform any automatic conversion. For example, if the myclass constructor is declared as explicit, the automatic conversion will not be supplied. Here is myclass() declared as explicit. Nu ban khng mun s chuyn i ngm c thc hin, ban co th ngn chung bng cach s dung explicit. Explicit khng chi ap dung cho ham dng. Mt ham dng chi ro nh la explicit se c dung chi khi mt s khi tao dung cu phap dng binh thng. No se khng trinh bay bt ky i s t ng nao. Vi du, nu ham dng myclass c dn xut nh la explicit, i s t ng se khng c cung cp. y myclass() c dn xut nh la explicit. #include <iostream> using namespace std;

class myclass { int a; public: 781

explicit myclass(int x) a=x; int geta() return a; }; Now only constructors of the form myclass ob(110); will be allowed.

EXAMPLES (VI DU)


1. There can be more than one converting constructor in a class. For example, consider this variation on myclass: Co nhiu hn mt s chuyn i cua ham dng trong mt lp. Vi du, xem xet s khac bit trong lp myclass. #include <iostream> #include <cstdlib> using namespace std;

class myclass { int a; public: myclass(int x) a=x; myclass(char *str) a=atoi(str); 782

int geta() return a; };

int main() { myclass ob1=4; // converts to myclass(4) myclass ob2=123; // converts to myclass(123);

cout<<ob1:<<ob1.geta()<<endl; cout<<ob2:<<ob2.geta()<<endl;

return 0; } Since both constructors use different type arguments(as, of course, they must), each initialization statement is automatically converted into its equivalent constructor call. Khi ca hai ham dng dung loai i s khac nhau(nh la, di nhin, no phai), mi s khi tao oan chng trinh c t ng chuyn i thanh li goi ham dng tng ng cua no. 2. The automatic conversion from the type of a constructors first argument into a call to the constructor itself has interesting implications. For example, assuming myclass from Example 1, the following main() function makes use of the conversions from int and char* to assign ob1 and ob2 new values. S chuyn i t ng t loai cua mt i s u tin cua ham dng trong mt li goi chinh ham dng co mt s lin quan thu vi. Vi du, tha nhn rng lp myclass trong vi du 1, ham main() di y s dung cach chuyn i t int va char* gan cho ob1 va ob2 gia tri mi. int main()

783

{ myclass ob1=4; // converts to myclass(4) myclass ob2=123; // converts to myclass(123);

cout<<ob1:<<ob1.geta()<<endl; cout<<ob2:<<ob2.geta()<<endl;

// use automatic conversion to assign new values ob1=1776; // converts into ob1=myclass(1776); ob1=2001; //converts into ob2=myclass(2001);

cout<<ob1:<<ob1.geta()<<endl; cout<<ob2:<<ob2.geta()<<endl;

return 0; } 3. To prevent the conversions just shown, you could specify the constructors as explicit, as shown here: ngn s s chuyn i chi c trinh bay, ban co th chi ro ham dng nh la explicit, nh c trinh bay di y: #include <iostream> #include <cstdlib> using namespace std;

class myclass {

784

int a; public: explicit myclass(int x) a=x; explicit myclass(chat *str) a=atoi(str); int geta() return a; };

EXERCISES (BAI TP)


In Example 3, if only myclass(int) is made explicit, will myclass(char *) still allow implicit conversions? (Hint: try it.) Trong vi du 3, chi duy nht myclass(int) c lam explicit, myclass(char*) se vn cho phep s chuyn i ngm? (Gi y:th lam no i) Will the following fragment work? oan di y se lam vic nh th nao? class Demo { double x; public: Demo(double i) x=i; // ... };

// ... 785

Demo counter=10; Justify the inclusion of the explicit keyword. (In other words, explain why implicit constructor conversions might not be a desirable feature of C++ in some cases.) Giai thich s thm vao cua t khoa explicit. (Mt khac, giai thich tai sao s chuyn i ham dng co th khng la mt tinh nng ang ao c cua C++ trong mt s trng hp.)

13.6. USING LINKAGE SPECIFIERS AND THE ASM KEYWORD


C++ providers two important mechanisms that make it easier to link C++ to other languages. One is the linkage specifier. Which tells the compiler that one or more functions in your C++ program wil be linked with another language that might have a different approach to naming, parameter passing, stack restoration, and the like. The second is the asm keyword, which allows you to embed assembly language instructions in your C++ source code. Both are examined here.

C++ cung cp cho chng ta 2 k thut quan trng c th kt ni d dng cc ngn ng vi nhau. Mt trong nhng cch l k thut ch r kt ni. K thut ny cho php trnh bin dch c th nhn din ra c cc hm trong chng trnh c lin kt vi cc ngn ng khc m c th c s khc nhau v cch t tn, tham bin, stack, v nhng ci i loi nh th. K thut th 2 l dng t kha asm, k thut ny cho php nhng cu trc lnh ca hp ng vo m ngun C++. Tt c u c nghin cu di y. By default, all functions in C++ program are compiled and linked as C++ functions. However, you can tell the c++ compiler to link a function so that it is compatible with another type of language. All C++ compilers allow functions to be linked as either C or C++ functions. Some also allow you to link functions with languages such as PasCal, Ada or FORTRAN. To cause a function to be linked for a different language, use this general form of the linkage specification: ch mc nh, tt c cc hm trong chng trnh C++ c bin dch v lin kt nh l cc hm C++. Tuy nhin, bn c th ch cho trnh bin dch ca C++ bin dch mt hm cho hm tng thch vi cc ngn ng lp trnh khc. Tt c cc trnh bin dch C++ u cho php hm c lin kt khng l hm C hoc C++. Mt vi ci khc

786

th cho php bn lin kt cc hm vi cc ngn ng lp trnh khc, chng hn nh Pascal, Ada hay FORTRAN. Bi v l do mt hm c th lin kt vi cc ngn ng khc, nn mt hnh thc ph bin l ch r ngn ng lin kt Here language is the name of the language with which you want the specified function to link. If you want to specify linkage for more than one function, use this form of the linkage specification: Pha di l tn ca ngn ng m bn mun ch r hm lin kt. Nu bn mun ch r lin kt cho nhiu hm, c th s dng cu trc nh sau: Exten language function-prototype: extern language { function prototype; } All linkage specifications must be global: they cannot be used inside a function. Mi s ch r lin kt phi l ton cc, chng khng th s dng bn trong thn hm. The most common use of linkage specifications occurs when linking C++ programs to C code. By specifying C linkage you prevent the compiler from mangling( also known as decorating) the names of functions with embedded type information. Because of C++s ability to overload functions and create member functions, the link-name of a function usually has type information added to it. Since C does not support overloading or member functions. It cannot recognize a mangled name. Using C linkage avoids this problem.

Vic s dng k thut ch r s lin kt thng xy ra ph bin khi lin kt chng trnh C++ vi m lnh C. Bng cch ch r lin kt C bn c th ngn cn trnh bin dch khi s sai lc ( trang hong) tn ca hm vi cc loi thng tin c nhng vo. Bi v C++ cho php np chng cc hm v to cc hm thnh vin, tn lin kt ca hm thng l loi thng tin c thm vo n. Trong khi , C khng h tr np chng hay l hm thnh vin. N khng th nhn din mt tn c sai. S dng lin kt C s trnh c iu ny. Although it is generally possible to link assembly language routines with a C++ program, there is often an easier way to use assembly language. C++ supports the 787

special keyword asm, which allows you to embed assembly language instructions within c C++ function. These assembler is that your entire program is completely defined as a C++ program and there is no need to link separate assembly language files. The general form of the asm keyword is shown here: Mc d thng c th lin kt thng trnh hp ng vi chng trnh C++, Nhng cng c mt cch d dng hn s dng hp ng. C++ cung cp t kha c bit asm, cho php bn nhng cu trc lnh hp ng vo trong hm C++. Nhng chng trnh dch hp ng ny nm trong chng trnh ca bn v c nh ngha hon ton nh l mt chng trnh C++ v cng khng cn c lin kt chia s vi cc tp tin hp ng. Hnh thc ph bin ca t kha asm c th hin di y: Asm(op_code); Where op-code is the assembly language instruction that will be embedded in your program. Its important to note that several compilers accept these three slightly different forms of the asm statement. Ni no m m thao tc l cu trc lnh hp ng th s c nhng vo trong chng trnh ca bn. Mt ch quan trng l mt vi trnh bin dch chp nhn 3 hnh thc khc nhau ( khng ng k) ca cu trc lnh asm. Asm op-code; Asm op-code newline Asm { Instruction sequence; } Here op-code is not enclosed in double quotes. Because embedded assembly language instructions tends to be implementation dependent. You will want to read your compilers user manual on this issue. NOTE: Microsoft Visual C++ uses_ _ asm for embedding assembly code. It is otherwise similar to asm.

788

y l m lnh thao tc khng c km theo trong nhy kp. Bi v lnh hp ng c nhng vo thi hnh mt cch c lp. Bn s mun c trnh bin dch ca bn bin dch nh th no? CH : Mircosoft Visual C++ s dng __asm nhng m hp ng. N th khc vi asm.

EXAMPLE:
1. This program links func() as a C, rather than a C++, function: Chng trnh ny lin kt hm func() nh l mt hm ca C, hn l ca C++, Hm nh sau: // Illustrate linkage specifier. #include <iostream> using namespace std;

extern "C" int func(int x); // link as C function

// This function now links as a C function. int func(int x) { return x/3; } This function can now be linked with code compiled by a C compiler. Hm ny c th lin kt vi m lnh bin dch bi trnh bin dch C. 2. The following fragment tells the compiler that f1(), f2(), and f3() should be linked as C functions: S sp xp bn di ch cho trnh bin dch bit rng f1(), f2(), v hm f3() c th lin kt nh l hm C: extern "C" { 789

void f1(); int f2(int x); double f3(double x, int *p); }

3. This fragment embeds several assembly language instructions into func(): Nhng on ny c nhng mt vi lnh hp ng vo hm func(): void func() { asm("mov bp, sp"); asm("push ax"); asm("mov cl, 4"); // ... } Remember: You must be an accomplished assembly language programmer in order to successfully use in-line assembly language. Also, be sure to check your compilers user manual for details regarding assembly language usage. Nh rng: Bn phi l mt ngi lp trnh hp ng tt s dng thnh cng hp ng ni hm. Bn cng phi chc chn vic kim tra ngi s dng trnh bin dch cu bn nm vng v cch s dng hp ng.

EXERCISE:
On your own, study the sections in your compilers user manual that refer to linkage specifications and assembly language interfacing. 1. Theo bn, hc phn no trong sch hng dn s dng trnh bin dch ca mn m gii thiu n k thut ch r lin kt v giao din hp ng.

790

13.7.

ARRAY-BASE I/O MNG C S NHP/XUT

In addition to console and file I/O, C++ supports a full set of functions that use character arrays as the input or output devide. Although C++s array-base I/O parallels, in concept, the array-base I/O found in C++ ( specifically, Cs sscanf() and sprintf() functions), C++s array-base I/O is more flexible and useful because it allows user defined types to be integrated into it. Although it is not possible to cover every aspect of array-base I/O here, the most important and commonly used features are examined. Thm vo cch gii quyt v tp tin I/O , C++ cung cp y cc hm s dng cho mng k t nhp xut t thit b. Mc d C++ c cc mng c s song song, tng t, mng c s I/O cng c tm thy trong C++( c bit l cc hm C : sscanf(), sprintf). Mng c s trong C++ uyn chuyn hn v tin li hn bi v n cho php ngi dng nh ngha loi c tch hp vo n. Mc d khng th bao gm ht cc kha cnh ca mng c s I/O, Nhng c im quan trong nht v ph bin nht s c nghin cu di y. It is important to understand from the outset that array-based I/O still operates through streams. Everything you learned about C++ I/O in Chapters 8 and 9 is applicable to array-based I/O. In fact, you need to learn to use just a few new functions to take full advantage of array-based I/O . These functions link a stream to a region of memory. Once this has been accomplished, all I/O takes place through the I/O functions you know already. Vic hiu mng c s I/O vn thao tc trn dng th rt quan trng. Mi th bn hc c chng 8 v 9 u hu dng cho mng c sI/O. S tht, bn can phi hc thm cch s dng mt vi hm mi s dng ht nhng im thun li ca mng c s I/O. Nhng hm ny lin kt dng thc thi vi mt vng nh. Mt trong nhng hm c hc, v mi I/O xy ra thng qua cc hm I/O m bn bit. Before you can use array-based I/O , you must be sure to include the header <strstream> in your file. IN this header are defined the classes istrstream, ostrstream, and strstream. These classes create array-based input, output, and input/output streams, respectively. These classes have as a base ios, so all the functions and manipulators istrstream, opstrstream, and strstream. To use a character array for output, use this general form of the ostrstream

791

constructor: Trc khi bn c th s dng mng c s I/O, bn phi chc chn l km file <strstream> vo trong tp tin ca bn. Trong phn u trang ny c nh ngha cc lp istrstream, ostrstream, v strstream. Nhng lp ny to ra mng c s nhp xut, v tch bit nhp v xut trn dng thc thi. Nhng lp ny l mt c s ios, m mi hm v thao tc trn isstrstream, opstrstream, v strstream. s dng mng k t nhp, dng hnh thc chung ca phng thc khi to ostrstream nh sau: ostrstream ostr( char * buf, streamsize size, openmode mode = ios::out); Here ostr will be the stream associated with the array buf. The size of the array is specified by size. Generally, mode is simply defaulted to output, but you can see any output mode flag defined by ios if you like. (Refer to Chapter 9 for details.) Hm ostr s l mt dng thc thi kt hp vi mng m. Kch thc ca mng c ch r bi size. Ni chung, trng thi mc nh n gin l xut, nhng bn c th thy bt k c trng thi xut c nh ngha bi ios nu bn mun. ( xem chi tit chng 9). One an array has been opened for output, characters will be put into the array until it is full. The array will not be overrun. Any array attempt to overfill the array will result in an I/O error. To find out how many characters have been written to the array, use the pcount() member function, shown here: Mt mng ang c m nhp, mt k t s c t vo mng cho n khi mng y. Mng s khng trn. Bt k mng no c gng in vo mng s c kt qu li I/O. tm xem c bao nhiu k t c ghi vo mng, dng hm thnh vin pcount(): streamsize pcount(); You must call the function in conjunction with a stream, and it will return the number of characters written to the array, including any null terminator. To open an array for input, use this form of the isstrstream constructor: Bn phi gi hm chung trong dng thc thi, v n s tr v s lng k t c nhp vo mng, bao gm c k t ngt NULL. m mt mng nhp, s dng hnh thc thit lp isstrstream nh sau: Isstrstream istr( const char* buf); Here buf is a pointer to the array that will be used for input. The input stream will be

792

called istr. When input is being read from an array, eof() will return true when the end of the array has been reached. Phn buf l mt con tr vo mng dng nhp. Dng thc thi nhp s c gi istr. Khi m s kin nhp din ra t mng, hm eof() s tr v true khi n cui mng To open an array for input/output operations, use this form of the strstream constructor: Here is iostr will be an input/output stream that uses the array pointed to by buf, which is size characters long. The character-based streams are also referred to as char* streams in some C++ literature. It is important to remember that all I/O functions described earlier operate with arraybased I/O, including the binary I/O functions and the random-access functions. m mt mng cho thao tc nhp xut, s dng hnh thc thit lp ca strstream nh sau: Iostr s l dng nhp xut dng mng c tr n bi buf, c di l size. Dng thc thi k t c s cng c quy vo nh l char* trong mt vi ti liu C++. Nh rng tt c cc hm I/O u m t thao tc d dnh hn vi mng c s I/O, bao gm c I/O nh phn v hm truy xut ngu nhin. strtream iostr( char * buf, streamzie size, openmode mode ios::in||ios::out); Note : the character-based stream classes have been deprecated by Standard C++. This means that they are still valid, but future versions of the C++ language might not support them. They are included in the book because they are still widely used. However, for new code you will probably want to use one of the containers described in Chapter 14.

Ch : lp dng thc thi k t c s b phn i bi C++ chun. iu c ngha l chng vn c gi tr, tuy nhin trong nhng phin bn mi hn ca ngn ng C++ s khng h tr chng na. Chng c trong cun sch ny bi v chng vn c dng ph bin. Tuy nhin, Mt m lnh m m bn c l mun dng c m t trong chng 14.

793

EXAMPLES: Hers is a short example that shows how to open an array for output and write data to it: y l mt v d ngn ch ra lm cch no m mt mng xut v ghi d liu vo n. #include <iostream> #include <strstream> using namespace std;

int main() { char buf[255]; // output buffer

ostrstream ostr(buf, sizeof buf); // open output array

ostr << "Array-based I/O uses streams just like "; ostr << "'normal' I/O\n" << 100; ostr << ' ' << 123.23 << '\n';

// you can use manipulators, too ostr << hex << 100 << ' '; // or format flags ostr.setf(ios::scientific); ostr << dec << 123.23; ostr << endl << ends; // ensure that buffer is null-terminated

794

// show resultant string cout << buf;

return 0; } Here is an example of array-based input: This program reads and then redisplays the values contained in the input array buf. y l mt v d ca mng c s nhp: Chng trinh c v sau th hin gi tr ch trong mng nhp buf #include <iostream> #include <strstream> using namespace std;

int main() { char buf[] = "Hello 100 123.125 a";

istrstream istr(buf); // open input array

int i; char str[80]; float f; char c; istr >> str >> i >> f >> c; cout << str << ' ' << i << ' ' << f; 795

cout << ' ' << c << '\n'; return 0; } Keep in mind that an input array, once linked to a stream, will appear the same as a file. For example, this program uses get() and the eof() function to read the contents of buf. Nh rng mt mng nhp, mt lin kt vi dng thc thi, s xut hin trong cng mt file. V d nh, chng trinh ny s dng hm get() v eof() c ni dung trong buf. #include <iostream> #include <strstream> using namespace std;

int main() { char buf[] = "Hello 100 123.125 a"; istrstream istr(buf); char c; while(!istr.eof()) { istr.get(c); if(!istr.eof()) cout << c; }

return 0; }

796

Chng trnh ny biu din nhp xut trn mt mng: Chng trnh u tin s ghi d liu xut vo iobuf, Sau c n tr li. N c u tin ton b dng this is a test s dng hm getline(). Sau c s thp phn c gi tr l 100 v s thp lc phn c gi tr 0x64. #include <iostream> #include <strstream> using namespace std;

int main() { char iobuf[255]; strstream iostr(iobuf, sizeof iobuf); iostr << "This is a test\n"; iostr << 100 << hex << ' ' << 100 << ends; char str[80]; int i; str.getline(str, 79); // read string up to \n iostr >> dec >> i; // read 100 cout << str << ' ' << i << ' '; iostr >> hex >> i; cout << return 0; } hex << i;

797

EXERCISES: Sa i v d 1 n th hin s k t c ghi vo buf cho n khi gp ngt. Write an application that uses array-based I/O to copy the contents of one array to another. ( This is, of course, not the most efficient way to accomplish this task.) Vit mt ng dng m s dng mng c s I/O sao chp ni dung ca mt mng khc. ( D nhin, y khng phi l cch tt nht cho bi tp ny) Using array-based I/O , write a program that converts a string that contains a floating-point value into its internal representation. S dng mng c s I/O, vit chng trnh chuyn i chui s thc thnh gi tr

Skills check Mastery Skills Check At this pont you should be able to perform the following exercises and answer the questions. Ti thi im ny bn c th lm cc bi tp di y v tr li cc cu hi. What makes a static member variable different from other member variables? Thnh phn tnh c g khc so vi cc thnh phn khc? What header must be included in your program when you use array-based I/O ? Khi s dng mng c s I/O, header trong chng trnh ca bn phi bao gm ci g ? Aside from the fact that array-based I/O uses memory as an input and/or output device, is there any difference between it and normal I/O in C++? Bn cnh s tht l mng c s i/O s dng b nh nhp v/ hay xut, c nhng im no khc nhau gia n v bnh thng I/O trong C++ ? Given a function called counter(), show the statement that causes the compiler to compile this function for C language linkage. Cho mt chng trnh gi hm counter(), ch ra cu lnh no l nguyn nhn m trnh bin dch dch hm ny ra ngn ng C. What does a conversion function do? Hm chuyn i lm ci g? Explain the purpose of explicit. Gii thch mc ch ca thnh phn explicit. 798

What is the principal restriction placed on a const member function? Hn ch ch yu ca hm thnh vin hng l g? Explain namespace Gii thch namespace. What does mutable do? Mutbale l ci g ?

Cumulative Skills check This section checks how well you have integrated material in this chapter with that from the preceding chapters. Trong phn ny kim tra xem bn kt hp kin thc ca chng ny vi cc chng trc nh th no. Since a constructor that requires only one argument, provides an automatic conversion of the type of that argument to its class type, is there a situation in which this feature eliminates the need to create an overloaded assignment operators? T khi phng thc thit lp ch cn mt tham s, cung cp mt hm chuyn i t ng ca loi ca lnh thuc loi lp ca n. C cn mt v tr. Can a const_cast be used within a const member function to allow it to modify its invoking object ? Const_cat c th c s dng trong hm thnh vin hng cho php n thay i yu cu ca i tng khng ? Thought question : since the original C++ library was contained in the global namespace and all old C++ programs have already dealt with this fact, What is the advantage to moving the library into the std namespace after the fact, so to speak ? T cu hi : khi m th vin gc C++ c ch trong bin namespave v tt c cc chng trnh C++ c tha thun vi iu ny. iu thun li ca vic di chuyn th vin vo std namespace sau s tht gii thch? Look back at the examples in the first twelve chapters. Think about what member functions can be made const or static. Are there examples in defining a namespace would be appropriate ? Nhn li nhng v d trong phn u chng 12. Hm thnh vin c th l hng hoc l tnh. C nhng v d no trong phn nhn dng namespace cng th hin iu ny ? 799

CHAPTER 14 INTRODUCING THE STANDARD TEMPLATE LIBRARY GII THIU V TH VIN GIAO DIN CHUN
CHAPTER OBJECTIVES 14.1 AN OVERVIEW OF THE STANDARD TEMPLATE LIBRARY Tng quan v th vin giao din chun 14.2 THE CONTAINER CLASSES - Cc lp cha ng 14.3 VECTORS 14.4 LISTS 14.5 MAPS 14.6 ALGORITHMS - Cc thut ton 14.7 THE STRING CLASS - Lp chui

CONGRATULATIONS if you have worked your way through the preceding chapters of this book, you can definitely call yourself a C++ programmer. In this, the final chapter of the book, we will explore of C++s most exciting- and most advanced- features : the Standard Template Library. Chc mng bn nu bn lm vic theo cch ca bn trong nhng chng trc ca cun sch, bn c th t gi mnh l mt lp trnh vin C++. Trong chng cui ca cun sch, chng ta s khm ph nhng im th v nht v tin ch nht : Th vin Template chun. The instruction of the Standard Template Library, or STL, was one of the major efforts that took place during the standardization of C++. The STL was not a part of the original specification for C++ but was added during the standardization proceedings.

800

The STL provides general purpose, templatized classes and functions that implement many popular and commonly used algorithms and data structures. For example, it includes support for vectors, lists,queues, and stacks. It also defines various routines that access them. Because the STL is constructed from template classes, the algorithms and data structures can be applied to nearly any type of data. Cu trc ca th vin Template chun, hay STL, l mt trong nhng n lc ln nht trong vic chun ha C++. STL khng l mt phn ca s phn loi gc cho C++ tuy nhin n c thm vo trong qu trnh chun ha. STL cung cp nhng mc ch chung, cc lp mu v hm thc thi rt ph bin v thng dng trong tnh ton v cu trc d liu. V d nh, STL h tr lp vectors, lists, queues, v stack. N cng nh ngha nhng nh trnh khc nhau thc thi chng. Bi v STL c xy dng t lp mu, nn cc thut ton v cu trc d liu c th c cung cp gn ging vi cc loi d liu khc. It must be stated at the outset that the STL is a complex piece of software engineering that uses some of C++s most sophisticated features. To understand and use the STL you must be comfortable with all of the material in the preceding chapters. Specifically, you must feel at home with templates. The template syntax that describes the STL can seem quite intimidating- although it looks more complicated than it actually is. While there is nothing in this chapter that is any more difficult than the material in the rest of this book, dont be surprised or dismayed if you find the STL confusing at first. Just be patient, study the examples, and dont let the unfamiliar syntax distract you from the STLs basic simplicity. N phi trng thi bt u m STL th l mt phn phc tp ca k s phn mm, h s dng mt vi c im phc tp ca C++. hiu v s dng STL bn phi nm r tt c cc kin thc trong cc chng trc. c bit bn phi c hng th vi templates. Cu trc ca template c m t STL kh phc tp- mc d n phc tp hn l thc s nh th. Trong khi khng c g trong chng ny m c th kh hn nhng kin thc trong chng cui ny, ng ngc nhin v mt tinh thn nu nh bn b STL t chi ngy t u. Hy kin nhn, nghin cu v d, v ng v nhng cu trc khng bnh thng lm bn xao lng tnh c bn ca STL. The STL is a large library, and not all of its features can be described in this chapter. In fact, a full description of the STL and all of its features, nuances, and programming techniques to familiarize you with its basic operation, design philosophy, and programming fundamentals. After working through this chapter, you will be able to easily explore the remainder of the STL on your own. This chapter also describes one of C++s most important new classes: string. The string class defines a string data type that allows you to work with character strings much as you do with other data types, using operators. 801

STL l mt th vin ln, v khng th m t ht tt c cc c im ca n trong chng ny. S tht. mt m t y ca STL v tt c cc c im ca n, sc thi v k thut lp trnh gip cho bn hiu r vi cc thao tc c bn, l lun thit k, v cc chng trnh c bn. Sau khi hc xong chng ny, bn s c th d dng khm ph nhng phn cn li ca STL bng cch ca bn. Chng ny cng m t mt trong nhng lp mi quan trng nht ca C++: string. Lp chui nh ngha mt kiu d liu chui cho php bn lm vic vi cc chui k t nh lm vic vi cc kiu d liu khc, s dng cc ton t. Before proceeding, you should be able to correctly answer the following questions and do the exercises. Trc khi thc thi, bn c th tr li ng nhng cu hi di y v lm nhng bi tp sau. Explain why namespace was added to C++. Gii thch ti sao namespace c thm vo C++. How do you specify a const member function ? Lm th no m bn ch r cc hm thnh vin hng ? The mutable modifier allows a library function to be changed by the user of your program. True or false? Sa i mutable cho php hm th vin thay i bi ngi s dng chng trnh ca bn. ng hay sai ? Given this class: class X { int a,b; public: X(int I, int j)( a= I; b=j;} // Create conversion to int here }; Create an integer conversion function that returns the sum of a and b. Cho lp sau: class X 802

{ int a,b; public: X(int I, int j)( a= I; b=j;} // Create conversion to int here }; To mt hm chuyn i m tr v tng ca a v b. A static member variable can be used before an object of its class exists. True or false? Mt bin thnh phn tnh c th dng trc khi i tng ca lp ca n tn ti. ng hay sai ? Given this class Cho lp: class Demo { Int a; public: Explicit demo(int i) { a=I;} Int geta() { return a;} }; Is the following declaration legel? M t sau y ng quy cch hay khng? Demo o =10;

803

14.1. AN OVERVIEW OF THE STANDARD TEMPLATE LIBRARY TNG QUAN V TH VIN GIAO DIN CHUN
Although the Standard Templates Library is large and its syntax is, at times, rather intimidating, it is actually quite easy to use once you understand how it is constructed and what elements it employs. Therefore, before looking at any code examples, an overview of the STL is warranted Mc d th vin chun Templates v cu trc ca n th ln, ti thi im ny th kh l kinh s, n thc s l kh d dng s dng mt ln bn s hiu c n c cu to nh th no v cc thnh phn ca n lm vic th no. Do vy, trc khi xem xt bt k mt on m no, cn phi c 1 ci nhn tng qut v STL. At the core of the Standard Templates Library are three foundational items: containers, algorithms, and iterators . These items work in conjunction with one another to provide off the shelf solutions to a variety of programming problems. Ct li ca th vin chun Templates l 3 thnh phn chnh : phn cha, thut ton, v bin lp. Nhng thnh phn ny lm vic chung vi cc thnh phn khc cung cp cc gii php cho cc vn khc nhau ca chng trnh. Containers are objects that hold other objects. There are several dynamic array, queue creates a queue, and list also defines associative containers, which allow efficient retrieval of values base on keys. For example, the map class defines a map that provides access to values with unique keys. Thus, a map stores a key/value pair and allows a value to be retrieved when its key is given. Phn cha l mt i tng cha cc i tng khc. C mt vi mng ng, queue to mt hng i, v list cng nhn dng phn cha mu, cho php c kh nng phc hi gi tr trn c s t kha. V d, lp map nh ngha mt nh x m cung cp cch truy cp gi tr vi nhng t kha duy nht. V vy, mt nh x ch mt cp t kha/ gi tr v co php gi tr phc hi khi m n nhn c t kha. Each container class defines a set of functions that can be applied to the container. For example, a list container includes functions that insert, delete, and merge elements. A stack includes functions that push and pop values. Mi lp phn cha nh ngha mt hm thit lp m c th cung cp cho phn cha. V d, mt phn cha danh sch bao gm nhng hm co th chn, xa v trn cc thnh phn. Mt stack bao gm hm push v pop cc gi tr.

804

Algorithms act on containers. Some of the services algorithms perform are initializing, sorting, searching, and transforming the contents of containers. Many algorithms operate on a sequence, which is a linear list of elements within a container. Thc hin thut ton trn phn cha. Mt vi thao tc thut ton phc v c khi to, sp xp, tm kim v thay i ni dung ca phn cha. C nhiu thut ton thao tc tun t, l mt danh sch tuyn tnh cc thnh phn trong mt phn cha. Iterators are object that are, more or less, pointers. They give you the ability to cycle through the contents of a container in much the same way that you would use a pointer to cycle through an array. The five types iterators are described in the following table. Bin lp l mt i tng nhiu hn hay t hn con tr. N cung cp cho chng ta kh nng x l theo chu k thng qua ni dung ca phn cha theo mt cch ging nh l bn s dng con tr to x l chu k trong mng. C 5 loi bin lp c m t trong bng di. Iterator Random access Access Allowed Stores and retrieves values. Elements can be accessed randomly Cha v nhn gi tr. Cc thnh phn c th truy xut ngu nhin Bidirectional Stores and retrieves values. Forward and backward moving Cha v nhn gi tr. Di chuyn ti trc v sau Forward Stores and retrieves values. Forward moving only. Ch v nhn gi tr. Ch cho php di chuyn ti. Input Retrieves but does not store values. Forward moving only Phc hi nhng khng lu tr gi tr. Ch cho php di chuyn ti. Output Stores but does not retrieve values. Forward moving only. Cha nhng khng phc hi gi tr. Ch cho php di chuynti. In general, an iterator that has greater access capabilities can be used in place of one that has lesser capabilities. For example, a forward iterator can be used in place of an input iterator.

805

Ni chung, mt bin lp c kh nng truy cp ln c th dng trong trng hp nh hn kh nng ca n. V d, mt bin lp ti c th dng t mt bin lp nhp. Iterators are handled just like pointers. You can increment and decrement using the iterator type defined by the various containers. Nhng bin lp c truy cp ch nh l con tr. Bn c th tng hoc gim s dng loi bin lp c inh ngha bng cc phn cha khc nhau. The STL also supports reverse iterators. Reverse iterators are either bidirectional or random-access iterators that move through a sequence in reverse direction. Thus, if a reverse iterator points to the end of a sequence, incrementing that iterator will cause it to point to one element before the end. STL cng cung cp bin lp o ngc. Bin lp o ngc khng l bin lp hai chiu cng nh l bin lp truy xut ngu nhin, n c duy chuyn tun t theo trnh t o ngc. V vy, nu bin lp o ngc tr n cui ca thao tc, tng gi tr ca bin lp s l nguyn nhn lm n ch n mt thnh phn trc kt thc. When referring to the various iterator types in template descriptions, this book will be use the terms listed in the following table. Khi thng k cc loi bin lp khc nhau trong phn m t template, cun sch ny dng bang in kin di y: Term Bilter Integrator type Bidirectional iterator ( Bin lp hai chiu Forlter Forward iterator Bin lp ti Inlter Input iterator Bin lp nhp Outlter Output iterator Bin lp xut Randlter Random-access iterator Bin lp truy xut ngu nhin

806

In addition to containers, algorithms, and iterators, the STL relies upon several other standard components for support. Chief among these are allocators, predicates, and comparison functions. Thm vo phn cha, thut ton , v bin lp, STL cung cp thm mt vi thnh phn chun na. Ch yu l cp pht, thuc tnh v hm so snh. Each allocation has an allocator defined for it. Allocators manage memory allocation for containers. The default allocators is an object of class allocator, but you can define your own allocations if you need them for specialize applications. For most uses, the default allocator is sufficient. Mi mt vng cp pht c mt nh ngha cp pht cho n. Hm cp pht qun l vng nh cho phn ch. Mc nh hm cp pht l mt i tng ca lp allocator, nhng bn co th nh ngha n bng cch ca bn nu nh bn cn trong nhng trng hp c bit. Several of the algorithms and containers use a special type of function called a predicate. There are two variations of predicates: unary and binary. A unary predicate takes one argument, and a binary predicate has two arguments. These functions return true or false; the precise conditions that make them return true or false are defined by the programmer. In this chapter, when a unary predicate function is used. It will be notated with the type unpred. In a binary predicate, the arguments are always in the order of first, second. For both binary and binary predicates, the arguments will contain values of the same type as the objects being stored by the container Mt vi thut ton v phn cha dng mt loi hm c bit gi thuc tnh. C 2 thuc tnh khc nhau : unary v binary. Thuc tnh unary ch mt i s v thuc tnh binary cha 2 i s. Nhng hm ny tr v ng hay sai; nhng iu kin cho trc lm n tr v gi tr ng hay sai c nh ngha bi ngi lp trnh. Trong chng ny, khi m hm thuc tnh unary c s dng. N s c k hiu vi loi unpred. Trong thuc tnh binary, nhng i c yu cu trc nht v th hai. C 2 thuc tnh unary v binary, cc i s ca n cha gi tr ca cng loi ca i tng c cha trong phn cha. Some algorithms and classes use a special type of binary predicate that compares two elements. Called a comparison function, this type of predicate returns true if its first argument is less than its second. Comparison functions will be notated by the type Comp. Mt vi thut ton v lp dng mt loi thuc tnh binary c bit l so snh 2 thnh 807

phn. Gi hm so snh, mt loi thuc tnh s tr v gi tr dung nu i s 1 nh hn i s th 2. Hm so snh c k hiu bi Comp. In addition to the headers required by the various STL classes, the C++ standard library includes the< utility> and <functional> headers, which provide support for the STL. For example, <utility> contains the definition of the template class pair, which can hold a pair of values. We will make use of pair later in this chapter. Thm vo phn u l yu cu ca cc lp STL khc nhau, th vin C++ chun bao gm <utility> v <functional> headers,h tr cho STL. V d <utility> ch nh ngha ca lp template pair, c th qun l c cp gi tr, Chng ta s dng pair sau trong chng ny. The templates in <functional> help you construct objects that define operator(). These are called function objects, and they can be used in place of function pointers in many places. There are several predefined function objects declared within < functional> . Some are shown in the following table. Templates trong <functional> gip bn xy dng i tng m nh ngha thao tc. Chng c gi hm i tng, v chng c th dng trong hm cont tr nhiu ni. Mt vi hm i tng nh ngha trc c m t trong <functional>. Mt vi hm c trnh by bng di. Plus Negate Less Minus Equal_to Less_equal Multiplies Not_qual_to Logical_and Divides Greater Logical_or Modulus Greater_equal Logical_not

Perhaps the most widely used function object is less, which determines whether the value of one object is less than the value of another. Function objects can be used in place of actual function pointers in the STL algorithms described later. Using function objects rather than function pointers allows the STL to generate more efficient code. However, for the purpose of this chapter, function objects are not need and we wont be using them directly. Although function objects are not difficult, a detailed discussion of function objects is quite lengthy and is beyond the scope of this book. They are something that you will need, however, to get maximum efficiency from the STL. C l vic s dng hm i tng ph bin th kh him, quyt nh gi tr ca i tng ca mt i tng th t hn l gi tr ca i tng khc. Hm i tng c th dng trong hm con tr thc s trong STL thut ton s c m t sau. S dng hm i tng th tt hn hm con tr cho php STL to ra nhiu on m hiu qu. Tuy nhin, v 808

mc ch ca chng ny, hm i tng khng cn v chng ta s khng s dng chng trc tip. Mc d hm i tng khng kh, nhng m t chi tit ca hm th kh l di v vt qu phm vi ca cun sch.Tuy nhien chng th c mt vi th cn thit cho bn t c hiu qu ti a t STL.

EXERCISES As they relate to the STL, what are containers, algorithms, and iterators? Lin quan n STL, phn cha, thut ton, v bin lp l g? What are the two type of predicate? Ci g th c 2 loi thuc tnh ? What are the five types of iterators? K tn 5 loi bin lp ?

THE CONTAINER CLASSES LP CONTAINER


As explained, containers are the STL objects that actually store data. The containers defined by the STL are shown in Table 14-1. also shown are the headers you must include to use each container. The string class, which manages character strings, is also a container, but it is discussed later in this chapter. Theo nh ngha, nhng container l nhng i tng STL lu tr d liu. Nhng container c nh ngha bi STL theo nh trong bng 14-1. Ban u bn phi include s dng tng container. Lp string, dung qun l nhng k t trong chui, cng l mt container, nhng n s c cp n sau trong chng ny. Container Bitset deque list map multimap multiset Description a set of bits a double-ended queue a linear list Stores key/value pairs in which each key is associated with only one value Stores key/value pairs in which one key can be associated with two or more values A set in which each element is not necessarily unique Required header <bitset> <queue> <list> <map> <map > <set>

809

priority_queue queue set stack vector Container Bitset deque list map multimap multiset priority_queue queue set stack vector

A priority queue A queue A set in which each element is unique A stack A dynamic array Description Mt lnh ca bit Hng i 2 u Danh sch tuyn tnh Lu tr cp kha/gi tr m trong mi kha ch c kt hp vi 1 gi tr. Lu tr cp kha/gi tr m trong mt kha c th kt hp vi 2 hay nhiu hn 2 gi tr. Mt lnh m trong mi b phn khng nht thit phi n tr. Mt hng i u tin. Mt hng i Mt lnh m trong cc thnh phn ca lnh phi n tr. Mt ngn xp. Mt mng ng

<queue> <queue> <set> <stack> <vector> Required header <bitset> <queue> <list> <map> <map > <set> <queue> <queue> <set> <stack> <vector>

Because the names of the placeholder types in a template class declaration are arbitrary, the container classes declare typedefed versions of these types. This makes the type names concrete. Some of the most common typedef names are shown in the following table. Bi v tn ca loi gi ch nm trong mt lp chun khai bo ty , do lp container khai bo c typedef thnh nhng kiu tng t. iu ny lm cho kiu t tn c c th. Mt vi tn typedef ph bin c a ra trong bng sau: Typedef Name description size_type An integral type equivalent to size_t reference A reference to an element const_reference A const reference t an element iterator An iterator const_iterator A const iterator reverse_iterator A reverse iterator const_reverse_iterator A const reverse iterator

810

value_type allocator_type key_type key_compare value_compare Typedef Name size_type reference const_reference iterator const_iterator reverse_iterator const_reverse_iterator value_type allocator_type key_type key_compare value_compare

The type of a value stored in a container The type of the allocator The type of a key The type of a function that compares two key The type of a function that compares two values M t Mt s nguyn tng ng size_t Mt tham chiu n 1 phn t Mt tham tr n mt phn t. Mt bin lp Mt tham tr lp Mt bin lp ngc Mt tham tr lp ngc Kiu d liu c lu tr trong container Kiu ca allocator. Kiu ca kha. Loi hm so snh 2 kha Loi hm so snh 2 gi tr.

Although it is not possible to examine each container in this chapter, the next sections explore three representatives: vector, list, and map. Once you understand how these containers work, you will have no trouble using the others. Mc d n khng th khng th xem xt mi loi container trong chng ny, nhng phn sau s nghin cu k 3 i din: vector, list, v map. Mt khi bn hiu c cch m nhng container ny lm vic, th bn s khng gp kh khn g trong vic s dng nhng loi khc.

14.3.VECTORS
Perhaps the most general-purpose of the containers is the vector. The vector class supports a dynamic array. This is an array that can grow as needed. As you know, in C++ the size of an array is fixed at compile time. Although this is by far the most efficient way to implement arrays, it is also the most restrictive, because the size of the array cannot be adjusted at run time to accommodate changing program conditions. A vector solves this problem by allocating memory as needed. Although a vector is dynamic, you can still use the standard array subscript notation to access its elements. C l mc ch chung ca container l vector. Lp vector h tr cho mng ng. y l mt mng m c th pht trin khi cn. Nh bn bit, trong C++ kch thc ca mt mng l th c chnh sa ti thi im bin dch. Mc d, y l mt cch tc ng c hiu qu nht n mng hin thi, nhng n cng l mt hn ch nht, bi v kch thuowcsc ca mng khng th thay i c khi chy cung cp cc iu kin chuyn i chng trnh. Mt vector s gii quyt vn ny bng cch ch nh vng nh khi cn thit. Mc d vector l mng ng nhng bn vn c th s dng mt ch s ca mng chun truy xut n tng phn t ca chng. The template specification for vector is shown here:

811

Template <class T, class Allocator = allocator <T>> class vector

Here T is the type of the data being stored and Allocator specifies the allocator, which defaults to the standard allocator. vector has the following constructors: Nhng c trng chun ca vector c trnh by di y: Template<class T, class Allocator = allocator <T>> class vector y T l kiu d liu c lu tr v allocator ch nh bin tm, ngm nh l bin tm chun. vector c mt s hm thit lp di y:
explicit vector(const Allocator &a= Allocator()); explicit vector(size_type num, const T&val=T() , const Allocator &a = Allocator()); vector (const vector <T Allocator> &obj); template <class Inlter> vector (Inlter start, lnlter end , const Allocator &a=Allocator ());

The first form constructors an empty vector. The second form constructors a vector that has num elements with the value val. The value of val can be allowed to default. The third form constructs a vector that contains the same elements as ob. The fourth form constructs a vector that contains the elements in the range specified by the iterators start and end. Dng u tin to mt vector rng. Dng th 2 to mt vector c num phn t vi gi tr l val. Gi tr ca val c th c php mc nh. Dng th 3 to nn mt vector c nhng phn t ging nh ob. Dng th 4 to mt vector bao gm nhng phn t trong mt dy c ch nh bi bin lp t start n end. Any object that will be stored in a vector must define a default constructor. It must also define the <and == operations. Some compilers might require that other comparison operators be defined. (Because implementations vary, consult your compilers documentation for precise information.) All of the built-in-types automatically satisfy thee requirements. Bt c mt i tng no c lu tr trong vector u phi nh ngha hm khi to mc nh. N cng phi nh ngha ton t < v ==. Mt s chng trnh bin dch cn yu cu nh ngha them mt s ton t so snh khc.(Bi v s thi hnh th a dng, v phc v cho vic tra cu thng tin chnh xc ca trnh bin dch.) Tt c cc kiu khi to t ng i hi 3 yu cu: Although the template syntax looks rather complex, there is nothing difficult about declaring a vector. Here are some examples: vector<int> iv; //creates a zero-length int vector vector<char> cv(5); //creates a 5-element char vector vector<char> cv(5, x) ; //initializes a 5-element char vector vector<int> in2(iv) ; //creates an int vector from an int vector The following comparison operators are defined for vector : ==, <, <=, !=, >, >= Mc d, hnh thc trng c v l kh phc tp nhng thc cht l khng c g kh khn trong vic khai bo mt vector. y l mt vi v d:

812

vector<int> iv; //To mt vector s nguyn c chiu di l 0 vector<char> cv(5); //To mt vector 5 k t vector<char> cv(5, x) ; //Khi to mt vector 5 k t vector<int> in2(iv) ; //To mt vector s nguyn t mt vector s nguyn Nhng ton t so snh c nh ngha cho vector bn di: ==, <, <=, !=, >, >= The subscripting operator [] is also defined for vector. This allows you to access the elements of a vector using standard array subscripting notation. The member functions defined by the vector are shown in Table 14-2 (Again, it is important not to be put off by the syntax.) Some of the most important member functions are size(), begin(), end(), push_back(), insert(), and erase(). The size() function returns the current size of the vector. This function is quite useful because it allows you to determine the size of a vector at run time. Remember, vectors will increase in size as needed, so the size of a vector must be determined during execution, not during compilation.. K hiu ton t [] cng c nh ngha cho vector. Phng thc ny cho php bn truy xut n cc phn t ca vector bng cch s dng k hiu ch s mng chun. Hm thnh phn c nh ngha bi vector c trnh by bng 14-2. Mt s hm thnh phn quan trng l size(), begin(), end(), push_back(), insert(), v erase(). Hm size() tr v kch thc hin thi ca vector. Hm ny th kh hu dng bi v n cho php bn xc nh kch thc ca mt vector khi chy chng trnh. Nh rng, nhng vector s tng kch thc khi cn, v vy kch thc ca mt vector phi c xc nh trong sut qu trnh thc thi, ch khng phi trong qu trnh bin son. The begin() function returns an iterator to the start of the vector. The end() function returns an iterator to the end of the vector. As explained, iterators are similar to pointers, and it is through the use of the begin() and end() functions that you obtain an iterator to the beginning and end of a vector. Hm begin() tr v mt bin lp bt u ca vector. Hm end() tr v mt bin lp kt thc vector. Theo ng ngha ca n, th bin lp ging nh l con tr, v thng qua hm begin() , end() bn c th ly c bin lp bt u v kt thc ca mt vector. template<class lnlter> void assign(lnlter start, lnlter end); Template<class Size, class T) Void assign(Size num, const T &val = T()); Reference at(size_type l); Const_reference at(size_type l) const; Referenc back(size_type l); Const_reference at(size_type l) const; Iterator begin(); Const _iterator begin() const; Size_type capacity() const; Assigns the vector the sequence defined by start and end Assigns the vector num elements of value val Returns a reference to an elements of value val. Returns a reference to the last element in the vector. Returns an iterator to the first the element in the vector. Returns the current capacity of the vector. This is the number of elements it can hold before it will need to allocate more

813

Void clear(); Bool empty() const; Iterator end(); Const_iterator end() const iterator erase(iterator i); Iterator erase(iterator start, iterator end); Reference front(); Const_reference front() const; Allocator_type get_allocator() const; Iterator insert(iterator I, const T&val=T()); Void insert(iterator I, size_type num, const T& val); Template<class lnlter> Void insert(iterator I, lnlter start, lnltr end); Size_type max_size() const; Reference operator[](size_type i) const; Const_reference operator[](size_type i) const; Void pop_back(); Void push_back(cons T&val); Reverse_iterator rbegin(); Const_reverse_iterator rbegin() const; Reverse_iterator rend(); Const_reverse_iterator rend() const; Void reverse (size_type num); Void resize (size_type num, T val =T());

memory. Removes all elements from the vector Returns true if the invoking vector is empty and false otherwise. Returns an iterator to the end of the vector. Removes the element pointed to by i. returns an iterator to the element after the one removed. Removes the elements in the range start to end. Returns an iterator to the element after the last vector. Returns a reference to the first element in the vector. Returns the vectors allocator. Inserts val immediately before the element specified be I. an iterator to the element is returned. Inserts num copies of val immediately before the element specified be i. inserts the sequence defined by start and end immediately before the element specified by i. Returns the maximum number of element that the vector can hold. Returns a reference to the element specified by i. Removes the last element in the vector. Adds an element with the value specified be val to the end of the vector. Returns a reverse iterator to the end of the vector. Returns a reverse iterator to the start of the vector. Sets the capacity of the vector so that it is equal to at least num. Changes the size of the vector to that specified by num. if the vector must be lengthened , elements with the value specified by val are added to the end. Returns the number of elements currently in the vector. Exchanges the elements stored in the invoking vector with those on ob.

Size_type size() const; Vois swap(vector<T, Allocator>&ob)

814

Hm thnh phn template<class lnlter> void assign(lnlter start, lnlter end); Template<class Size, class T) Void assign(Size num, const T &val = T()); Reference at(size_type l); Const_reference at(size_type l) const; Reference back(size_type l); Const_reference at(size_type l) const; Iterator begin(); Const _iterator begin() const; Size_type capacity() const;

M t Gn gi tr cho vector theo trnh t t start n end. Gn gi tr ca val cho num phn t ca vector. Tr v mt tham chiu n mt phn t c ch nh bi i. Tr v mt tham chiu n phn t cui cng ca vector. Tr v mt bin lp ch nh phn t u tin ca vector. Tr v dung lng hin thi ca vector. y l s lng cc phn t m n c th cha trc khi n cn cp pht thm vng nh. Xa tt c cc phn t trong vector. Tr v true nu vector rng v tr v false nu ngc li. Tr v mt bin lp kt thc mt vector. Xa mt phn t c ch bi i. Tr v mt bin lp ch n phn t sau phn t c xa. Xa nhng phn t trong dy t start n end. Tr v mt bin lp ch n phn t sau cng ca vector. Tr v mt tham chiu n phn t u tin ca vector. Tr v vng nh c cp pht cho vector. Chn val trc tip vo trc thnh phn c ch nh bi i. bin lp ch n phn t c tr v. Chn num val mt cch trc tip trc phn t c ch nh bi i. Chn chui xc nh t start n end trc tip trc mt phn t c ch nh bi i. Tr v s lng phn t ln nht m vector c th cha. Tr v mt tham chiu n phn t c ch nh bi i.

Void clear(); Bool empty() const; Iterator end(); Const_iterator end() const iterator erase(iterator i); Iterator erase(iterator start, iterator end); Reference front(); Const_reference front() const; Allocator_type get_allocator() const; Iterator insert(iterator I, const T&val=T()); Void insert(iterator I, size_type num, const T& val); Template<class lnlter> Void insert(iterator I, lnlter start, lnltr end); Size_type max_size() const; Reference operator[](size_type i) const; Const_reference operator[](size_type i) const; 815

Void pop_back(); Void push_back(cons T&val); Reverse_iterator rbegin(); Const_reverse_iterator rbegin() const; Reverse_iterator rend(); Const_reverse_iterator rend() const; Void reverse (size_type num); Void resize (size_type num, T val =T());

Xa phn t cui cng trong vector. Thm vo mt phn t c gi tr val vo cui ca vector. Tr v bin lp nghch ch im kt thc ca vector. Tr v mt bin lp ngch ch im bt u ca vector. Thit lp kch thc ca vector t nht l bng num. Chuyn i kch thc ca vector c xc nh bi num. Nu nh kch thc ca vector tng ln th cc phn t c gi tr val s c thm vo cui vector. Tr v s lng cc phn t hin thi ca trong vector. Chuyn i nhng phn t c lu trong vector hin thi vi nhng phn t trong ob.

Size_type size() const; Vois swap(vector<T, Allocator>&ob)

The push_back() function puts a value onto the end of the vector. If necessary, the vector is increased in length to accommodate the new element. You can also add elements to the middle using insert(). A vector can also be initialized. In any event, once a vector contains elements, you can use array subscripting to access or modify those elements. You can remove elements from a vector using erase(). Hm push_back() a mt gi tr vo cui vector. Nu cn thit th vector tng di ln cung cp cho phn t mi. Bn cng c th thm nhng phn t vo gia s dng insert(). Mt vector cng c th c khi to. Trong bt k cng vic no, mt khi mt vector cha phn t, th bn u c th s dng ch s mng truy xut hoc sa i nhng phn t . Bn c th xa nhng phn t trong vector bng cch s dng hm erase(). EXAMPLES: Here is a short example that illustrates the basic operation of a vector. y l mt chng trnh th hin nhng thao tc cn bn ca mt vector. #include "stdafx.h" // Vector basics. #include <iostream> #include <vector> using namespace std; int main() { 816

vector<int> v; // create zero-length vector int i; // display original size of v cout << "Size = " << v.size() << endl; /* put values onto end of vector -vector will grow as needed */ for(i=0; i<10; i++) v.push_back(i); // display current size of v cout << "Current contents:\n"; cout << "Size now = " << v.size() << endl; // display contents of vector for(i=0; i<v.size(); i++) cout << v[i] << " "; cout << endl; /* put more values onto end of vector -again, vector will grow as needed */ for(i=0; i<10; i++) v.push_back(i+10); // display current size of v cout << "Size now = " << v.size() << endl; // display contents of vector cout << "Current contents:\n"; for(i=0; i<v.size(); i++) cout << v[i] << " "; cout << endl; // change contents of vector for(i=0; i<v.size(); i++) v[i] = v[i] + v[i]; // display contents of vector cout << "Contents doubled:\n"; for(i=0; i<v.size(); i++) cout << v[i] << " "; cout << endl; return 0; } Lets look at this program carefully. In main(), an integer vector called v is created. Since no initialized is used, it is an empty vector with an initial capacity of zero. That is, it is a zero-length vector. The program confirms this by calling the size() member 817

function. Next ten elements are added to the end of v with the member function push_back(). This causes v to grow in order to accommodate the new elements. As the output shows, its size after these additions is 10. next, the contents of v are displayed. Notice that the standard array subscripting notation is employed. Next, ten more elements are added and v is automatically increased in size to handle them. Finally, the values of vs elements are altered using standard subscripting notation. Hy xem xt k chn trnh ny. Trong hm main(), to mt vector cc s nguyn c gi l v. K t khi n khng c khi to, th n l mt vector rng vi s phn t bng 0. c ngha l di ca vector cng bng 0. Chng trnh xc nhn iu ny bng cch gi hm thnh phn size(). Tip n, 10 phn t c thm vo cui vector v bng hm thnh phn push_back(). iu ny lm cho kch thc ca tng ln cung cp cho cc phn t mi. Khi xut, kch thc ca n sau khi thm l 10. K tip, ni dung ca v c xut ra ngoi mn hnh. Ch rng ch s kiu mng chun th c s dng. Tip theo, thm 10 phn t na v v t ng tng kch thc ln x l chng. Cui cng, nhng gi tr ca cc phn t ca v c sa i bng cch s dng ch s ca mng chun. There is one other point of interest in this program. Notice that the loops that displays the contents if v use as their target v.size(). One of the advantages that vectors have over arrays is that it is impossible to find the current size of a vector. As you can imagine, this is quite useful in a variety of situations. C mt con tr khc trong chng trnh ny. Ch rng nhng vng lp xut ni dung ra mn hnh nu nh v s dng nh l nh v ca chng v.size(). Mt trong s nhng thun tin ca vector hn mng l: mng khng th bit c kch thc hin thi ca mt vector. Khi bn hnh dung rng, iu ny th kh hu dng trong nhiu tnh hung. As you know, arrays and pointers are tightly linked in C++. An array can be accessed either through subscripting or through a pointer. The parallel to this in the STL is the link between vectors and iterators. You cac access the members of a vector by using an iterator. The following example shows both of these approaches. Nh bn bit, mng v con tr c mi quan h cht ch vi nhau trong C++. Mt mng c th c truy xut thng qua ch s, hoc mt con tr. S tng ng ny trong STL l mi quan h gia bin lp (ch s) v vector. Ban c th truy xut n cc thnh phn ca mt vector bng s dng mt bin lp. v d sau y s th hin c 2 phng php. // Access a vector using an iterator. #include <iostream> #include <vector> using namespace std; int main() { vector<int> v; // create zero-length vector int i;

818

// put values into a vector for(i=0; i<10; i++) v.push_back(i); // can access vector contents using subscripting for(i=0; i<10; i++) cout << v[i] << " "; cout << endl; // access via iterator vector<int>::iterator p = v.begin(); while(p != v.end()) { cout << *p << " "; p++; } return 0; } The output from this program is: 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 In this program, the vector is initially created with zero length. The push_back() member function puts values onto the end of the vector, expanding its size as needed. Trong chng trnh ny, vector ban u c to c chiu di 0. Hm push_back() a cc gi tr vo cui vector, m rng kch thc ca n theo yu cu. Notice how the iterator p is declared. The type iterator is defined by the the container classes. Thus, to obtain an iterator for a particular container, you will use a declaration similar to that shown in the example: simply quality iterator with the name of the container. In the program, p is initialized to point to the start of the vector by using the begin() member function. This function returns an iterator to the start of the vector. This iterator can then be used to access the vector an element at a time by incrementing it as needed. This process is directly parallel to the way a pointer can be used to access the elements of an array. To determine when the end of the vector has been reached, the end() member function is employed. This function returns an iterator to the location that I some last element in the vector. Thus, when p equals v.erase(), the end of the vector has been reached. Ch cch khai bo con tr p. Kiu iterator c nh ngha bi lp container. Thay v, mt container thng thng cha mt bin lp, bn s s dng mt khai bo tng t, n c a ra trong v d: c trng c bn ca iterator lin quan n tn ca contianer. Trong chng trnh, p ban u ch n v tr ban u ca vector nh hm thnh phn l begin(). Hm ny tr v bin lp ch n v tr u tin ca vector. Bin lp c th c s dng truy xut ln lt tng phn t ca vector bng cch tng dn n ln 1. Qu trnh ny th tng t nh cch s dng mt con tr truy xut cc phn t ca mt mng. chng minh iu ny, khi kt thc vector, th s dng hm end(). Hm ny tr v mt bin lp ch v tr phn t cui cng trong vector. Thay v dng p tng t nh

819

v.earase(), th tin n im cui ca vector. 3. In addition to putting new values on the end of vector, you can insert elements into the middle using the insert() function. you can also remove elements using erase(). The following program demonstrates insert() and erase(). 3. Thm vo gi tr vo cui vector, bn c th chn nhng phn t vo gia bng cch s dng hm insert(). Bn cng c th xa s dng erase(). Chng trnh sau y th hin hm insert() v erase(). // Demonstrate insert and erase. #include <iostream> #include <vector> using namespace std; int main() { vector<int> v(5, 1); // create 5-element vector of 1s int i; // display original contents of vector cout << "Size = " << v.size() << endl; cout << "Original contents:\n"; for(i=0; i<v.size(); i++) cout << v[i] << " "; cout << endl << endl; vector<int>::iterator p = v.begin(); p += 2; // point to 3rd element // insert 10 elements with value 9 v.insert(p, 10, 9); // display contents after insertion cout << "Size after insert = " << v.size() << endl; cout << "Contents after insert:\n"; for(i=0; i<v.size(); i++) cout << v[i] << " "; cout << endl << endl; // remove those elements p = v.begin(); p += 2; // point to 3rd element v.erase(p, p+10); // remove next 10 elements // display contents after deletion cout << "Size after erase = " << v.size() << endl; cout << "Contents after erase:\n"; for(i=0; i<v.size(); i++) cout << v[i] << " "; cout << endl; 820

return 0; } 4. Here is an example that uses a vector to store objects of a programmer-defined class. Notice that the class defines the default constructor and that overloaded versions of < and == are provided. Remember, depending upon how your compiler implements the STL, other comparison operators might need to be defined y l mt v d s dng mt vector lu tr i tng ca class do ngi lp trnh nh ngha. Ch rng class nh ngha hm thit lp mc nh v cung cp ton t < v == c np chng. Nh rng, da trn chng trnh bin dch ca bn thc thi STL, m cc ton t so snh khc c th cn c nh ngha. // Store a class object in a vector. #include <iostream> #include <vector> using namespace std; class Demo { double d; public: Demo() { d = 0.0; } Demo(double x) { d = x; } Demo &operator=(double x) { d = x; return *this; } double getd() { return d; } }; bool operator<(Demo a, Demo b) { return a.getd() < b.getd(); } bool operator==(Demo a, Demo b) { return a.getd() == b.getd(); } int main() { vector<Demo> v; int i; 821

for(i=0; i<10; i++) v.push_back(Demo(i/3.0)); for(i=0; i<v.size(); i++) cout << v[i].getd() << " "; cout << endl; for(i=0; i<v.size(); i++) v[i] = v[i].getd() * 2.1; for(i=0; i<v.size(); i++) cout << v[i].getd() << " "; return 0; } The output from this program is shown here: Kt qu ca chng trnh ny l:
0 0 0.333333 0.7 1.4 0.666667 1 1.33333 1.66667 2 2.33333 2.66667 3 2.1 2.8 3.5 4.2 4.9 5.6 6.3

EXERCISES: Try examples just shown, marking small modifications and observing their effects. in example 4, both a default (i.e, parameterless) and a parameterized constructor were defined for Demo. Can you explain why this is important? Here is a simple Coord class. Write a program that stores objects of type Coord in a vector. (Hint : Remember to define the < and == operators relative to Coord.) Kim tra li cc v d va mi c a ra, lm nhng s thay i nh v xem xt nhng nh hng ca chng. Trong v d 4, c hm khi to mc nh v khi to c tham s u c nh ngha cho demo. Bn c th gii thch ti sao c iu ny? y l lp Coord. Vit mt chng trnh lu tr cc i tng dng Coord trong mt vector. (Hng dn: nh ngha cc ton t < v == lin quan n Coord.) class Coord { public: int x, y; Coord() { x = y = 0; } Coord(int a, int b) { x = a; y = b; } };

822

LISTS - DANH SCH


The list class supports a bidirectional, linear list. Unlike a vector, which supports random access, a list can be accessed sequentially only.because, lists are bidirectional, they can be accessed front to back or back to front. Danh sch lp h tr danh sch 2 chiu, tuyn tnh. Khng ging nh vector, h tr truy xut mt cch ngu nhin, mt danh sch ch c th c truy xut mt cch tun t. Bi v, danh sch th 2 chiu, chng c th c truy xut t u n cui, hoc ngc li. The list class has this this template specification: Template<class T, class Allocator = allocator<T>> class list Here T is the type the type of data stored in the list. The allocator is specified be Allocator, which defaults to the standard allocator. This class has the following constructors: Mt danh sch lp c khai bo nh sau: Template <class T, class Allocator = Allocator<T>> class list. y T l kiu d liu c lu tr trong danh sch. Allocator c ch nh bi Allocator, n mc nh allocator chun. Lp ny c cu trc nh sau: Explicit list (const Allocator &a= Allocator()); Explicit list (size_type num, const T&val=T(), const Allocator &a= Allocator()); List (const list <T, Allocation> &ob); Template<class lnlter> list (lnlter start, lnlter end, const Allocator &a=Allocator()); The first form constructs an empty list. The second form constructs a list that has num elements with the value val, which can be allowed to default. The third form constructs a list that contains the same elements as ob. The fourth form constructs a list that contains the elements in the range specified by the iterators start and end. The following comparison operators are defined for list: ==, <, <=, !=, >, >= Dng u tin xy dng nn mt danh sch rng. Dng th 2 xy dng mt danh sch c num phn t c gi tr l val, n c th c mc nh. Dng th 3 xy dng mt danh sch bao gm nhng thnh phn ging nh ob. Dng th 4 to nn mt danh sch bao gm nhng thnh phn trong dy c nh ngha bi bin lp t start n end. Nhng ton t so snh c nh ngha cho danh sch: ==, <, <=, !=, >, >= The member functions defined for list are shown in Table 14-3. Like a vector a list can have elements put into it with the push_back() function. you can put elements on the front o the list by using push_front(), and you can insert an element into the middle of a list by using insert(). You can use splice() to join two lists, and you can merge one list into another by using merge(). 823

Nhng hm thnh phn c nh ngha cho list c trnh by trong bng 14-3. Ging nh mt vector, mt danh sch c nhng thnh c a vo vi hm push_back(). Bn c th chn mt phn t vo gia ca mt danh sch bng hm insert(). Bn c th s dng splice() nhp 2 danh sch li vi nhau, v bn c th trn danh sch ny vi mt danh sch khc bng s dng hm merge(). Any data type that will be held in a list must define a default constructor. It must also define the various comparison operators. At the time of this writing, the precise requirements for an object that will be stored in a list vary from compiler to compiler and are subject to change, so you will need to check your compilers documentation. Bt k loi d liu no c lu trong danh sch u phi nh ngha hm khi to mc nh. Bn cng phi nh ngha thm nhng ton t so snh khc. Ti thi im ny ca bi vit, iu kin cho mt i tng s c lu trong mt danh sch thay i t chng trnh bin dch ny sang chng trnh bin dch khc, v l mt ch th chuyn i, v vy bn s cn kim tra chng trnh bin dch ca mnh. Member function Template<class lnlter> Void assign(lnlter start, lnlter end); Template<class Size, class T> Void assign (Size num, const T&val=T()); Reference back(); Const_reference back()const; Iterator begin(); Void clear(); Bool empty()const; Iterator end(); Const_iterator end() const; Iterator erase(iterator i); Iterator erase(iterator start, iterator end); Reference front(); Const_referencefront() const; Allocator_type get_allocator() const; Iterator insert(iterator i, const T &val=T()); Void insert(iterator I, size_type num, const T&val=T()); Template<class lnlter> 824 Description Assigns the list the sequence defined by start and end. Assigns the list num elements of value. Returns a reference to the last element in the list. Return an iterator to the first element in the list,. Removes all elements from the list. Returns true if the invoking list is empty and false otherwise. Returns an iterator to the end of the list. Removes the element pointed to by i. returns an iterator to the element after the one removed. Removes the element in the range start to end. Returns an iterator to the element after the last element removed. Returns a reference to the first element in the list. Returns the lists allocator. Inserts val immediately before the element specified by i. An iterator to the element is returned. Inserts num copies of valimmediately before the element specified by i. Inserts the sequence defined by start and

Void insert(iteratir i, lnlter start, lnlter end); Size_type max_size max_size() const; Void merge(list<T. allocator>&ob); Template<class Comp> Void merge(<list<T, Allocator>&ob, Comp cmpfn);

Void pop_back(); Void pop_front(); Void push_back(const T&val); Void push_front(const T&val); Reverse_iterator rbegin(); Const_reverse_iterator rbegin() const; Void remove(const T&val); Template <class UnPred> Void remove_if(UnPred pr); Reverse_iterator rend(); Const_reverse_iterator rend() const; Void resize(isze_type num, T val=T());

Void reverse(); Size_type size() const; Void sort (); Template <class Comp> Void sort(conm cmpfn); Void spilice(iterator i, list <T, allocator> &ob); Void splice(iterator i, list <allocator> &ob, iterator el); Void spice(iterator I, list<T, Allocator>&ob, iterator start, iterator end).

end immediately before the element specified by i. Returns the maximum number of elements that the list can hold. Merges the ordered list contained in ob with the invoking ordered list. The result is ordered. After the merge, the list contained in ob is empty. In the second form, a comparison function can be specified to determine whether the value of one element is less than that of another. Removes the last element in the list. Removes the first element in the list. Adds an element with the value specified by val to the end of the list. Adds an element with the value specified by val to the front of the list. Returns a reverse iterator to the end of the list. Removes elements with the value val from the list. Removes elements for which the unary precidate pr is true. Returns a reverse iterator to the start of the list. Changes the size of the list to that specified by num. if the list must be lengthened, elements with the value specified by val are added to the end. Reverses the invoking list. Returns the number of elements currently in the list. Sorts the list. The second form sorts the list using the comparison function cmpfn to determine whether the value of one element is less than that of another. Inserts the contents of ob into the invoking list at the location pointed to by i. after the operation ob is empty. Removes the element pointed to by el from the list ob and stores it in the invoking list at the location pointed to by i. Removes the range defined by start and end from ob and stores it in the invoking list beginning at the location pointed to by i.

825

Void swap(list<T, Allocator> &ob); Void unique(); Template<class BinPred> Void unique(BinPred pr); Hm thnh vin Template<class lnlter> Void assign(lnlter start, lnlter end); Template<class Size, class T> Void assign (Size num, const T&val=T()); Reference back(); Const_reference back()const; Iterator begin(); Void clear(); Bool empty()const; Iterator end(); Const_iterator end() const; Iterator erase(iterator i); Iterator erase(iterator start, iterator end); Reference front(); Const_referencefront() const; Allocator_type get_allocator() const; Iterator insert(iterator i, const T &val=T()); Void insert(iterator I, size_type num, const T&val=T()); Template<class lnlter> Void insert(iteratir i, lnlter start, lnlter end); Size_type max_size max_size() const; Void merge(list<T. allocator>&ob); Template<class Comp> Void merge(<list<T, Allocator>&ob, Comp cmpfn); 826

Exchanges the elements stored in the invoking list with those in ob. Removes duplicate elements from the invoking list. The second form uses pr to determine uniqueness. M t Gn gi tr cho danh sch theo trnh t t bt u n kt thc. Gn num phn t ca danh sch bng gi tr value. Tr v mt tham chiu n phn t cui cng trong danh sch. (Ch n phn t cui cng ca danh sch.) Tr v mt bin lp ch n phn t u tin ca danh sch. (Ch n phn t u tin danh sch.) Xa tt c cc phn t trong danh sch. Tr v true nu danh sch rng, v false nu ngc li. Tr n cui danh sch. Xa phn t c ch nh bi i. Ch n phn t k sau phn t c xa. Xa nhng phn t t v tr start n end. Ch n phn t k sau phn t cui cng c xa. Tr v mt tham chiu ch n phn t u tin ca danh sch. Tr v vng nh c cp pht cho danh sch. Chn gi tr val mt cch trc tip vo trc phn t c ch nh bi i. Tr v mt bin lp ch n phn t ny. Chn num gi tr val trc tip trc phn t c ch nh bi i. Chn theo trnh t c xc nh t start n end trc tip trc phn t c ch nh bi i. Tr v s lng cc phn t m danh sch c th gi. Trn danh sch c th t c cha trong ob vi mt danh sch c th t ang gi thc hin phng thc. Kt qu l mt danh sch c th t. Sau khi trn, danh

Void pop_back(); Void pop_front(); Void push_back(const T&val); Void push_front(const T&val); Reverse_iterator rbegin(); Const_reverse_iterator rbegin() const; Void remove(const T&val); Template <class UnPred> Void remove_if(UnPred pr); Reverse_iterator rend(); Const_reverse_iterator rend() const; Void resize(isze_type num, T val=T());

sch cha trong ob l rng. dng th 2, mt hm so snh c nh ngha xc nh gi tr ca mt phn t th nh hn gi tr mt phn t khc. Xa phn t cui cng trong danh sch. Xa phn t u tin trong danh sch. Thm mt phn t c gi tr c xc nh bi val vo cui danh sch. Thm mt phn t c gi tr c xc nh bi val vo u danh sch. Tr v mt bin lp ngc ch n cui danh sch. Xa nhng phn t c gi tr val trong danh sch. Xa nhng m phn t nu nh pr l true. Tr v mt bin lp ngc ch n u danh sch. Thay i kch thc ca danh sch c xc nh bi num. Nu nh danh sch di ra thm, th nhng phn t c gi tr val c thm vo cui. o ngc danh sch ang gi thc hin phng thc. Tr v s lng cc phn t hin ti trong mng. Sp xp danh sch. Dng th 2 s dng hm so snh cmpfn xc nh gi tr ca phn t ny c nh hn gi tr ca phn t kia khng. Chn ni dung ca ob vo trong danh sch ang gi thc hin phng thc ti v tr c ch nh bi i. Sau thao tc ny ob l rng. Xa phn t c ch nh bi el trong danh sch ob v lu tr n vo trong danh sch ang gi thc hin phng thc ti v tr c ch nh bi i. Xa mt mng phn t bt u t v tr start n v tr end v lu n vo trong danh sch ang gi thc hin phng thc bt u t v tr i. Hon v ni dung nhng phn t c lu tr trong danh sch ang gi thc hin phng thc vi nhng phn t trong ob. Xa nhng phn t trng trong danh sch

Void reverse(); Size_type size() const; Void sort (); Template <class Comp> Void sort(conm cmpfn); Void spilice(iterator i, list <T, allocator> &ob); Void splice(iterator i, list <allocator> &ob, iterator el); Void splice(iterator I, list<T, Allocator>&ob, iterator start, iterator end). Void swap(list<T, Allocator> &ob); Void unique(); 827

Template<class BinPred> Void unique(BinPred pr); EXAMPLES: Here is a simple example of a list:

ang gi thc hin phng thc. Dng 2 s dng pr nh ngha s duy nht.

1. y l mt v d n gin ca mt danh sch:

// List basics. #include <iostream> #include <list> using namespace std; int main() { list<char> lst; // create an empty list int i; for(i=0; i<10; i++) lst.push_back('A'+i); cout << "Size = " << lst.size() << endl; list<char>::iterator p; cout << "Contents: "; while(!lst.empty()) { p = lst.begin(); cout << *p; lst.pop_front(); } return 0; } The output produced by this program is shown here.
Kt qu c sn xut bi chng trnh ny c cho thy y.

Size = 10 Contents: ABCDEFGHIJ This program creates a list of characters. First, an empty list object is created. Next, ten characters, the letters A througn J, are put into the list. This is acconmplished with the push_back() function, which puts each new value on the end of the existing list. Next, the size of the list is pisplayed. Then, the contents of the list are output by repeatedly obtaining, displaying, and then removing the first element in the list. This process stops when the list is empty. Chng trnh ny to ra mt danh sch nhng c tnh. u tin, mt i tng list rng c to ra. Tip theo, mi c tnh, nhng mu t t A qua J, c t vo trong danh 828

sch. N c hon thnh vi hm push_back(), mang mi gi tr mi vo v tr kt thc ca danh sch hin hu. Tip theo, kch thc ca danh sch c trnh by. Ri, ni dung ca danh sch c gi ra bi nhiu ln s dng, trnh by, v sau loi b phn t u tin trong danh sch. Qu trnh ny dng li khi danh sch rng. In the previous example, the list was emptied as it aws traversed. This is, of course, not necessary. For example, the loop that displays the list could be recorded as shown here.
2. Trong v d trc y, danh sch tr nn rng nh n c xt ton b. iu ny, tt nhin, khng cn thit. Chng hn, vng m trnh by danh sch c th c lu gi nh c cho thy y.

list<char>::iterator p = lst.begin(); while(p != lst.end()) { cout << *p; p++; } Here the iterator p is initialized to point to the start of the list. Each time through the loop, p is incremented, causing it to point to the next lelement. The loop ends when p points to the end of the list. y iterator p c khi to tr vo v tr bt u ca danh sch. Mi ln thng qua vng,p tng tr s, l nguyn nhn vic to ra n tr vo phn t k tip. Vng kt thc khi p tr vo v tr kt thc ca danh sch. 3. Because lists are bidirectional, elements can be put on a list either at the front or at the back. For example, the following program creates two lists, with the first being the reverse of the second. 3. V nhng danh sch l hai chiu, nhng phn t c th c mang mt danh sch ti mt trc hay pha sau. Chng hn, chng trnh sau y to ra hai danh sch lit k, vi danh sch u tin l s nghch o ca danh sch th hai. // Elements can be put on the front or end of a list. #include <iostream> #include <list> using namespace std; int main() { list<char> lst; list<char> revlst; int i; for(i=0; i<10; i++) lst.push_back('A'+i); cout << "Size of lst = " << lst.size() << endl; cout << "Original contents: ";

829

list<char>::iterator p; /* Remove elements from lst and put them into revlst in reverse order. */ while(!lst.empty()) { p = lst.begin(); cout << *p; lst.pop_front(); revlst.push_front(*p); } cout << endl << endl; cout << "Size of revlst = "; cout << revlst.size() << endl; cout << "Reversed contents: "; p = revlst.begin(); while(p != revlst.end()) { cout << *p; p++; } return 0; } This program produces the following output. Chng trnh ny xut kt qu sau y. Size of list = 10 Original contents: ABCDEFGHIJ Size of revlst = 10 Reversed contents: JIHGFEDCBA The program, the list is reversed by removing elements from the start of lst and pushing them onto the front of revlst. This causes the elements to be stored in the reverse order in revlst. Trong chng trnh, danh sch c o ngc bi vic loi b nhng phn t bt u ca 1st v y h ln trn mt trc ca revlst. Vic ny gy ra nhng phn t s c ct gi vi th t ngc trong revlst. 4. You can sort a list by calling the sort() member function. the following program creates a lost of random characters and then pits the list intosorted order. 4. Bn c th phn loi mt danh sch bng cch gi chc nng thnh vin sort(). Chng trnh sau y to ra mt danh sch nhng c tnh ngu nhin v sau t danh sch vo trong th t c phn loi.

830

// Sort a list. #include <iostream> #include <list> #include <cstdlib> using namespace std; int main() { list<char> lst; int i; // create a list of random characters for(i=0; i<10; i++) lst.push_back('A'+ (rand()%26)); cout << "Original contents: "; list<char>::iterator p = lst.begin(); while(p != lst.end()) { cout << *p; p++; } cout << endl << endl; // sort the list lst.sort(); cout << "Sorted contents: "; p = lst.begin(); while(p != lst.end()) { cout << *p; p++; } return 0; } Here is sample out put produced by the program. y l kt qu xut ra bi chng trnh. Original contents : PHQGHUMEAY Sorted contents: AEGHMPQUY 5. One ordered list can be mergerd with another. The result is an ordered list that contains the contents of the two original lists. The new list I sleft in the incoking list and the second list is left empty. This example merges two lists. The first contains the letters ACEGI and the second BDFHJ. These lists are then merged to produce the sequence BCDEFGHIJ.

831

5. Mt danh sch th t c th c ha trn vi danh sch khc. Kt qu l mt danh sch th t m cha ng ni dung ca c hai danh sch nguyn bn. Danh sch mi c li trong danh sch ko theo v danh sch th hai trng rng. V d ny ha trn hai danh sch. Danh sch u tin cha ng nhng k t ACEGI v danh sch th hai l BDFHJ . Nhng danh sch ny c ha trn ri xut ra chui ABCDEFGHIJ. // Merge two lists. #include <iostream> #include <list> using namespace std; int main() { list<char> lst1, lst2; int i; for(i=0; i<10; i+=2) lst1.push_back('A'+i); for(i=1; i<11; i+=2) lst2.push_back('A'+i); cout << "Contents of lst1: "; list<char>::iterator p = lst1.begin(); while(p != lst1.end()) { cout << *p; p++; } cout << endl << endl; cout << "Contents of lst2: "; p = lst2.begin(); while(p != lst2.end()) { cout << *p; p++; } cout << endl << endl; // now, merge the two lists lst1.merge(lst2); if(lst2.empty()) cout << "lst2 is now empty\n"; cout << "Contents of lst1 after merge:\n"; p = lst1.begin(); while(p != lst1.end()) {

832

cout << *p; p++; } return 0; } The output produced by this program is shown here: Kt qu ca chng trnh ny nh sau: Contents of lst1: ACEGI Contents of lst2: BDFHJ Lst2 is now empty Content of lst1 after merge: ABCDEFGHIJ 6. Here is an example that uses a list to store objects of type. Project, which is a class that helps manage software projects. Notice that the <, >, != and == operators are overloaded for objects of type project. These are the operators that were required by Microsofts Visual C++ 5( the compiler used to test the STL examples in this chapter). Other compilers might require you to overload additional operators. The STL uses these functions to determine the ordering and equality of container. Even through a list is not an ordered container, it still needs a way to compare elements when searching, sorting, or merging. 6. y l mt v d m s dng mt danh sch ct gi nhng i tng kiu Project, m mt lp tr gip qun l nhng d n phn mm. Ch cc ton t <, >,!=, v == b trn cho nhng i tng ca kiu Project. y l nhng ton t m c i hi bi Microsofts Visual C++5 (trnh bin dch thng kim tra nhng v d STL trong chng ny). Nhng trnh bin dch khc c l yu cu bn b sung nhng ton t qu ti. STL s dng nhng chc nng ny xc nh trt t iu chnh v quyn bnh ng ca nhng i tng trong mt vng cha. Mc d mt danh sch khng l mt vng cha th t. N vn cn cn mt cch so snh nhng phn t khi tm kim, chn hay ha trn. #include "stdafx.h" #include <iostream> #include <list> #include <cstring> using namespace std; class Project { public: char name[40]; int days_to_completion; Project() {

833

strcpy(name, ""); days_to_completion = 0; } Project(char *n, int d) { strcpy(name, n); days_to_completion = d; } void add_days(int i) { days_to_completion += i; } void sub_days(int i) { days_to_completion -= i; } bool completed() { return ! days_to_completion; } void report() { cout << name << ": "; cout << days_to_completion; cout << " days left.\n"; } }; bool operator<(const Project &a, const Project &b) { return a.days_to_completion < b.days_to_completion; } bool operator>(const Project &a, const Project &b) { return a.days_to_completion > b.days_to_completion; } bool operator==(const Project &a, const Project &b) { return a.days_to_completion == b.days_to_completion; } bool operator!=(const Project &a, const Project &b) {

834

return a.days_to_completion != b.days_to_completion; } int main() { list<Project> proj; proj.push_back(Project("Compiler", 35)); proj.push_back(Project("Spreadsheet", 190)); proj.push_back(Project("STL Implementation", 1000)); list<Project>::iterator p = proj.begin(); /* display projects */ while(p != proj.end()) { p->report(); p++; } // add 10 days to 1st project p = proj.begin(); p->add_days(10); // move 1st project to completion do { p->sub_days(5); p->report(); } while (!p->completed()); return 0; } The output from this program is shown here. Kt qu ca chng trnh nh sau: Compiler: 35 days left Spreadsheer: 190 days left. STl Implementation: 1000 days left Compiler: 40 days left. Compiler: 35 days left. Compiler: 30 days left. Compiler: 25 days left. Compiler: 20 days left. Compiler: 15 days left. Compiler: 10 days left. Compiler: 5 days left. Compiler: 0 days left. 835

EXERCISE: Experiment with the examples, trying minor varations. In example 1, the list was emptied in the process of displaying it. In example 2, you saw another way to traverse a list that does not destroy it. Can you think of another way to traverse a list without emptying it? Show that your solution works by substituting it into the program in Example 1. Expand Example 6 by creating another list of projects that consists of the following: 1. Th nghim nhng v d, th nhng s bin i ph Trong v d 1, danh sch c tr nn rng trong qu trnh trnh by n. Trong V d 2, bn nhn thy cch khc duyt qua mt danh sch m khng ph hy n. Bn c th ngh cch khc duyt qua mt danh sch m khng lm rng n khng? Ch ra gii php lm vic ca bn bi vic thay th n vo trong chng trnh trong v d 1. 3. M rng v d 6 to ra mt danh sch lit k nhng d n m gm c s sau v thi gian: Project Database Mail merge COM objects D n Database Mail merge COM objects 50 300 Days to Completion 780 50 300 Ngy hon thnh 780 tip

Next, sort both lists and then merge them together. Display the final result Tip theo, phn loi c hai danh sch v sau ha trn chng vi nhau. Trnh by kt qu cui cng.

14.4. MAPS - BAN :


The map class supports an associative container in which unique keys are mapped with values. In essence, a key is simply a name that you give to a value. Once a value has been stored,you can retrieve it by using its key. Thus , in its most general senes a map is a list of key/ value pairs. The power of a map is 836

that you can look up a value given its key. For example, you could define a map that uses a persons name as its key and stores that persons telephone number as its value. Associative container are becoming more popular in programming. Ban lp h tr mt d liu lin quan trong chia khoa duy nht nao c ve trong ban vi nhng gia tri. Tht cht, chia khoa n gian la mt tn ma ban a vao mt gia tri. Mt ln mt gia tri c ct gi, ban co th khi phuc c no bi mt chia khoa. Nh vy, trong xu th chung nht ban la mt danh sach chia khoa/ gia tri. im manh cua ban la ban co th nhin thy gia tri c a cho chia khoa o. Vi du, ban co th inh nghia mt ban s dung tn nh chia khoa va a nhng gia tri vao la nhng con s in thoai. Lin kt cha tr nn thng dung hn trong lp trinh. As mentioned, a map can hold only unique keys. Duplicate keys are not allowed. To create a map that allows nonunique keys use mulitimap. Nh a cp mt ban gi co th gi duy nht mt chia khoa. Nhng chia khoa ban sao khng c cho phep. tao tra ban ma cho phep s dung cac chia khng duy nht mulitimap. The map container has the following template specification: Ban cha co khung mu nh sau: Template <class Key, class T, class Comp=less<Key>, class Allocator = allocator<T> class map Here Key is the data type of the keys, T is the data type of the values being stored (mapped), and Comp is a function that compares two keys. This defaults to the standard less() utility function object. Allocator is the allocator (which defaults to allocator). O y key la d liu nhng chia khoa, T la d liu cua nhng gia tri c ct gi (ban ), va Comp la ham dung so sanh hai chia khoa. Nhng xac lp mc inh chun less () la tin ich i tng ham. Allocator la mt b phn( ma chng trinh phn b). The map class has the following constructors: Ban lp c xy dng nh sau: explicit map (const Comp & cmpfn = Comp(), const Allocator & a= Allocator()); map(const map<Key, T , Comp, Allocator> & ob); template<class Inter> map( Inter start, Inter end, const Comp & cmpgfn = Comp(), const Allocator &a = Allocator)); The first form constructors an empty map. The second form constructors a map that contains the same elements as ob. The third form constructs a map that contains the elements in the range specified by the iterators the ordering of the map. u tin la phai xy dng mt biu rng. Th hai xy dng mt ban cha ng cung phn t ob. Th ba t vic xy dng mt ban co cha nhng phn t trong pham vi chi tro bi nhng iterator iu khin ban . In general, any object used as a key must define a default constructor and overload any necessary comparison operators.

837

Noi chung, bt ki i tng nao c s dung nh mt chia khoa phai inh nghia phng thc mc inh va ham chng khi toan t so sanh cn thit. The following comparison operators are defined for map. Nhng toan t so sanh sau c inh nghia cho ban .

==, <, <=, !=, >, >=


The member functions defined by map are shown in Table 14-4. In the descriptions key_type is the of the type of the key and value_type represents pair< Key, T> Nhng chc nng thnh vin c nh ngha bi bn c a vo Bng 14-4. Trong phn m ta key_type la kiu cua nhng chia khoa va value_type trinh baypair< Key, T> Member Function iterator begin(); const_iterator begin() const void clear(); size_type count(const key_type & k) const; bool empty() const; iterator end(); const_iterator end() const; pair<iterator, iterator> equa_range (const key_type & k); pair<const_iterator, const_iterator> equa_range (const key_type & k) const; void erase(iterator i); void arese(iterator strart,iterator end); size_type erase(const key_type & k); iterator find(const key_type &k); const_iterator find(const key_type & k) const; allocator_type get_allocator() const; iterator insert (iterator I, const value_type & val); template<class Inter> void insert(Inter start, Inter end); pair<iterator, bool> void insert(const value_type & val); Description Return an iterator to the first element in the map. Removes all elements form the map. Return the number of times k occours in the map(1 or 0). Return true if the invoking map is empty and false otherwise. Return an iterator to the end of the map. Return a pair of iterator that point to the first and last elements in the map that contain the specified key. Removes the element pointed to by i. Removes the elements in the range start to end. Removes elements that have keys with the value k. Returns an iterator to the specified key. If the key is not found, an iterator to the end of the map is returned. Return the maps allocator. Insert val at or after the element specified by i. An iterator to the element is returned. Inserts a range of elements Iterator val into the invoking map. An iterator to the element is returned. The element is inserted only if it does not already exist, if the element was insert, pair<iterator, true> is returned.

838

key_compate key_comp()const; iterator lower_bound(const key_type & k); const_iterator lower_bound(const key_type &k) const; size_type max_zise()const; reference operator[](const key_type & i) reverse_iterator rbegin(); const_reverse_iterator rbegin() const; reverse_iterator rend(); const_reverse_iterator rend() const; size_type size () const; void swap(map<Key, T, Comp, Allocator> &ob); iterator upper_bound(const key_type & k); const_iterator upper_bound(const key_type & k) const; value_compare value_comp() const;

Otherwise, pair<iterator, false> is returned. Returns the function object that compares keys. Return an iterator to the fisrt element in the map with the key equal to or greater than k. Return the maximun number of elements that the map can hold Returns a reference to the element specified by i. If this element does not exist, it is inserted. Returns a reverse iterator to the end of the map. Returns a reverse iterator to the start of the map. Return the number of elements currently in the map. Exchanges the elements strored in the invoking map with those in ob. Returns an iterator to the first element in the mao with the key greater than k. Returns the function object that compares values. Description Tra lai mt iterator cua phn t u tin trong ban . Laoi bo tt ca cac phn t t ban . Tra lai nhng con s xut hin k ln trong ban ( 1 hoc 0). Tra lai la ung nu vic keo theo trong ban g la rng va sai ngc lai. Tra v mt iterator khi kt thuc ban . tra lai mt cp iterator va a con tro v u tin va nhng phn t cui cung trong ban cha dng chia khoa c chi ro. . Loai bo phn t con tro tai vi tri i. Loai bo nhng phn t trong pham vi t bt u n kt thuc. Loai bo nhng phn t ma co nhng chia khoa co giad tri la k.

Member Function iterator begin(); const_iterator begin() const void clear(); size_type count(const key_type & k) const; bool empty() const; iterator end(); const_iterator end() const; pair<iterator, iterator> equa_range (const key_type & k); pair<const_iterator, const_iterator> equa_range (const key_type & k) const; void erase(iterator i); void arese(iterator strart,iterator end); size_type erase(const key_type & k); iterator find(const key_type &k);

839

const_iterator find(const key_type & k) const; allocator_type get_allocator() const; iterator insert (iterator I, const value_type & val); template<class Inter> void insert(Inter start, Inter end); pair<iterator, bool> void insert(const value_type & val);

Nu chia khoa khng tim thy, mt iterator ti kt thuc ban c tra lai. Tra lai s phn b ban . Chen val tai hoc sau phn t chi ro tai vi tri i.Mt iterator ti phn t c tra lai. Chen nhng pham vi cua phn t. Iterator val vao trong keo ban . Mt iterator ti phn t c tra lai. Phn t chen vao khi phn t cha tn tai, nu phn t chen, pair<iterator, true> c tra lai.Mt khac, pair<iterator, false> c tra lai. Nhng s tr li i tng m nhng s so snh chc nng la chia khoa. Tra lai mt iterator cua phn t u tin trong ban vi khoa bng nhau hay ln hn so vi k. Tra v s lng phn t ma ban co th gi. Tra lai tham chiu cua phn t c chi ro tai vi tri i. Nu phn t khng tn tai, no c chen vao. Tra lai mt iterator ao ngc cho ti kt thuc trong ban . Tra lai mt iterator ao ngc u tin trong ban . Tra lai s lng phn t hin thi trong ban . Trao i nhng phn t ct gi trong vic keo theo ban vi i tng ob. Tra lai mt iterator phn t u tin trong ban vi chia khoa ln hn so vi k. Nhng s tr li i tng m nhng s so snh chc nng nh gi.

key_compate key_comp()const; iterator lower_bound(const key_type & k); const_iterator lower_bound(const key_type &k) const; size_type max_zise()const; reference operator[](const key_type & i) reverse_iterator rbegin(); const+reverse_iterator rbegin() const; reverse_iterator rend(); const_reverse_iterator rend() const; size_type size () const; void swap(map<Key, T, Comp, Allocator> &ob); iterator upper_bound(const key_type & k); const_iterator upper_bound(const key_type & k) const; value_compare value_comp() const;

Key/ value pairs are stored in a map as object of type pair, which has this template specification: Chia khoa/ gia tri la nhng cp ct gi trong ban khi i tng c ghep i, ma c chi dn di: template < class Ktype, class Vtype> struct pair { typedef Ktype

840

first_type; // type of key typedef Vtype second _type; // type of value Ktype first ; // contains the key Vtype second; // contains the value //constructors pair(); pair( const Ktype &k, const Vtype &v); template<class A, class B> pair(const <A, B> ob); } As the comments suggest, the value in first contains the key and the value in second contains the value associated with that key. Nh nhng bnh lun gi , gi tr u tin cha ng cha kha v gi tr th hai cha ng gi tr c lin quan n m cha kha. You can construct a pair using either one of pairs constructors or by using make_pair() , which constructs a pair object based upon the types of the data used as parameters make_pair() is a generic function that has this prototype: Bn c th xy dng mt cp ang s dng c hai mt cp nhng ngi xy dng hay gn s dng make_pair() , m xy dng mt cp i tng da trn nhng kiu d liu c dng nh nhng tham s make_pair() l mt chc nng chung m c nguyn mu ny: Template<class Ktype, class Vtype> pair <Ktype, Vtype> make_pair(const Ktype &k , const Vtype & v); As you can see, it return a pair object consisting of values of the types specified by Ktype and Vtype. The advantage of make_pair() is that it allows the types of the types of the objects being stored to be determined automatically by the compiler rather than being explicitly specified. Nh bn c th nhn thy, n tr li mt i tng ch nhng gi tr cua kiu c ch r bi Ktype v Vtype. Li th ca make_pair() l n cho php nhng kiu ca nhng kiu nhng i tng ang c ct gi s c xc nh t ng gn ca ngi bin tp ung hn so vi r rng c ch r.

EXAMPLES - Vi Du
The following program illustrates the basics of using a map. It stored ten key/ value pairs. The key is a character and the value is an integer. The key/ value pairs stored are Chng trnh di y minh ha c s ca vic s dng mt bn . N ct gi 10 cp cha kha/ gi tr. Cha kha l mt c tnh v gi tr l mt s nguyn. Nhng

841

cp cha kha/ gi tr c ct gi: A 0 B 1 C 2 and so on. Once the pairs have been stored, the user is prompted for a key(I.e , a letter form A through J), and the value associated with that key is displayed. vn vn. Mt ln nhng cp c c ct gi, ngi s dng c nhc cho mt cha kha ( I.e, mt bc th hnh thnh A xuyn qua J), v gi tr kt hp vi cha kha kia c trinh bay. // A simple map demonstration #include <iostream> #include <map> using namespace std; int main() { map<char, int> m; int i; //put pair into map for(i = 0; i < 10 ;i++) { m.insert(pair<char, int>('A'+i, i)); } char ch; cout<< "Enter key :"; cin>> ch; map<char , int>::iterator p; // find value given key p=m.find(ch); if(p!=m.end ()) cout<< p->second; else cout<< "Key not in map \n"; return 0; } Notice the use of the pair template to construct the key/ value pairs. The data types specified by pair must match those of the map into which the pair are being inserted. Chu y nhng cp s dng khung mu cp xy dng cha kha/ gi tr. Nhng kiu d liu c ch r bi cp phi ph hp ca bn vo trong cp ang

842

c chn vo. Once the map has been initialized with keys and value, you can search for a value given its key by using the find() function. Find() returns an iterator to the matching element or to the end of the map if the key is not found. When a match is found, the value associate with the key is contained in the second member of pair. Mt ln bn c khi to vi nhng cha kha v gi tr, bn c th tm kim mt gi tr cho nhng bi cha kha gn s dng ham find(). Ham find() tr li mt iterator cho phn t thch ng hay ti kt thc bn nu cha kha khng c tm thy. Khi mt kt qua c tm thy, gi tr lin tng vi cha kha c cha ng trong Second cua thnh vin cp. In the preceding example, key/ value pairs were constructed explicitly, using pair< char, int > . Although there is nothing wrong with this approach, it is often easier to use make_pair() , which constructs a pair object upon the types of the data used as parameters. For example, assuming the previous program, this line of code will also insert key/ value pairs into m: Trong v d c trc, nhng cp cha kha/ gi tr xy dng r rng, s dng pai < char, int>. D khng c g sai vi cch tip cn ny, n thng d dng hn i vi s s dng make_pair() , m xy dng mt cp i tng trn nhng kiu d liu s dng nh tham s. Chng hn, gi thit chng trnh trc y, nhng cp cha kha/ gi tr chn ng nh m cng ny vo trong M: m.insert (make_pair((char) (A +i), i)); Here the cast to char is needed to override the automatic conversion to int when I is added to A. Otherwise, the type determination is automatic. y cn sc thi ky t s chuyn i t ng ti int khi ti c thm vo At. Cch khc, s xc nh kiu t ng. Like all of the containers, maps can be used to store object of types that you create. For example, the program shown here create that a map of words with their opposite. To do this it creates two classes called word and opposite. Sine a map maintains a soteres list of keys, the program also defines the <operator for operator objects of type word . In general, you must defines the< operator for any classes called that you will use as keys.(Some compilers might require that additional comparison operators be defined.) Cng nh tt c nhng cha, nhng bn c th c dng ct gi i tng nhng kiu m bn to ra. V d, chng trnh c cho thy y to ra m mt bn nhng t vi i ngc nhau. lm u ny to ra hai lp c gi l call v opposite. T khi mt bn bo ct gi lit k ca nhng cha kha, chng trnh cng nh ngha thao tc vin < cho nhng i tng thao tc vi kiu d

843

liu word. . Ni chung,bn phi nh ngha thao tc cho bt k lp no c gi l m bn s s dng nh nhng cha kha. .(Mt s ngi bin tp c l yu cu nhng ton t so snh b sung c nh ngha.) //A map oppsites #include <iostream> #include <map> #include <cstring> using namespace std; class word { char str[20]; public: word() { strcpy(str, ""); } word(char * s) { strcpy(str, s); } char *get() { return str; } }; //must define less than reletive to word objects bool operator<(word a, word b) { return strcmp(a.get(),b.get())<0; } class opposite { char str[20]; public: opposite() { strcmp(str, " "); } opposite(char *s) { strcpy(str, s); } char *get()

844

{ return str; } }; int main() { map<word ,opposite> m; //put words and opposite into map m.insert(pair<word,opposite>(word("yes"), opposite("no"))); m.insert(pair<word,opposite>(word("good"), opposite("bad"))); m.insert(pair<word,opposite>(word("left"), opposite("right"))); m.insert(pair<word,opposite>(word("up"), opposite("down"))); //given a word, find opposite char str[80]; cout<< "Enter word : "; cin>> str; map<word, opposite>::iterator p; p=m.find(word(str)); if(p!=m.end()) cout<< " Opposite : " << p>second.get(); else cout<< " Word not in map .\n"; return 0; }

EXERICES - Bai Tp
Experiment with the examples, trying small variations. Th nghim nhng v d, th nhng s bin i nh. Create a map that contains names and telephone munbers. Allow names and numbers to be entered, and set up your program so that a number can be found when a name is given. To ra mt bn m cha ng nhng tn v nhng s in thoi. Cho php nhng tn v nhng s s c vo, v thit lp chng trnh cc bn sao cho mt s c th

845

c tm thy khi mt tn c cho. (Hint: Use Example 3 as a model.) Do you need to define the <operator for object used as keys in map ? Bn cn nh ngha thao tc vin< operator i tng c dng nh nhng cha kha trong bn khng? 14.5.

ALGORITHMS - Nhng thut giai

As explained, algorithms act on containers. Although each container provides support for its own basic operations, the standard algorithms provides more extended or extended or complex actions. They also allow you to word with two different types of containers at the same time. To have access to the STL algorithms, you must include <algorithm> in your program. Nh c gii thch, nhng gii thut hnh ng theo nhng cha. D mi cha cung cp s h tr nhng thao tc c bn ca mnh, nhng gii thut tiu chun cung cp nhng hot ng m rng hn hay m rng hay phc tp. Chng cng cho php bn ti t vi hai kiu khc nhau cua nhng cha cung luc. c s truy nhp ti nhng gii thut STL , bn khai bao include <algrithm> trong chng trnh ca cc bn. The STL defines a large number of algorithms, which are summarized in Table 14-5. All of the algorithms are template functions. This means that they can be applied to any type of container. The example that follow demonstrate a representative sample. STL nh ngha mt s ln gii thut, m c tng kt trong Bng 14-5. Tt c nhng gii thut l nhng chc nng khung mu. Nhng phng tin ny m chng c th c ng dng vo bt k kiu no ca cha. V d m i theo sau trnh din mt mu i din. Algorithm Purpose adjacent_find Searches for adjacent matching element within a sequence and return an iterator to the first match. binary_search Performs a binary search on an ordered sequence. copy Copies a sequence. copy_backward Same as copy() except that it moves the elements from the end of the sequence first. count Returns the number of elements in the sequence. count_if Returns the number of elements in the sequence that satisfy some predicate. equal Determines whether two range are the same. equal_range Returns a range in which an element can be inserted into a sequence without disrupting the ordering of the sequence. Fills range with the specified value. fill fill_n Searches a range for a value an returns an iterator to the 846

find find_end find_fisrt_of find_if for_each generate generate_n includes inplace_merge iter_swap lexicographical_compare lower_bound make_heap max max_element merge min min_element mismatch next_permutation nth_element partial_sort partial_sort_copy partition pop_heap prev_permutation

first occurrence of the element. Searches a range for a subsequence.This function returns an iterator to the end of the subsequence within the range. Finds the first element within a sequence that matches an element within a range. Searches a range for an element for which a user-defined unary predicate returns true. Applies a function to a range of elements Assigns to elements in a range the values returned by a generator function. Determines whether one sequence includes all of the elements in another sequence. Merges a range with another range. Both ranges must be sorted I increasing order. The resulting sequence is sorted. Exchanges the values pointed to by two iterator arguments. Alphabetically compares one sequence with another. Finds the first point in the sequence that is not less than a specified value. Constructs a heap from a sequence Returns the maximun of two values Returns an iterator to the maximum element within a range. Merges two ordered sequences, placing the result into a third sequence. Returns the minimum of two values. Returns an iterator to the minimun element within a range. Finds the first mismatch between the element in two sequences. Iterators to the two elements are returned. Constructs the next permutation of a sequence. Arranges a sequence such that all elements less than a specified element E come before that element and all elements greater than E come after it. Sorts a range. Sorts a range and than copies as many elements as will fit into a reasult sequence. Arranfges a sequence such that all elements for which a predicate returns true come before those for which the predicate returns flase. Exchanges the first last-1 elements and then rebuilds the heap. Constructs the previous permutation of a sequence. Pushes an element into the end of a heap. Randomizes a sequence. Revomes elements from a specified range.

847

push_heap random_shuffle revome revome_if revome_copy revome_copy_if replace replace_copy replace_if replace_copy_if reverse reverse_copy rotate rotate_copy search search_n set_difference set_intersection set_symmetric_defference set_union sort sort_heap stable_partion

Replaces element within a range.

Reverses the order of a range Left-rotate the elements in range. Searches for a subsequence within a sequence. Searches for a subsequence of a specified number of similar element. Produces a sequence that contains the difference between two ordered sets. Produces a sequence that contains the intersection of two ordered sets. Produces a sequence that contains the symmetric difference between two ordered sets. Produces a sequence that contains the union of two ordered sets. Sort a range. Sorts a heap within a specified range. Arranges a sequence such that all elements for which a predicate returns true come before those for which the predicate return false. The partitioning is stable; the relative ordering of the sequence is preserved. Sorst a range. The sort is stable; equal elements are the not rearranged. Exchanges two values. Exchanges elements in a range. Applies a function to a range of elements and stored the uotcome in a new sequence. Eliminates duplicate elements from a range. Finds the last point in a sequence that is not greater than some value.

stable_sort swap swap_range transfrom unique unique_copy upper_bound

Algorithm adjacent_find

Purpose Tm kim phn t thch ng k bn bn trong mt chui

848

binary_search copy copy_backward count count_if equal equal_range fill fill_n find find_end find_fisrt_of find_if for_each generate generate_n includes inplace_merge iter_swap lexicographical_compare lower_bound make_heap max max_element merge min min_element

v s tr li mt iterator ti u tin ph hp. Thc hin mt s tm nh phn trn mt chui c ra lnh. Sao chp mt chui. Cng nh copy () s loi tr m n di chuyn nhng phn t t kt thc chui u tin. Tr li nhng s phn t trong chui. Tr li s phn t trong chui m tha mn v t no . Xc nh liu c phi hai phm vi ging nh vy Tr li mt phm vi trong mt phn t c th c chn vo trong mt chui khng c vic ph v s iu chnh chui. Nhng s in vo cng dy vi gi tr c ch r Tm kim mt phm vi mt gi tr mt iterator nhng tr li ti bin c u tin phn t. Tm kim mt phm vi mt s n sau.Chc nng tr li mt iterator cho kt thc s n sau bn trong phm vi. Tm thy phn t u tin bn trong mt chui m ph hp vi mt phn t bn trong mt phm vi. Tim kim mt phm vi mt phn t m mt v t n nguyn do ngi dng nh ra tr li ng. Ap dung mt chc nng ti mt phm vi nhng phn t. Gn ti nhng phn t trong mt phm vi nhng gi tr c tr li bi mt chc nng my pht Xc nh liu c phi mt chui bao gm mi th nhng phn t trong chui khc. Ha trn mt phm vi vi phm vi khc. C hai phm vi vic phi c phn loi Ti tng ra lnh. Chui kt qu c phn loi. Trao i nhng gi tr c tr vo bi hai l l iterator. Theo th t abc so snh mt chui vi khc. Nhng s tm kim u tin ch trong chui m khng t hn so vi mt gi tr c ch r. Xy dng mt ng t mt chui. Tra li cc ai hai gi tr. Mt iterator nhng tr li ti phn t cc i bn trong mt phm vi. Nhng s ha trn hai sp t nhng chui, t kt qu vo trong mt chui th ba. Tr li min hai gi tr. Tr li mt iterator phn t nho nht bn trong mt phm vi. Tm thy s khng thch ng u tin gia phn t trong hai chui. Iterators ti nhng hai phn t c tr li. Xy dng s hon v k tip ca mt chui.

849

mismatch next_permutation nth_element partial_sort partial_sort_copy partition pop_heap prev_permutation push_heap random_shuffle revome revome_if revome_copy revome_copy_if replace replace_copy replace_if replace_copy_if reverse reverse_copy rotate rotate_copy search search_n set_difference set_intersection set_symmetric_defference set_union sort sort_heap

Thu xp mt chui sao cho tt c cc phn t t hn so vi mt phn t c ch r E n trc phn t kia v tt c phn t ln hn so vi n sau n. Phn loi mt phm vi. Phn loi mt phm vi so vi sao chp nhiu phn t thch hp vo trong mt chui reasult. Sp t mt chui sao cho tt c cc phn t m mt v t tr li ng n trc khi ngi v t no tr li flase Trao i nhng phn t-1 cui cng u tin v sau xy dng li ng. Xy dng s hon v trc y mt chui. y mt phn t vo trong kt thc mt ng. Lm ngu nhin mt chui. Xoa nhng phn t t mt phm vi c ch r.

Thay th phn t bn trong mt phm vi.

o ngc mt phm vi. Left-rotate Nhng phn t trong phm vi. Tm kim mt s n sau bn trong mt chui. Nhng s tm kim mt s n sau mt s c ch r phn t tng t. Kt qua mt chui m cha ng s khc nhau gia hai ra lnh nhng tp hp. Kt qua mt chui m cha ng s khc nhau cn i gia hai sp t nhng tp hp. Kt qua mt chui m cha ng lin hip hai sp t nhng tp hp. Kt qua mt chui m cha ng lin hip (ca) hai sp t nhng tp hp. Phn loi mt phm vi. Phn loi mt ng bn trong mt phm vi c ch r. Thu xp mt chui sao cho tt c cc phn t mt thuc tinh nhng s tr li ng n trc khi ngi v t no tr li sai. Phn danh ring n nh; dng tng quan ra lnh chui c gi gn. Loi mt phm vi. Loi n nh; nhng phn t bng nhau l khng phi c nui.

850

stable_partion

stable_sort swap swap_range transfrom unique unique_copy upper_bound

Trao i hai gi tr. Trao i nhng phn t trong mt phm vi. Ap dng mt chc nng vo mt phm vi (ca) nhng phn t v c ct gi trong mt chui mi. Loi tr nhng phn t bn sao t mt phm vi. Nhng s tm kim cui cng ch trong mt chui m khng l thu vi so vi l gi tr no .

EXAMPLES
Two of the simplest algorithms are count() and count_if(). Their general forms are shown here: Hai trong s nhng gii thut n gin nht count() va count_if(). Nhng co dng chung c cho thy y: template<class Inter, class T> size_t count(Inter start, Inter end, const T & val); template<class Inter, class T> size_t count(Inter start, Inter end, Unpred T & pfn); The count() algorithm returns the number of elements in the sequence beginning at start and ending at end that match val. Gii thut count() tr li s lng phn t trong chui ang bt u ti bt u v kt thc ti kt thc m ph hp val. The count_if() algorithm returns the number of elements in the sequence beginning at start and ending at end for which the unary predicate pfn returns true. Gii thut count_if() tr li s lng phn t trong chui bt u ti start v kt thc ti end v t n nguyn no pfn nhng s tr li ng. The following program demonstrates count() and count_if(). Chng trnh sau y trnh din count() v count _if(). //Demonstrate count and count_if() #include <iostream> #include <vector> #include <algorithm> using namespace std;

851

/* This is a unary predicate that determies if value is even */ bool even (int x) { return !(x%2); } int main() { vector<int> v; int i; for(i=0; i<20; i++) { if(i%2) v.push_back(1); else v.push_back(2); } cout << "Sequence :"; for(i=0 ; i< v.size();i++) cout<< v[i] <<" "; cout<<endl; int n; n= count(v.begin(), v.end(),1); cout<<n<< "elements are 1 \n"; n= count_if(v.begin(), v.end(),even); cout<<n<< "elements are even \n"; return 0; } This program dislays the following output: Kt qua cua chng trinh la : Sequence : 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 10 element are 1 10 element are even. The program begin by creating a 20 element vector that contairns alternating 1s and 2s. Next, count() is used to count the number of 1s. Then, cuont_if() counts the number of elements that are even.Notice how the unary predicate even() is coded. All unary predicates receive as a parameter an object that is of the same type as that stored in the contianer upon which the predicate is operating. The predicate must then return a true or false result based upon this object. Chng trnh bt u bi vic to ra mt 20-vect phn t m contairns ang xen k 1s v 2s. Ri ,cuont_if() m s phn t m chn. Ch nh th no v t n 852

nguyn even() c vit m. Tt c cc v t n nguyn nhn c khi mt tham s la mt i tng m cng kiu vi iu ct gi trn v t no ang vn hnh. V t phi ri tr li mt kt qu ng hay sai c da trn i tng ny. Sometimes it is useful to generate a new sequence that consists of only certain items from an original sequence. One algorithm that does this is revome_copy(). Its general forms is shown here: i khi pht sinh mt chui mi m gm c nhng tit mc nht nh duy nht t mt chui nguyn bn. Mt gii thut m lm iu nyrevome_copy(). Nhng dng chung nhng c cho thy y: Template<class Inlter, class Outlter, class T> Outlter revome_copy(Inter start. Inlter end, Outlter result,const T &val) The remove-copy() algorithm copies elements from the specified range that are equal to val and puts the result into the sequence pointed to by result. It returns an iterator to the end of the result. The output contairner must be large enough to hold the result. The following program demonstrates revome_copy(). It creates a sequence of 1s and 2s. it then revomes all the 1s from the sequence. Gii thut remove_copy() sao chp nhng phn t t phm vi c ch r m bng nhau ti val v t kt qu vo trong chui c tr tai vi tri result. . N tr li mt iterator ti kt thc kt qu. u vao cha phi ln gi kt qu. Chng trnh sau y trnh din revome_copy(). N to ra mt chui 1sv 2s. Ri no xoa tt ca cac t trong chui 1s. //Demonstrate revome_copy #include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> v, v2(20); int i; for(i=0; i<20; i++) { if(i%2) v.push_back(1); else v.push_back(2); } cout<< "sequence :"; for(i=0; i<v.size(); i++) cout<< v[i] << " "; cout<< endl; 853

//Revome 1s remove_copy(v.begin(),v.end(), v2.begin(),1); cout<< "Result: "; for(i=0; i< v2.size(); i++) cout<< v2[i]<< " "; cout<<endl<<endl; return 0; } The output procduced by this program is shown here: Kt qua cua chng trinh la: Sequence:2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 Result:2 2 2 2 2 2 2 2 2 2 0 0 0 0 0 0 0 0 0 0 An often useful algorithm is reverse(), which reveses a sequence. Its general form is Mt gii thut thng hu ch reverse(), m xoay mt chui. Dng chung ca n l: template<class Bilter> void reverse(Bilter stasrt,Bilter end); The reverse() algorithm reverses the order of the range specified by start and end. Gii thut reverse() o ngc mnh lnh cua phm vi c ch r by start v end. The following program demonstrates reverse(): Kt qua cua chng trinh revese() la: //Demonstrate reverse #include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> v; int i; for(i=0; i<10; i++) v.push_back(i); cout<< "Initial :"; for(i=0; i<v.size(); i++) cout<< v[i] << " "; cout<< endl;

854

reverse(v.begin(), v.end()); cout<< "Reverse : "; for(i=0; i< v.size(); i++) cout<< v[i]<< " "; return 0; } The output from this program is shown here: Initial: 0 1 2 3 4 5 6 7 8 9 Reverse: 9 8 7 6 5 4 3 2 1 0 Once of the more interesting algorithms is tramsform(), which modified each element in a range according to a function that you provide. The tramsform() algorithm has these two general forms: Mt trong nhng gii thut th v hn la tramsform(), phn t c sa i tng ci trong mt phm vi theo mt chc nng m bn cung cp. Gii thut tramsform()c hai dng chung ny: temp<class Inlter1, class Outlter, class Func) Outlter stransform(Inlter srart, Inlter end, Outlter result, Func unaryfunc); temp<class Inlter1, class Inlter2, class Outlter, class Func) Outlter stransform(Inlter1 srart1, Inlter1 end1, Inlter2 end1, Inlter2 start, Outlter result, Func unaryfunc); The transform() algorithm applies a function to a range of elements and stores the outcome in result. In the first form, the range is specified by start and end. This function receives the value of an element in its parameter, and it must return the elements transformation. In the second form, the transformation is applied using a binary operator function that receives the value of an element form the sequence to be transformed in its first parameter. Both versions return an iterator to the end of the resulting the original sequence. Gii thut transform() p dng mt chc nng vo mt phm vi nhng phn t v ct gi hu qa trong result. Trong mu u tin, phm vi c ch r boiwr srart v end. Chc nng ny nhn c gi tr mt phn t trong tham s cua n, v n phi tr li bin i cua phn t. Trong mu th hai, s bin i c p dng s dng mt chc nng ton t nh phn m nhn c gi tr ca mt phn t hnh thnh chui s c thay i trong tham s u tin ca n. C hai phin bn tr li mt iterator ti kt thc kt qu chui nguyn bn. The following program uses a simple transformation function called xform() to square the contents of a list. Notice that the resulting sequence is sroted in the same list that provided the original sequence. Chng trnh sau y s dng mt chc nng bin i n gin c gi l xform()

855

lm ni dung ca mt danh sch. Ch chui kt qu l sroted trong danh sch ging nh vy m cung cp chui nguyn bn. //An example of the transform algorithm #include <iostream> #include <list> #include <algorithm> using namespace std; // A simple transformation function int xform(int i) { return i*i;//square orginal value } int main() { list<int> x1; int i; //put value into list for(i=0; i<10; i++) x1.push_back(i); cout<< "orginal contents of x1 :"; list<int>::iterator p=x1.begin(); while (p!=x1.end()) { cout<< *p << " "; p++; } cout<< endl; //transform x1 p=transform(x1.begin(),

x1.end(),x1.begin(),xform);

cout<< "Transformed contents of x1: "; p=x1.begin(); while(p!=x1.end()) { cout<< *p << " "; p++; } return 0; } The output produced by the program is shown here: Kt qua cua chng trinh la : 856

Orginal contents of x1: 0 1 2 3 4 5 6 7 8 9 Transformed contents of x1: 0 1 4 9 16 25 36 49 64 81 as you can see, each element in the x1 list has been squared. trong khi bn c th nhn thy, mi phn t trong x1 danh sch c lm vung.

EXERCISES
The sort() algorithm has these forms: Gii thut sort()c nhng mu ny: template<class Randlter> void sort(Randlter start, Randlter end); template<class Randlter start, class Comp> void sort(Randlter start, Randlter end, Comp cmpfn); It sorts the range specified by start and end. The second form allows you to specify a comparison function that determines whether one element is less than another. Write a program that demonstrates sort(). ( Use either form you like.) Nhng loi phm vi ch r bi bt u v kt thc. Hai hnh thnh cho php bn ch r mt ham so sanh m xc nh liu mt phn t l t hn phn t khc. Vit mt chng trnh m biu diu sort(). ( S dng mi mu bn thch.) The merge() algorithm merges two ordered sequences and places the result into a third. One of its general forms is shown here: Gii thut merge() ha trn hai chui c ra lnh v t kt qu vo trong mt c mc trc. Mt trong s nhng dng chung n c cho thy y: template<class Inlter1, class Inlter2, class Outlter> Outlter merge(Inlter1 start1, Inlter1 end, Inlter2 start2, Inlter2 end2, Outlter result); The sequence to be meged are defined by start 1, end1 and start2, end2. The result is put into sequence pointed to by result. An iterator to the end of the reslting sequence is returned. Demonstrate this algorithm. Chui l bt u c nh ngha gn bi srart1, end1 v start2, end2. Kt qu c t vo trong chui c tr vo bi kt qu. Trnh din giai thut ny.

14.6.

THE STRING CLASS - Lp Chui


As you know, c++ does not support a built-in string type, per se. it does, however, provide two ways to handle strings. First, you can use the traditional, null-terminated charater array with which you are already familiar. This is sometimes referred to as a string. The second method is to use a class object of type string. This is the approach that is examined here. Nh bn a bit , C++ khng co s h tr mt chui inh sn, n lam.Tuy nhin,

857

cung cp hai cch x l nhng chui. u tin, bn c th s dng cach truyn thng, dung mang rng cha cac ky t vi bn quen thuc. y thi i khi c tham chiu ti nh mt chui. Phng php th hai s s dng mt i tng lp kiu string. iu ny la cch tip cn m c kho st y. Actually, the stirng class is a specialization of a more general template class called basic_string. In fact, there are two specializations of basic_string : string, which supports wide characters strings in normal programming, string is the version of basic_string examined here. Tht s,stirng lp l mt s chuyn mn ha mt lp khung mu chung hn c gi l basic_string. Tht ra, thi hai s chuyn mn ha ca basic_string: string, m h tr chui nhng c tnh rng trong lp trnh bnh thng, string l phin bn ca basic_string kho st y. Before you look at the string class, it is important that you understand why it is part of the C++ library. Standard classes have not been casually added to C++ . In fact, a significant amount of thought arrays, it migth at first seem that the inclusion of the string class in an exception to this rule. However, this is actually far from the truth. Here id why: Null-terminated strings cannot be manipulated by any of the standard C++ operators, nor can they take part in normal C++ expressions. For example. Consider this fragment: Trc y bn quan st string trong lp , quan trng rng bn hiu ti sao l phn th vin C++ . Nhng lp tiu chun khng co trong C++ c thm vo. Tht ra, mt s lng quan trng mng s suy ngh, trc ht c v s bao gm string phn loi trong mt ngoi l ti quy tc ny. Tuy nhin, y tht s xa vi s tht. y ti sao : nhng chui c kt thc bi null khng th l thao tc bi bt k ci no ca thao tc th vin chun cua C++, m cng khng chng c th tham gia vo nhng biu thc C++ bnh thng. Chng hn. Xem xt on ny: char s1[80], s2[80], s3[80]; s1="one" ; //can't do s2="two" ; //can't do s3=s1+s2 ; // error, not allowed As the comments show, in C++ it is possible to use the assignment operator to give a character array a new value ( exept during initialization), nor is it possible to use the + operator to concatenate two strings. These operations must be written using library functions, as shown here: Trong khi nhng bnh lun hin ra, trong C++ n c th xy ra s dng thao tc th vin n nh cho mt c tnh by bin mt gi tr mi ( Tr trong thi gian s khi to), m cng khng n c th xy ra s dng +operator ghp ni hai chui. Nhng thao tc ny phi c vit s dng nhng hm th vin chun sau, nh c cho thy y: strcpy(s1 , "one"); strcpy(s1 , "two"); strcpy(s3, s1); 858

strcpy(s3, s2); Since null-terminated arrays are not technically data types in their own right, the C++ operators cannot be applied to them. This makes even the most rudimentary string operations clumsy. More than anything else, it is the inability to operator on null-terminated strings using the standard C++ operators that has driven the development of a standard string class. Remember, when you define a class in C++, you are defining an new data type that can be fully integrated into the C++ environment. This, of course, means that the operators can be overloaded relative to the new class. Therefore, with the addition of a standard string class, it becomes possible to manage strings in the same way that any other type of data is managed: through the use of operators. Mt khi nhng mng c kt thc bi null v mt k thut l nhng kiu d liu trong quyn ca mnh, nhng thao tc th vin C++ khng th c ng dng vo h. Ci ny lm cho thm ch chui s khai nht l nhng thao tc vng v. Hn bt k vt g khc,o l s khng c kh nng ti thao tc th vin trn nhng chui c kt thc bi null s dng nhng thao tc th vin chun C++ m iu khin s pht trin mt tiu chun lp string. Nh, khi bn nh ngha mt lp trong C++, bn phi nh ngha mt kiu d liu mi m c th hon ton tng hp vo trong mi trng C++. iu ny, tt nhin, c ngha rng nhng thao tc vin c th b cht qu nng tng i ti lp mi. Bi vy, vi s thm mt tiu chun lp string, n c tr nn c th xy ra qun l nhng chui trong cch ging nh vy m mi kiu khc d liu c qun l thng qua s s dng nhng thao tc vin. There is, however, one other reason for the standard string class: safety. An inexperienced or careless programmer can very easily overrun the end of an array that holds a null-terminated string. For example, consider the standard string copy function strcpy(). This function contains no provision for checking the boundary of the target array. If the source array contains more characters than the target array can hold, a program error of system crash is possible (likely) . As you will see, the standard string class prevents such errors. Vn d , tuy nhin, mt l do khc tiu chun lp string: s an ton. Mt lp trnh vin thiu kinh nghim hay cu th c th rt d dng trn qua kt thc mt mng m gi chui c bi NULL kt thc mng. Chng hn, cho rng chui tiu chun sao chp chc nng strcpy(). Chc nng ny khng cha ng s chun b kim tra ranh gii ca mng ch. . Nu mng ngun cha nhng c tnh so vi mng ch c th gi(chim), mt li chng trnh ca s va chm h thng l c th xy ra (thch hp). In the final analysis, there are three reasons for the inclusion of the standard string class :consistency ( a string now defines a data type), convenience ( you can use the standard C++ operators) and safety ( array boundaries will not be overrun) . Keep in mind that there is no reason that you should abandon normal, null-terminated strings altogether. They are still the most efficient way 859

in which to implement strings. However, when speed is not an overloading cpncern, the new string class gives you access to a safe and fully integrated way to manage strings. Although not traditionally thought of as part of the STL, sting is another container class defined by C++. This means that it supports the algorithms described in the previous section. However, strings have additional capabilities. To have access to the string class you must include <string> in your program. Mc d cha theo truyn thng c ngh nh phn STL, sting lp cha cc c nh ngha bi C++. Ci ny c ngha rng n h tr nhng gii thut c m t trong mc trc y. . Tuy nhin, nhng chui c nhng kh nng b sung. c s truy nhp Ti string lp bn phi khai bo include<string> trong chng trnh ca bn. The string class is very large, with many constructors and member functions. Also, many member functions have multiple overloaded forms. For this reason, it is not possible to look at the entire contents of string in this chapter. Instead, we will examine several of its most commonly used features. Once you have a general understanding of how string words, you will be able to easily explore the rest of it on your own. Lp string th rt ln, vi nhiu ngi xy dng v thnh vin vn hnh. ng thi, nhiu thnh vin nhng chc nng c nhiu qu ti trong nhng mu. L do ny, c th xy ra khng quan st ton b ni dung string trong chng ny. Thay vo , chng ti s kho st ring bit nhng c tnh thng s dng nht ca n. Mt ln bn cho php mt tng quan hiu nh th no nhng t trong string, bn s c kh nng d dng t khm ph phn cn li ca n. The string class support several constructors. The prototypes for three of its most commonly used constructors are shown here. Lp string h tr cho ngi xy dng. Nhng nguyn mu ba trong s nhng ngi xy dng thng s dng nht c cho thy y. string(); string(const char *str); string(const string & str); the first form create an empty string object. The second creates a string object from the null-terminated string pointed to by str. This form provides a conversion from null-terminated strings to string object. The third form creates a string object from another string object. u tin hnh thnh to ra mt lm rng string i tng. Hai to ra mt string i tng t chui c kt thc bi gi tr NULL c tr ti str. Mu ny cung cp mt s chuyn i t nhng chui c kt thc bi s khng Ti string i tng. Mu th ba to ra mt string i tng t string i tng khc. A number of operators that apply to strings are defined for string object, including those listed in the following table:

860

Mt s thao tc vin m ng dng vo nhng chui c nh ngha Cho string i tng, bao gm c vo danh sch trong bng sau y: Operator = + += == != < <= > >= [] Meaning Assignment Concatenation Concatenation assignment Equality Inequality Less than Less than or equal Greater than Greater than or equal Subscripting Output Input

These operator allow the use of string object in normal expressions and eliminate the need for calls to functions such as strcpy() or strcat(), for example. In general, you can mix string objects with normal, null-terminated strings in expressions. For example, a string object can be assigned a nullterminated string. Thao tc vin ny cho php s s dng string phn i trong nhng biu thc bnh thng v loi tr nhu cu gi vn hnh nh strcpy() hay strcat(), chng hn. Ni chung, bn c th pha trn string nhng i tng vi nhng chui bnh thng, c kt thc bi s khng trong nhng biu thc. Chng hn, mt string i tng c th c gn mt gi tr NULL kt thc chui. The + operator can also be used to concatenate a string object with another string object or a string object with C-style string. That is, the following variations are supported: Ton t + c th cng c quen a h ghp mt string i tng vi string i tng khc hay mt string i tng vi kiu chui C. Ngha l, nhng s bin i sau y c h tr: string + string string + C-string C-string + string The + operator can also be used to concatenate a character onto the end of a string. Ton t + c th cng c dng ghp ni mt c tnh ln trn kt thc ca mt chui. The string class defines the constant npos, which is usually-1. This constant represents the length of the longest possible string. 861

Lp string nh ngha hng s npos, m thng thng l-1. Hng s ny i din cho chiu di ca chui c th xy ra di nht. Although most simple string operations can be accomplished with string clsas member functions. Although there are far too many to discuss in this chapter, we will examine several of the most common ones. To assign one string to another, use the assign() function. Two of its forms are shown here: Mc d a s nhng thao tc chui n gin c th c hon thnh Vi string nhng chc nng thnh vin ca lp. Mc d c cng c nhiu ngi tranh lun trong chng ny, chng ti s kho st ring bit mt cch chung nht. gn mt chui ti chui khc, s dng chc nng assign(). Hai trong s nhng mu n l ch ra y: string&assign(const string &strob, size_type start, size_type num); string &assign(const char *str, size_type num); In the first form, num characters from strob beginning at the index specified by start will be assigned to the invoking object. In the second form insert() inserts num characters from strob. Beginning at inStart into the invoking string at the index specified by Start . Trong mu u tin,num nhng c tnh n t strob bt u ti ch s c ch r bi start s l gn ti vic ko theo i tng. Trong trng hp th hai insert() nhng s chn num nhng c tnh n t strob. Bt u ti inStart vo trong ko theo chui ti ch s c ch r bi start. Beginning at start, the first form of replace( ) replaces num characters from the invoking string, with strob. The second form replaces orgNum characters, beginning at start, in the invoking string with replaceNum characters from the string specified by strob, beginning at replaceStart. In both cases, a reference to the invoking object is returned. Bt u ti tsart, u tin hnh thnh replace()thay th num nhng c tnh n t ko theo chui, vi strob. Hai hnh thnh thay thorgNumnhng c tnh, s bt u ti Sbnh nhn mt, trong ko theo chui vi replaceNum nhng c tnh t chui c ch r gn strob, bt u ti replaceStart. Trong c hai trng hp, mt s tham kho ti vic ko theo i tng c tr li. You can append part of one string to another using the append() member function. Two of its forms are shown here: Bn c th ni vo b phn ca mt chui ti khc s dng append() chc nng thnh vin. Hai trong s nhng mu n c cho thy y string &erase(size_type start = 0, size_type num = npos); It removes num characters from the invoking string, beginning at start. A reference to the invoking string is returned. N loi b num nhng c tnh n t ko theo chui, bt u ti start. Mt s

862

tham kho ti vic ko theo chui c tr li. The string class provides several member functions that search a string, including find( ) and rfind( ). Here are the prototypes for the most common versions of these functions: Lp string cung cp vi chc nng thnh vin m tm kim mt chui, bao gm find() v rfind(). y nhng nguyn mu chung nht l nhng phin bn y nhng chc nng: size_type find(const string &strob, size_type start=0) const; size_type rfind(const string &strob, size_type start=npos) const; Beginning at start, find( ) searches the invoking string for the first occurrence of the string contained in strob. If found, find( ) returns the index at which the match occurs within the invoking string. If no match is found, then npos is returned. rfind( ) is the opposite of find( ). Beginning at start, it searches the invoking string in the reverse direction for the first occurrence of the string contained in strob (i.e., it finds the last occurrence of strob within the invoking string). If found, rfind( ) returns the index at which the match occurs within the invoking string. If no match is found, npos is returned. Bt u ti start, find() nhng s tm kim ko theo chui bin c u tin ca chui c cha ng trong strob. Nu tm thy,find() tr li ch s m trn u xut hin bn trong vic ko theo chui. Nu khng c trn u c tm thy, ri npos c tr li rfind() i din ca find(). Bt u ti start, n tm kim ko theo chui trong s nghch o cho bin c u tin ca chui c cha ng trong strob (th d, n tm thy cui cng bin c strob bn trong vic ko theo chui). Nu tm thy, rfind() tr li ch s ti trn u no xut hin bn trong vic ko theo chui. Nu khng c trn u c tm thy,npos tr li. To compare the entire contents of one string object with another, you will normally use the overloaded relational operators, described earlier. However, if you want to compare a portion of one string with another, then you will need to use the compare( ) member function, shown here: Ti s so snh nguyn bng nhau ca mt i tng chui vi nhau, bn c th nh bnh thng s dng nhng ton t quan h ton t chng, c m t trc . Tuy nhin, nu bn mun so snh mt phn ca mt chui, ri chui vi chui khc s cn vi s s dng compare ()thnh vin vn hnh, cho thy y: int compare(size_type start, size_type num, const string &strob) const; Here, num characters in strob, beginning at start, will be compared against the invoking string. If the invoking string is less than strob, compare( ) will return less than 0. If the invoking string is greater than strob, it will return greater than 0. If strob is equal to the invoking string, compare( ) will return 0. y,num nhng c tnh trong strob, bt u ti start, s l so snh g li vic ko theo chui. Nu vic ko theo chui t hn so vi strob, compare()s tr li t so vi 0. Nu Vic ko theo chui (th) ln hn so vi strob, nh tr li 863

ln hn so vi0. Nu strob bng nhau ti vic ko theo chui,compare()s tr li 0. Although string objects are useful in their own right, there will be times when you will need to obtain a null-terminated character-array version of the string. For example, you might use a string object to construct a file name. However, when opening a file, you will need to specify a pointer to a standard, null-terminated string. To solve this problem, the member function c_str( ) is provided. Its prototype is shown here: Mc d string nhng i tng hu ch trong quyn ca mnh, s c nhng ln khi bn s cn thu c mt gi tr NULL - c tnh c kt thc- phin bn mng chui. V d, bn c l s dng mt string i tng xy dng mt tn tp tin. Tuy nhin, khi m mt h s, bn s cn ch r mt con tr ti mt tiu chun, c kt thc bi s khng chui. gii quyt vn ny, chc nng thnh vin c_str() c cung cp. Nguyn mu ca n c cho thy y: const char *c_str( ) const; This function returns a pointer to a null-terminated version of the string contained in the invoking string object. The null-terminated string must not be altered. It is also not guaranteed to be valid after any other operations have taken place on the string object. Chc nng ny tr li mt con tr cho mt phin bn c kt thc bi s 0 ca chui c cha ng Trong ko theo string i tng. Chui c kt thc bi s 0 th khng phi bin i. N cng khng phi bo m hp l sau khi mi thao tc khc tip tc xy ra string i tng. Because it is a container, string support the begin() and end() functions that return an iterator to the start and end of string, respectively. Also included is the zise() function, which returns the munber of charaters currently in a string. Bi v l mt cha, string s h tr begin() v end() nhng chc nng m tr li mt iterator cho bt u v kt thc chui, tng ng. Cng c bao gmZ () chc nng, m tr li s t hin thi trong mt chui.

EXAMPLES
Although the traditional, C-style strings have always been simple to use, the C++ string classes make string handling extraordinarily easy. For example, by using string objects, you can use the assignment operator to assign a quoted string to a string, the + operator to concatenate strings, and the comparison operators to compare strings. The following program illustrates these operations: Mc d t ruyn thng,C- Kiu nhng chui c lun lun n gin ti s s dng, tng ta chui C++ nhng lp lm s dng chui d dng l thng. . Chng hn,

864

gn s dng string nhng i tng, bn c th s dng thao tc vin n nh gn mt chui c bo gi ti mt string,ton +operator thao tc vin ti nhng chui dng chui, v nhng ton t so snh ti nhng chui so snh. Chng trnh sau y minh ha nhng thao tc: // A short string demonstration. #include <iostream> #include <string> using namespace std; int main() { string str1("The string class gives "); string str2("C++ high-powered string handling."); string str3; // assign a string str3 = str1; cout << str1 << "\n" << str3 << "\n"; // concatenate two strings str3 = str1 + str2; cout << str3 << "\n"; // compare strings if(str3 > str1) cout << "str3 > str1\n"; if(str3 == str1+str2) cout << "str3 == str1+str2\n"; /* A string object can also be assigned a normal string. */ str1 = "This is a null-terminated string.\n"; cout << str1; // create a string object using another string object string str4(str1); cout << str4; // input a string cout << "Enter a string: "; cin >> str4; cout << str4; return 0; } This program produces the following output: The string class gives The string class gives The string class gives C++ high-powered string handling. str3 > str1 str3 == str1+str2 This is a null-terminated string.

865

This is a null-terminated string. Enter a string: Hello Hello As you see, object of type string can be manipulated with techniques similar to those used to word with C++s built-class data types. In fact, this is the main advantage to the string class. Trong khi bn nhn thy, i tng kiu string c th c thao tc vi k thut tng t nh ngi (vt) c dng ti t vi C++ xy dng- nhng kiu d liu lp. Trong thc t iu ny l li th chnh ti lp chui. Notice the ease with which the string handling is accomplished. For example, the + is used to concatenate strings, and the > is used to compare two strings. To accomplish these operations using C-style, null-terminated strings, less convenient calls to the strcat( ) and strcmp( ) functions would be required. Because C++ string objects can be freely mixed with C-style strings, there is no disadvantage to using them in your program - and there are considerable benefits to be gained. Ch vic s d dng m vi chui x l c hon thnh. Chng hn, + thng ghp ni nhng chui, v > c quen s so snh hai chui. Hon thnh nhng thao tc ny ang s dng C- kiu, nhng chui c kt thc bi s khng, nhng s gi t tin li hn ti strcat() v strcmp() nhng chc nng c yu cu. V C++ string nhng i tng cn ty thch pha trn vi nhng chui kiu C, khng c s bt li s dng h trong chng trnh (ca) cc bn - v c nhng li ch ng k s c kim c. There is one other thing to notice in the preceding program: The size of the strings are not specified. string objects are automatically sized to hold the string that they are given. Thus, when assigning or concatenating strings, the target string will grow as needed to accommodate the size of the new string. It is not possible to overrun the end of the string. This dynamic aspect of string objects is one of the ways that they are better than null-terminated strings (which are subject to boundary overruns). C mt th khc ch trong chng trnh c trc: kch thc (ca) nhng chui khng c ch r. String nhng i tng t ng c o c gi chui iu h c cho. Nh vy, khi s ch nh (nhng li) hay vic ghp ni nhng chui, nh chui ch ln ln chui nh c cn iu tit kch thc mi. N khng c th xy ra i vi s trn qua kt thc ca chui. Kha cnh ng ny ca string nhng i tng l mt trong s nhng cch iu h tt hn hn nhng chui c kt thc bi s khng ( mchinh phc ti ranh gii nhng s trn qua). The following program demonstrates the insert( ), erase( ), and replace( ) functions: Chng trnh sau y trnh din insert(), erase(), v replace( ) nhng chc nng:

866

// Demonstrate insert(), erase(), and replace(). #include <iostream> #include <string> using namespace std; int main() { string str1("This is a test"); string str2("ABCDEFG"); cout << "Initial strings:\n"; cout << "str1: " << str1 << endl; cout << "str2: " << str2 << "\n\n"; // demonstrate insert() cout << "Insert str2 into str1:\n"; str1.insert(5, str2); cout << str1 << "\n\n"; // demonstrate erase() cout << "Remove 7 characters from str1:\n"; str1.erase(5, 7); cout << str1 <<"\n\n"; // demonstrate replace cout << "Replace 2 characters in str1 with str2:\n"; str1.replace(5, 2, str2); cout << str1 << endl; return 0; } The output produced by this program is shown here: u rao c sn xut bi chng trnh ny c cho thy y Initial strings: str1: This is a test str2: ABCDEFG Insert str2 into str1: This ABCDEFGis a test Remove 7 characters from str1: This is a test Replace 2 characters in str1 with str2: This ABCDEFG a test Since string defines a data type, it is possible to create containers that hold objects of type string. For example, here is a better way to write the dictionary program shown earlier: T nh ngha string mt d liu nh my, c th xy ra to ra nhng cngten m gi nhng i tng ca kiu string. Chng hn, y l mt cch tt hn hn vit chng trnh t in c cho thy trc : // Use a map of strings to create a dictionary. 867

#include <iostream> #include <map> #include <string> using namespace std; int main() { map<string, string> dictionary; dictionary.insert(pair<string, string>("house", "A place of dwelling.")); dictionary.insert(pair<string, string>("keyboard", "An input device.")); dictionary.insert(pair<string, string>("programming", "The act of writing a program.")); dictionary.insert(pair<string, string>("STL", "Standard Template Library")); string s; cout << "Enter word: "; cin >> s; map<string, string>::iterator p; p = dictionary.find(s); if(p != dictionary.end()) cout << "Definition: " << p->second; else cout << "Word not in dictionary.\n"; return 0; }

EXERCISES:
Using object of type string, store the following string in a list. S dng i tng ca kiu string, ct gi chui sau y trong mt list one six two seven three eight four nine five ten

Next, sort the list. Finally, display the sorted list. Tip theo, phn loi danh sch. Cui cng, trnh by danh sch c phn loi. Since string is container, it can be used with the standard algorithm. Create a program that inputs a string from the user. Then, using count(), report how many es are in the string. T string l cha, n c th c s dng vi gii thut tiu chun. To ra mt 868

chng trnh m nhp vo mt chui t ngi s dng. Ri, s dng count(), bo co c nhiu ngi dng trong chui. Modify your answer to Exercise 2 so that it report the number of characters that are lowercase. (Hint :use count-if()). Sa i nhng cu tr li cc bn bi tp 2 sao cho n bo co s c tnh m thng. (Gi : s s dng count- nu ()). The string class is a specialization of what template class ? Lp string l mt chuyn mn ha ca lp khung mu no?

SKILLS CHECK KIM TRA K NNG


Mastery Skills Check Kim tr k nng tng hp At this point you should be able to perform the following exercises and answer the questions. Ti im ny bn cn phi c kh nng thc hin nhng bi tp di v tr li nhng cu hi. How does the STL make it easier for you to create more reliable programs? Hy lm vi STL lm n d dng hn cho bn to ra nhng chng trnh ng tin cy hn? Define a container, an iterator, and a algorithm as they relate to the STL nh ngha mt ci cha, mt iterator, v mt thut ton lin quan n STL. Write a program that creates a ten-element vector that contains the numbers 1 through 10. Next, copy only the even elements from this vector into a list. Vit mt chng trnh m to ra mt vect cha 10 phn t m cha ng nhng s t 1 n 10. Tip theo, ch sao chp nhng phn t chn t vect ny vo trong mt danh sch. Give one advantage of using the string data type. Give one disadvantage.

869

Cho mt li th s dng kiu d liu string. a cho mt s bt li. What is a predicate? Mt v t l g? On your own, recode your answer to Exercise 2 in Section 14.5 so that is uses string. T cc bn, recode cu tr li cc bn Baif tp 2 trong Mc 14.5 v vy iu l nhng s s dng string. Begin exploring the STL function objects. To get started, examine the two standard classes unary_function and binary_function, which aid in the construction of function objects. Bt u vic khm ph nhng i tng chc nng STL. bt u bt u, kho st nhng hai lp tiu chununary_function vbinary_function, s gip no trong nhng i tng xy dng mt hm. Study the STL documentation that comes with your compiler. You will find several interesting features and techniques. Hc ti liu STL m n vi ngi bin tp cc bn. Bn s tm thy vi c tnh th v v k thut. Mastery Skills Check Kim tr k nng tng hp This section checks how well you have integrated material in this chapter with that from the preceding chapters. Mc ny kim tra bn hiu c bng cht tng hp trong chng ny vi iu t nhng chng c trc tt ra sao. You have come a long way since Chapter 1. Take some time to skim through the book again. As you do so, think about ways you can improve the examples (especially those in the first six chapters) so that they take advantage of all the features of C++ you have learned. Bn c n l mt ng di t Chng 1. a mt lc no i s lt qua thng qua sch ln na. Nh bn lm v th, ngh v nhng cch bn c th ci thin nhng v d ( Mt cch c bit l ngi trong nhng su chng u tin) V th m chng tn dng tt c nhng c tnh) C++ bn c hc. Programming is learned best by doing. Write many C++ programs. Try to exercise those features of C++ that are unique to it.

870

Chng trnh c hc tt nht phi lm. Vit nhiu chng trnh C++. Th luyn tp nhng c tnh C++ m duy nht i vi n. Continue to explore the STL. In the future, many programming tasks will be framed in terms of the STL because often a program can be reduced to manipulations of containers by algorithms. Tip tc tm hiu trong STL. Vic trong tng lai, nhiu ngi lp trnh giao nhim v s c kt cu di dng STL bi v thng mt chng trnh c th c gim bt ti nhng s thao tc cha nhng gii thut. Finally, remember : C++ gives you unprecedented power. It is important that you learn to use this power wisely. Because of this power, C++ lets you push the limits of your programming ability. However, if this power is misused, you can also create programs that are hard to understand, nearly impossible to follow, and extremely difficult to maintain. C++ is a powerful tool. But, like any other tool, it is only as good as the person using it. Cui cng, nh: C++ a cho bn sc mnh cha h thy. Quan trng rng bn hc sc mnh ny mt cch khn ngoan. Bi v sc mnh ny, trong C++ cho bn y nhng kh nng lp trnh. Tuy nhin , nu sc mnh ny c s dng, bn c th cng to ra nhng chng trnh m s kh khn hn, gn khng th no i theo sau, v v cng kh ti phn ch yu. C++ l mt cng c mnh. Nhng, cng nh mi cng c khc, ch l li ch nh ngi ang s dng n.

A
A Few More Between C and C
For the most part, C++ is as superset of ANSI-standard C, and virtually all C program are also C++ program. However, a few differnces do exist, sevaral of which were mentioned in Chapter 1.Here are a few more that you should be aware of: a s phn, C++ nh l nh tp hp ca C ANSI- chun, v thc t tt c C lp trnh l chng trnh C++ na. Tuy nhin, vi differnces tn ti, sevaral (ca) m c cp trong Chng 1. yare vi na bn cn phi (th) thc (ca) : A small but potentially important difference between C and C++ is that in C, a character constant is automatically elevated to an integer, whereas in C++, it is not. Mt Nh m tim tng quan trng khc bit gia C v C++ l trong C, mt 871

hng s c tnh t ng c nng ln ti mt s nguyn, trong khi m trong C++, n khng t ng tng. In C, it is not an error to declare a global varible sveral times, even though it is bad programming practice. In C++, this is an error. Trong C, khng l mt li khai bo nhng mt ln varible sveral ton cu, mc d l thc hnh lp trnh xu. Trong C++, y l mt li. In C, an identifier will have at least 31 significant characters. In C++, all characters are considered signnficant. However, from a practical point of view, extremely long identifiers are unwieldy and are seldom needed. Trong C, mt nh danh s c t nht 31 nhng c tnh quan trng. Trong C ++, tt c cc c tnh u l t tnh c xem xt. Tuy nhin, t mt im thc hnh ca t tnh, t nhng nh danh th cng knh v c cn t khi. In C, you can call main() from within a program, although this would be unusual. In C++, this is not allowed. Trong C, bn c th gi main() t bn trong mt chng trnh, mc d iu ny th khc thng. Trong C++, y khng c cho php. In C, you cannot take the address of a register variable. In C++, you can. Trong C, bn khng th cm ly a ch bin ca mt thanh ghi. Trong C++, bn c th. In C, the type wchar_t is defined with a typedef. In C++, wchar_t is a keyword. Trong C, kiu wchar_t c nh ngha vi mt typedef. Trong C++, wchar_t l mt t kha. ********************** HT **********************

872

Você também pode gostar