The C++ Programming Language Third Edition phần 5 doc

102 1.1K 0
The C++ Programming Language Third Edition phần 5 doc

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Section 15.2.4.1 Programming Virtual Bases 399 v oi d W in w_ wi th _b or de r::d w() vo id Wi nd ow _w it h_ bo rd er dr aw { W in w::d w(); Wi nd ow dr aw o wn _d w(); // display the border ow n_ dr aw } v oi d W in w_ wi th _m en u::d w() vo id Wi nd ow _w it h_ me nu dr aw { W in w::d w(); Wi nd ow dr aw o wn _d w(); // display the menu ow n_ dr aw } v oi d C lo ck :d w() vo id Cl oc k: dr aw { W in w::d w(); Wi nd ow dr aw W in w_ wi th _b or de r::o wn _d w(); Wi nd ow _w it h_ bo rd er ow n_ dr aw W in w_ wi th _m en u::o wn _d w(); Wi nd ow _w it h_ me nu ow n_ dr aw o wn _d w(); // display the clock face and hands ow n_ dr aw } Casting from a v ir tu al base class to a derived class is discussed in §15.4.2 vi rt ua l 15.2.5 Using Multiple Inheritance [hier.using.mi] The simplest and most obvious use of multiple inheritance is to ‘‘glue’’ two otherwise unrelated classes together as part of the implementation of a third class The S at el li te class built out of the Sa te ll it e T as k and D is pl ay ed classes in §15.2 is an example of this This use of multiple inheritance is Ta sk Di sp la ye d crude, effective, and important, but not very interesting Basically, it saves the programmer from writing a lot of forwarding functions This technique does not affect the overall design of a program significantly and can occasionally clash with the wish to keep implementation details hidden However, a technique doesn’t have to be clever to be useful Using multiple inheritance to provide implementations for abstract classes is more fundamental in that it affects the way a program is designed Class B B_ iv al _s li de r (§12.3) is an example: BB _i va l_ sl id er c la ss B B_ iv al _s li de r cl as s BB _i va l_ sl id er : p ub li c I va l_ sl id er // interface pu bl ic Iv al _s li de r , p ro te ct ed B Bs li de r // implementation pr ot ec te d BB sl id er { // implementation of functions required by ‘Ival_slider’ and ‘BBslider’ // using the facilities provided by ‘BBslider’ }; In this example, the two base classes play logically distinct roles One base is a public abstract class providing the interface and the other is a protected concrete class providing implementation ‘‘details.’’ These roles are reflected in both the style of the classes and in the access control provided The use of multiple inheritance is close to essential here because the derived class needs to override virtual functions from both the interface and the implementation Multiple inheritance allows sibling classes to share information without introducing a dependence on a unique common base class in a program This is the case in which the so-called The C++ Programming Language, Third Edition by Bjarne Stroustrup Copyright ©1997 by AT&T Published by Addison Wesley Longman, Inc ISBN 0-201-88954-4 All rights reserved 400 Class Hierarchies Chapter 15 diamond-shaped inheritance occurs (for example, the R ad io (§15.2.4) and C lo ck (§15.2.4.1)) A Ra di o Cl oc k virtual base class, as opposed to an ordinary base class, is needed if the base class cannot be replicated I find that a diamond-shaped inheritance lattice is most manageable if either the virtual base class or the classes directly derived from it are abstract classes For example, consider again the I va l_ bo x classes from §12.4 In the end, I made all the I va l_ bo x classes abstract to reflect their Iv al _b ox Iv al _b ox role as pure interfaces Doing that allowed me to place all implementation details in specific implementation classes Also, all sharing of implementation details was done in the classical hierarchy of the windows system used for the implementation It would make sense for the class implementing a P op up _i va l_ sl id er to share most of the Po pu p_ iv al _s li de r implementation of the class implementing a plain I va l_ sl id er After all, these implementation Iv al _s li de r classes would share everything except the handling of prompts However, it would then seem natural to avoid replication of I va l_ sl id er objects within the resulting slider implementation objects Iv al _s li de r Therefore, we could make I va l_ sl id er a virtual base: Iv al _s li de r c la ss B B_ iv al _s li de r : p ub li c v ir tu al I va l_ sl id er p ro te ct ed B Bs li de r { /* */ }; cl as s BB _i va l_ sl id er pu bl ic vi rt ua l Iv al _s li de r, pr ot ec te d BB sl id er c la ss P op up _i va l_ sl id er : p ub li c v ir tu al I va l_ sl id er { /* */ }; cl as s Po pu p_ iv al _s li de r pu bl ic vi rt ua l Iv al _s li de r c la ss B B_ po pu p_ iv al _s li de r cl as s BB _p op up _i va l_ sl id er : p ub li c v ir tu al P op up _i va l_ sl id er p ro te ct ed B B_ iv al _s li de r { /* */ }; pu bl ic vi rt ua l Po pu p_ iv al _s li de r, pr ot ec te d BB _i va l_ sl id er or graphically: I va l_ sl id er Iv al _s li de r P op up _i va l_.sl id er Po pu p_ iv al _s li de r B Bs li de r BB sl id er B B_ iv al _s li de r BB _i va l_ sl id er B B_ po pu p_ iv al _s li de r BB _p op up _i va l_ sl id er It is easy to imagine further interfaces derived from P op up _i va l_ sl id er and further implementation Po pu p_ iv al _s li de r classes derived from such classes and B B_ po pu p_ sl id er BB _p op up _s li de r If we take this idea to its logical conclusion, all of the derivations from the abstract classes that constitute our application’s interfaces would become virtual This does indeed seem to be the most logical, general, and flexible approach The reason I didn’t that was partly historical and partly because the most obvious and common techniques for implementing virtual bases impose time and space overhead that make their extensive use within a class unattractive Should this overhead become an issue for an otherwise attractive design, note that an object representing an I va l_ sl id er Iv al _s li de r usually holds only a virtual table pointer As noted in §15.2.4, such an abstract class holding no variable data can be replicated without ill effects Thus, we can eliminate the virtual base in favor of ordinary ones: c la ss B B_ iv al _s li de r : p ub li c I va l_ sl id er p ro te ct ed B Bs li de r { /* */ }; cl as s BB _i va l_ sl id er pu bl ic Iv al _s li de r, pr ot ec te d BB sl id er c la ss P op up _i va l_ sl id er : p ub li c I va l_ sl id er { /* */ }; cl as s Po pu p_ iv al _s li de r pu bl ic Iv al _s li de r c la ss B B_ po pu p_ iv al _s li de r cl as s BB _p op up _i va l_ sl id er : p ub li c P op up _i va l_ sl id er p ro te ct ed B B_ iv al _s li de r { /* */ }; pu bl ic Po pu p_ iv al _s li de r, pr ot ec te d BB _i va l_ sl id er or graphically: The C++ Programming Language, Third Edition by Bjarne Stroustrup Copyright ©1997 by AT&T Published by Addison Wesley Longman, Inc ISBN 0-201-88954-4 All rights reserved Section 15.2.5 Using Multiple Inheritance I va l_ sl id er Iv al _s li de r I va l_ sl id er Iv al _s li de r P op up _i va l_.sl id er Po pu p_ iv al _s li de r 401 B Bs li de r BB sl id er B B_ iv al _s li de r BB _i va l_ sl id er B B_ po pu p_ iv al _s li de r BB _p op up _i va l_ sl id er This is most likely a viable optimization to the admittedly cleaner alternative presented previously 15.2.5.1 Overriding Virtual Base Functions [hier.dominance] A derived class can override a virtual function of its direct or indirect virtual base class In particular, two different classes might override different virtual functions from the virtual base In that way, several derived classes can contribute implementations to the interface presented by a virtual base class For example, the W in w class might have functions s et _c ol or Wi nd ow se t_ co lo r() and p ro mp t() In pr om pt that case, W in w_ wi th _b or de r might override s et _c ol or Wi nd ow _w it h_ bo rd er se t_ co lo r() as part of controlling the color scheme and W in w_ wi th _m en u might override p ro mp t() as part of its control of user interacWi nd ow _w it h_ me nu pr om pt tions: c la ss W in w { cl as s Wi nd ow // v ir tu al s et _c ol or Co lo r) = vi rt ua l se t_ co lo r(C ol or 0; v ir tu al v oi d p ro mp t() = vi rt ua l vo id pr om pt 0; }; // set background color c la ss W in w_ wi th _b or de r : p ub li c v ir tu al W in w { cl as s Wi nd ow _w it h_ bo rd er pu bl ic vi rt ua l Wi nd ow // s et _c ol or Co lo r); se t_ co lo r(C ol or // control background color }; c la ss W in w_ wi th _m en u : p ub li c v ir tu al W in w { cl as s Wi nd ow _w it h_ me nu pu bl ic vi rt ua l Wi nd ow // v oi d p ro mp t(); // control user interactions vo id pr om pt }; c la ss M y_ wi nd ow : p ub li c W in w_ wi th _m en u, p ub li c W in w_ wi th _b or de r { cl as s My _w in w pu bl ic Wi nd ow _w it h_ me nu pu bl ic Wi nd ow _w it h_ bo rd er // }; What if different derived classes override the same function? This is allowed if and only if some overriding class is derived from every other class that overrides the function That is, one function must override all others For example, M y_ wi nd ow could override p ro mp t() to improve on what My _w in w pr om pt W in w_ wi th _m en u provides: Wi nd ow _w it h_ me nu c la ss M y_ wi nd ow : p ub li c W in w_ wi th _m en u, p ub li c W in w_ wi th _b or de r { cl as s My _w in w pu bl ic Wi nd ow _w it h_ me nu pu bl ic Wi nd ow _w it h_ bo rd er // v oi d p ro mp t(); // don’t leave user interactions to base vo id pr om pt }; or graphically: The C++ Programming Language, Third Edition by Bjarne Stroustrup Copyright ©1997 by AT&T Published by Addison Wesley Longman, Inc ISBN 0-201-88954-4 All rights reserved 402 Class Hierarchies Chapter 15 W in w { s et _c ol or Wi nd ow se t_ co lo r(), p ro mp t() } pr om pt W in w_ wi th _b or de r { s et _c ol or } Wi nd ow _w it h_ bo rd er se t_ co lo r() W in w_ wi th _m en u { p ro mp t() } Wi nd ow _w it h_ me nu pr om pt M y_ wi nd ow { p ro mp t() } My _w in w pr om pt If two classes override a base class function, but neither overrides the other, the class hierarchy is an error No virtual function table can be constructed because a call to that function on the complete object would have been ambiguous For example, had R ad io in §15.2.4 not declared Ra di o w ri te wr it e(), the declarations of w ri te wr it e() in R ec ei ve r and T ns mi tt er would have caused an error Re ce iv er Tr an sm it te r when defining R ad io As with R ad io such a conflict is resolved by adding an overriding function Ra di o Ra di o, to the most derived class A class that provides some – but not all – of the implementation for a virtual base class is often called a ‘‘mixin.’’ 15.3 Access Control [hier.access] A member of a class can be p ri va te p ro te ct ed or p ub li c: pr iv at e, pr ot ec te d, pu bl ic – If it is p ri va te its name can be used only by member functions and friends of the class in pr iv at e, which it is declared – If it is p ro te ct ed its name can be used only by member functions and friends of the class in pr ot ec te d, which it is declared and by member functions and friends of classes derived from this class (see §11.5) – If it is p ub li c, its name can be used by any function pu bl ic This reflects the view that there are three kinds of functions accessing a class: functions implementing the class (its friends and members), functions implementing a derived class (the derived class’ friends and members), and other functions This can be presented graphically: general users derived class’ member functions and friends own member functions and friends public: protected: private: The access control is applied uniformly to names What a name refers to does not affect the control of its use This means that we can have private member functions, types, constants, etc., as well as private data members For example, an efficient non-intrusive (§16.2.1) list class often requires data structures to keep track of elements Such information is best kept private: The C++ Programming Language, Third Edition by Bjarne Stroustrup Copyright ©1997 by AT&T Published by Addison Wesley Longman, Inc ISBN 0-201-88954-4 All rights reserved Section 15.3 Access Control 403 t em pl at e cl as s Li st p ri va te pr iv at e: s tr uc t L in k { T v al L in k* n ex t; }; st ru ct Li nk va l; Li nk ne xt s tr uc t C hu nk { st ru ct Ch un k e nu m { c hu nk _s iz e = }; en um ch un k_ si ze 15 L in k v ch un k_ si ze ; Li nk v[c hu nk _s iz e] C hu nk n ex t; Ch un k* ne xt }; c la ss U nd er fl ow { }; cl as s Un de rf lo w C hu nk a ll oc at ed Ch un k* al lo ca te d; L in k* f re e; Li nk fr ee L in k* g et _f re e(); Li nk ge t_ fr ee L in k* h ea d; Li nk he ad p ub li c: pu bl ic v oi d i ns er t(T ; vo id in se rt T) T g et ; ge t() // }; t em pl at e: in se rt T va l) { L in k* l nk = g et _f re e(); Li nk ln k ge t_ fr ee l nk va l = v al ln k->v al va l; l nk ne xt = h ea d; ln k->n ex t he ad h ea d = l nk he ad ln k; } t em pl at e: ge t_ fr ee { i f (f re e == { if fr ee 0) // allocate a new chunk and place its Links on the free list } L in k* p = f re e; Li nk fr ee f re e = f re e->n ex t; fr ee fr ee ne xt r et ur n p re tu rn p; } t em pl at e: ge t() { i f (h ea d == t hr ow U nd er fl ow ; if he ad 0) th ro w Un de rf lo w() L in k* p h ea d; Li nk p= he ad h ea d = p ne xt he ad p->n ex t; p ne xt = f re e; p->n ex t fr ee f re e = p fr ee p; r et ur n p va l; re tu rn p->v al } The L is t: return type of g et _f re e() is mentioned before the name L is t: ge t_ fr ee full name L is t The C++ Programming Language, Third Edition by Bjarne Stroustrup Copyright ©1997 by AT&T Published by Addison Wesley Longman, Inc ISBN 0-201-88954-4 All rights reserved 404 Class Hierarchies Chapter 15 Nonmember functions (except friends) not have such access: v oi d w ou ld _b e_ me dd le r(L is t* p p) { L is t: Li nk 0; // q = p fr ee p->f re e; // i f (L is t 1) { if Li st T>: Ch un k: ch un k_ si ze 31 // } } // error: List::Link is private // error: List::free is private // error: List::Chunk::chunk_size is private In a c la ss a member is by default private; in a s tr uc t, a member is by default public (§10.2.8) cl as s, st ru ct 15.3.1 Protected Members [hier.protected] As an example of how to use p ro te ct ed members, consider the W in w example from §15.2.4.1 pr ot ec te d Wi nd ow The o wn _d w() functions were (deliberately) incomplete in the service they provided They ow n_ dr aw were designed as building blocks for use by derived classes (only) and are not safe or convenient for general use The d w() operations, on the other hand, were designed for general use This dr aw distinction can be expressed by separating the interface of the W in w classes in two, the p ro te ct ed Wi nd ow pr ot ec te d interface and the p ub li c interface: pu bl ic c la ss W in w_ wi th _b or de r { cl as s Wi nd ow _w it h_ bo rd er p ub li c: pu bl ic v ir tu al v oi d d w(); vi rt ua l vo id dr aw // p ro te ct ed pr ot ec te d: v oi d o wn _d w(); vo id ow n_ dr aw // other tool-building stuff p ri va te pr iv at e: // representation, etc }; A derived class can access a base class’ protected members only for objects of its own type: c la ss B uf fe r { cl as s Bu ff er p ro te ct ed pr ot ec te d: c r a 12 8]; ch ar a[1 28 // }; c la ss L in ke d_ bu ff er : p ub li c B uf fe r { /* */ }; cl as s Li nk ed _b uf fe r pu bl ic Bu ff er c la ss C yc li c_ bu ff er : p ub li c B uf fe r { cl as s Cy cl ic _b uf fe r pu bl ic Bu ff er // v oi d f Li nk ed _b uf fe r* p { vo id f(L in ke d_ bu ff er p) a 0] = a[0 0; // ok: access to cyclic_buffer’s own protected member p a[0 = p->a 0] 0; // error: access to protected member of different type } }; The C++ Programming Language, Third Edition by Bjarne Stroustrup Copyright ©1997 by AT&T Published by Addison Wesley Longman, Inc ISBN 0-201-88954-4 All rights reserved Section 15.3.1 Protected Members 405 This prevents subtle errors that would otherwise occur when one derived class corrupts data belonging to other derived classes 15.3.1.1 Use of Protected Members [hier.protected.use] The simple private/public model of data hiding serves the notion of concrete types (§10.3) well However, when derived classes are used, there are two kinds of users of a class: derived classes and ‘‘the general public.’’ The members and friends that implement the operations on the class operate on the class objects on behalf of these users The private/public model allows the programmer to distinguish clearly between the implementers and the general public, but it does not provide a way of catering specifically to derived classes Members declared p ro te ct ed are far more open to abuse than members declared p ri va te In pr ot ec te d pr iv at e particular, declaring data members protected is usually a design error Placing significant amounts of data in a common class for all derived classes to use leaves that data open to corruption Worse, protected data, like public data, cannot easily be restructured because there is no good way of finding every use Thus, protected data becomes a software maintenance problem Fortunately, you don’t have to use protected data; p ri va te is the default in classes and is usually pr iv at e the better choice In my experience, there have always been alternatives to placing significant amounts of information in a common base class for derived classes to use directly Note that none of these objections are significant for protected member functions; p ro te ct ed is a pr ot ec te d fine way of specifying operations for use in derived classes The I va l_ sl id er in §12.4.2 is an examIv al _s li de r ple of this Had the implementation class been p ri va te in this example, further derivation would pr iv at e have been infeasible Technical examples illustrating access to members can be found in §C.11.1 15.3.2 Access to Base Classes [hier.base.access] Like a member, a base class can be declared p ri va te p ro te ct ed or p ub li c For example: pr iv at e, pr ot ec te d, pu bl ic c la ss X : p ub li c B { /* */ }; cl as s pu bl ic c la ss Y : p ro te ct ed B { /* */ }; cl as s pr ot ec te d c la ss Z : p ri va te B { /* */ }; cl as s pr iv at e Public derivation makes the derived class a subtype of its base; this is the most common form of derivation Protected and private derivation are used to represent implementation details Protected bases are useful in class hierarchies in which further derivation is the norm; the I va l_ sl id er from Iv al _s li de r §12.4.2 is a good example of that Private bases are most useful when defining a class by restricting the interface to a base so that stronger guarantees can be provided For example, V ec adds Ve c range checking to its private base v ec to r (§3.7.1) and the l is t of pointers template adds type checkve ct or li st ing to its l is t base (§13.5) li st vo id The access specifier for a base class can be left out In that case, the base defaults to a private base for a c la ss and a public base for a s tr uc t For example: cl as s st ru ct c la ss X X : B { /* */ }; cl as s XX s tr uc t Y Y : B { /* */ }; st ru ct YY // B is a private base // B is a public base For readability, it is best always to use an explicit access specifier The C++ Programming Language, Third Edition by Bjarne Stroustrup Copyright ©1997 by AT&T Published by Addison Wesley Longman, Inc ISBN 0-201-88954-4 All rights reserved 406 Class Hierarchies Chapter 15 The access specifier for a base class controls the access to members of the base class and the conversion of pointers and references from the derived class type to the base class type Consider a class D derived from a base class B B: – If B is a p ri va te base, its public and protected members can be used only by member funcpr iv at e tions and friends of D Only friends and members of D can convert a D to a B D D* B* – If B is a p ro te ct ed base, its public and protected members can be used only by member pr ot ec te d functions and friends of D and by member functions and friends of classes derived from D D Only friends and members of D and friends and members of classes derived from D can convert a D to a B D* B* – If B is a p ub li c base, its public members can be used by any function In addition, its propu bl ic tected members can be used by members and friends of D and members and friends of classes derived from D Any function can convert a D to a B D D* B* This basically restates the rules for member access (§15.3) We choose access for bases in the same way as for members For example, I chose to make B Bw in w a p ro te ct ed base of I va l_ sl id er BB wi nd ow pr ot ec te d Iv al _s li de r (§12.4.2) because B Bw in w was part of the implementation of I va l_ sl id er rather than part of its BB wi nd ow Iv al _s li de r interface However, I couldn’t completely hide B Bw in w by making it a private base because I BB wi nd ow wanted to be able to derive further classes from I va l_ sl id er and those derived classes would need Iv al _s li de r, access to the implementation Technical examples illustrating access to bases can be found in §C.11.2 15.3.2.1 Multiple Inheritance and Access Control [hier.mi.access] If a name or a base class can be reached through multiple paths in a multiple inheritance lattice, it is accessible if it is accessible through any path For example: s tr uc t B { st ru ct i nt m in t m; s ta ti c i nt s m; st at ic in t sm // }; c la ss D : p ub li c v ir tu al B { /* */ } ; cl as s D1 pu bl ic vi rt ua l c la ss D : p ub li c v ir tu al B { /* */ } ; cl as s D2 pu bl ic vi rt ua l c la ss D D : p ub li c D 1, p ri va te D { /* */ }; cl as s DD pu bl ic D1 pr iv at e D2 D D* p d = n ew D D; DD pd ne w DD B p b = p d; B* pb pd i nt i = p d->m in t i1 pd m; // ok: accessible through D1 // ok: accessible through D1 If a single entity is reachable through several paths, we can still refer to it without ambiguity For example: c la ss X : p ub li c B { /* */ } ; cl as s X1 pu bl ic c la ss X : p ub li c B { /* */ } ; cl as s X2 pu bl ic c la ss X X : p ub li c X 1, p ub li c X { /* */ }; cl as s XX pu bl ic X1 pu bl ic X2 X X* p xx = n ew X X; XX px x ne w XX i nt i = p xx m; in t i1 px x->m // error, ambiguous: XX::X1::B::m or XX::X2::B::m i nt i = p xx sm in t i2 px x->s m; // ok: there is only one B::sm in an XX The C++ Programming Language, Third Edition by Bjarne Stroustrup Copyright ©1997 by AT&T Published by Addison Wesley Longman, Inc ISBN 0-201-88954-4 All rights reserved Section 15.3.2.1 Multiple Inheritance and Access Control 407 15.3.2.2 Using-Declarations and Access Control [hier.access.using] A using-declaration cannot be used to gain access to additional information It is simply a mechanism for making accessible information more convenient to use On the other hand, once access is available, it can be granted to other users For example: c la ss B { cl as s p ri va te pr iv at e: i nt a in t a; p ro te ct ed pr ot ec te d: i nt b in t b; p ub li c: pu bl ic i nt c in t c; }; c la ss D : p ub li c B { cl as s pu bl ic p ub li c: pu bl ic u si ng B :a us in g B: a; // error: B::a is private u si ng B :b us in g B: b; // make B::b publically available through D }; When a using-declaration is combined with private or protected derivation, it can be used to specify interfaces to some, but not all, of the facilities usually offered by a class For example: c la ss B B : p ri va te B { cl as s BB pr iv at e u si ng B :b us in g B: b; u si ng B :c us in g B: c; }; // give access to B::b and B::c, but not B::a See also §15.2.2 15.4 Run-Time Type Information [hier.rtti] A plausible use of the I va l_ bo xes defined in §12.4 would be to hand them to a system that conIv al _b ox trolled a screen and have that system hand objects back to the application program whenever some activity had occurred This is how many user-interfaces work However, a user-interface system will not know about our I va l_ bo xes The system’s interfaces will be specified in terms of the Iv al _b ox system’s own classes and objects rather than our application’s classes This is necessary and proper However, it does have the unpleasant effect that we lose information about the type of objects passed to the system and later returned to us Recovering the ‘‘lost’’ type of an object requires us to somehow ask the object to reveal its type Any operation on an object requires us to have a pointer or reference of a suitable type for the object Consequently, the most obvious and useful operation for inspecting the type of an object at run time is a type conversion operation that returns a valid pointer if the object is of the expected type and a null pointer if it isn’t The d yn am ic _c as t operator does exactly that For example, dy na mi c_ ca st assume that ‘‘the system’’ invokes m y_ ev en t_ nd le r() with a pointer to a B Bw in w, where an my _e ve nt _h an dl er BB wi nd ow activity has occurred I then might invoke my application code using I va l_ bo x’s d o_ so me th in g(): Iv al _b ox _s om et hi ng The C++ Programming Language, Third Edition by Bjarne Stroustrup Copyright ©1997 by AT&T Published by Addison Wesley Longman, Inc ISBN 0-201-88954-4 All rights reserved 408 Class Hierarchies Chapter 15 v oi d m y_ ev en t_ nd le r(B Bw in w* p w) vo id my _e ve nt _h an dl er BB wi nd ow pw { i f (I va l_ bo x* p b = d yn am ic _c as t(p w)) if Iv al _b ox pb dy na mi c_ ca st Iv al _b ox pw p b->d o_ so me th in g(); pb _s om et hi ng e ls e { el se // Oops! unexpected event } } // does pw point to an Ival_box? One way of explaining what is going on is that d yn am ic _c as t translates from the implementationdy na mi c_ ca st oriented language of the user-interface system to the language of the application It is important to note what is not mentioned in this example: the actual type of the object The object will be a particular kind of I va l_ bo x, say an I va l_ sl id er implemented by a particular kind of B Bw in w, say a Iv al _b ox Iv al _s li de r, BB wi nd ow B Bs li de r It is neither necessary nor desirable to make the actual type of the object explicit in this BB sl id er interaction between ‘‘the system’’ and the application An interface exists to represent the essentials of an interaction In particular, a well-designed interface hides inessential details Graphically, the action of p b = d yn am ic _c as t(p w) pb dy na mi c_ ca st Iv al _b ox pw can be represented like this: p w B Bw in w pw BB wi nd ow B Bs li de r BB sl id er I va l_ bo x Iv al _b ox pb pb I va l_ sl id er Iv al _s li de r B B_ iv al _s li de r BB _i va l_ sl id er The arrows from p w and p b represent the pointers into the object passed, whereas the rest of the pw pb arrows represent the inheritance relationships between the different parts of the object passed The use of type information at run time is conventionally referred to as ‘‘run-time type information’’ and often abbreviated to RTTI Casting from a base class to a derived class is often called a downcast because of the convention of drawing inheritance trees growing from the root down Similarly, a cast from a derived class to a base is called an upcast A cast that goes from a base to a sibling class, like the cast from B Bw in BB wi nd ow to I va l_ bo x, is called a crosscast w Iv al _b ox 15.4.1 Dynamic_cast [hier.dynamic.cast] The d yn am ic _c as t operator takes two operands, a type bracketed by < and >, and a pointer or referdy na mi c_ ca st ence bracketed by ( and ) Consider first the pointer case: d yn am ic _c as t(p p) If p is of type T or an accessible base class of T the result is exactly as if we had simply assigned T* T, p to a T For example: T* The C++ Programming Language, Third Edition by Bjarne Stroustrup Copyright ©1997 by AT&T Published by Addison Wesley Longman, Inc ISBN 0-201-88954-4 All rights reserved 486 Standard Containers Chapter 17 t em pl at e > cl as s al lo ca to r< pa ir co ns t Ke y,T c la ss m ap { cl as s ma p p ub li c: pu bl ic // // map operations: i te to r f in d(c on st k ey _t yp e& k ; it er at or fi nd co ns t ke y_ ty pe k) c on st _i te to r f in d(c on st k ey _t yp e& k c on st co ns t_ it er at or fi nd co ns t ke y_ ty pe k) co ns t; // find element with key k s iz e_ ty pe c ou nt co ns t k ey _t yp e& k c on st si ze _t yp e co un t(c on st ke y_ ty pe k) co ns t; // find number of elements with key k i te to r l ow er _b ou nd co ns t k ey _t yp e& k ; it er at or lo we r_ bo un d(c on st ke y_ ty pe k) // find first element with key k c on st _i te to r l ow er _b ou nd co ns t k ey _t yp e& k c on st co ns t_ it er at or lo we r_ bo un d(c on st ke y_ ty pe k) co ns t; i te to r u pp er _b ou nd co ns t k ey _t yp e& k ; it er at or up pe r_ bo un d(c on st ke y_ ty pe k) // find first element with key greater than k c on st _i te to r u pp er _b ou nd co ns t k ey _t yp e& k c on st co ns t_ it er at or up pe r_ bo un d(c on st ke y_ ty pe k) co ns t; p r e qu al _r an ge co ns t k ey _t yp e& k ; pa ir it er at or it er at or eq ua l_ ng e(c on st ke y_ ty pe k) p r e qu al _r an ge co ns t k ey _t yp e& k c on st pa ir co ns t_ it er at or co ns t_ it er at or eq ua l_ ng e(c on st ke y_ ty pe k) co ns t; // }; A m fi nd k) operation simply yields an iterator to an element with the key k If there is no such m.f in d(k k element, the iterator returned is m en d() For a container with unique keys, such as m ap and s et m.e nd ma p se t, the resulting iterator will point to the unique element with the key k For a container with nonk unique keys, such as m ul ti ma p and m ul ti se t, the resulting iterator will point to the first element that mu lt im ap mu lt is et has that key For example: v oi d f ma p& m m) { m ap st ri ng in t>::i te to r p = m fi nd Go ld ; ma p& m vo id f(m ul ti ma p& m vo id f(m ul ti ma p cl as s al lo ca to r< pa ir co ns t Ke y,T c la ss m ap { cl as s ma p p ub li c: pu bl ic // // list operations: p r i ns er t(c on st v al ue _t yp e& v al ; // insert (key,value) pair pa ir it er at or bo ol in se rt co ns t va lu e_ ty pe va l) i te to r i ns er t(i te to r p os c on st v al ue _t yp e& v al ; // pos is just a hint it er at or in se rt it er at or po s, co ns t va lu e_ ty pe va l) t em pl at e v oi d i ns er t(I n f ir st I n l as t); te mp la te cl as s In vo id in se rt In fi rs t, In la st // insert elements from sequence v oi d e se it er at or p os ; vo id er as e(i te to r po s) s iz e_ ty pe e se co ns t k ey _t yp e& k ; si ze _t yp e er as e(c on st ke y_ ty pe k) v oi d e se it er at or f ir st i te to r l as t); vo id er as e(i te to r fi rs t, it er at or la st v oi d c le ar ; vo id cl ea r() // erase the element pointed to // erase element with key k (if present) // erase range // }; The operation m in se rt va l) attempts to add a (K ey T) pair v al to m Since m ap rely on m.i ns er t(v al Ke y,T va l m ma ps The C++ Programming Language, Third Edition by Bjarne Stroustrup Copyright ©1997 by AT&T Published by Addison Wesley Longman, Inc ISBN 0-201-88954-4 All rights reserved 488 Standard Containers Chapter 17 unique keys, insertion takes place only if there is not already an element in the m with that key The return value of m in se rt va l) is a p r The b oo l is t ru e if v al was actually m.i ns er t(v al pa ir it er at or bo ol bo ol tr ue va l inserted The iterator refers to the element of m holding the key k For example: k v oi d f ma p& m m) { p r p9 9("P au l",9 9) p r::i te to r,b oo l> p = m in se rt p9 9); pa ir ma p::i te to r i = p fi rs t; ma p cl as s al lo ca to r< pa ir co ns t Ke y,T c la ss m ap { cl as s ma p p ub li c: pu bl ic // // capacity: s iz e_ ty pe s iz e() c on st si ze _t yp e si ze co ns t; // number of elements s iz e_ ty pe m ax _s iz e() c on st si ze _t yp e ma x_ si ze co ns t; // size of largest possible map b oo l e mp ty c on st { r et ur n s iz e()==0 } bo ol em pt y() co ns t re tu rn si ze 0; v oi d s wa p(m ap ; vo id sw ap ma p&) }; As usual, a value returned by s iz e() or m ax _s iz e() is a number of elements si ze ma x_ si ze In addition, m ap provides ==, !=, , =, and s wa p() as nonmember functions: ma p sw ap t em pl at e b oo l o pe to r==(c on st m ap Ke y,T Cm p,A bo ol op er at or co ns t ma p&, c on st m ap Ke y,T Cm p,A co ns t ma p&); // similarly !=, , = t em pl at e v oi d s wa p(m ap Ke y,T Cm p,A vo id sw ap ma p&, m ap Ke y,T Cm p,A ma p&); Why would anyone want to compare two m ap When we specifically compare two m ap we usuma ps? ma ps, ally want to know not just if the m ap differ, but also how they differ if they In such cases, we ma ps don’t use == or != However, by providing ==, cl as s al lo ca to r< pa ir co ns t Ke y,T c la ss s td :m ul ti ma p { cl as s st d: mu lt im ap p ub li c: pu bl ic // like map, except: i te to r i ns er t(c on st v al ue _t yp e&); it er at or in se rt co ns t va lu e_ ty pe // returns iterator, not pair // no subscript operator [] }; For example (using C st ri ng _l es s from §17.1.4.1 to compare C-style strings): Cs tr in g_ le ss v oi d f ma p& m m ul ti ma p& m m) mm { m in se rt ma ke _p r("x 4)); m.i ns er t(m ak e_ pa ir x",4 m in se rt ma ke _p r("x 5)); // no effect: there already is an entry for "x" (§17.4.1.7) m.i ns er t(m ak e_ pa ir x",5 // now m["x"] == m m.i ns er t(m ak e_ pa ir x",4 ; mm in se rt ma ke _p r("x 4)) m m.i ns er t(m ak e_ pa ir x",5 ; mm in se rt ma ke _p r("x 5)) // mm now holds both ("x",4) and ("x",5) } This implies that m ul ti ma p cannot support subscripting by key values in the way m ap does The mu lt im ap ma p e qu al _r an ge eq ua l_ ng e(), l ow er _b ou nd lo we r_ bo un d(), and u pp er _b ou nd up pe r_ bo un d() operations (§17.4.1.6) are the primary means of accessing multiple values with the same key Naturally, where several values can exist for a single key, a m ul ti ma p is preferred over a m ap mu lt im ap ma p That happens far more often than people first think when they hear about m ul ti ma p In some ways, mu lt im ap a m ul ti ma p is even cleaner and more elegant than a m ap mu lt im ap ma p Because a person can easily have several phone numbers, a phone book is a good example of a m ul ti ma p I might print my phone numbers like this: mu lt im ap v oi d p ri nt _n um be rs co ns t m ul ti ma p& p ho ne _b oo k) ph on e_ bo ok { t yp ed ef m ul ti ma p: co ns t_ it er at or I; p r b = p ho ne _b oo k.e qu al _r an ge St ro us tr up ; pa ir I,I ph on e_ bo ok eq ua l_ ng e("S tr ou st ru p") f or (I i = b fi rs t; i != b se co nd ++i c ou t cl as s al lo ca to r> and

Ngày đăng: 12/08/2014, 19:21

Từ khóa liên quan

Mục lục

  • The C++ Programming Language (Special 3rd Edition)

    • Part II: Abstraction Mechanisms

      • Ch15 Class Hierarchies

        • 15.3 Access Control

        • 15.4 Run Time Type Information

        • 15.5 Pointers to Members

        • 15.6 Free Store

        • 15.7 Advice

        • 15.8 Exercises

        • Part III: The Standard Library

          • Ch16 Library Organization and Containers

            • 16.1 Standard Library Design

            • 16.2 Container Design

            • 16.3 Vector

            • 16.4 Advice

            • 16.5 Exercises

            • Ch17 Standard Containers

              • 17.1 Standard Containers

              • 17.2 Sequences

              • 17.3 Sequence Adapters

              • 17.4 Associative Containers

              • 17.5 Almost Containers

              • 17.6 Defining a New Container

Tài liệu cùng người dùng

Tài liệu liên quan