📄 libg++.texinfo
字号:
to pass by value. SO you should pick by-reference.You can now create @file{Person.defs.h} via @code{genclass Person ref defs}.This creates a simple skeleton that you must edit. First, add@code{#include "Person.h"} to the top. Second, edit the @code{<T>CMP(a,b)}macro to compare on name, via@example#define <T>CMP(a, b) ( compare(a.name(), b.name()) )@end example@noindentwhich invokes the @code{int compare(const String&, const String&)}function from @file{String.h}. Of course, you could define this in anyother way as well. In fact, the default versions in the skeleton turnout to be OK (albeit inefficient) in this particular example.You may also want to create file @file{SSN.defs.h}. Here, choosingcall-by-value makes sense, and since no other capabilities (likecomparison functions) of the SSNs are used (and the defaults are OKanyway), you'd type@examplegenclass SSN val defs@end example@noindentand then edit to place @code{#include "SSN.h"} at the top.Finally, you can generate the classes. First, generate the baseclass for Maps via@examplegenclass -2 Person ref SSN val Map@end example@noindentThis generates only the abstract class, not the implementation, in file@file{Person.SSN.Map.h} and @file{Person.SSN.Map.cc}. To create theAVL implementation, type@examplegenclass -2 Person ref SSN val AVLMap@end example@noindentThis creates the class @code{PersonSSNAVLMap}, in@file{Person.SSN.AVLMap.h} and @file{Person.SSN.AVLMap.cc}.To use the AVL implementation, compile the two generated @file{.cc} files, andspecify @samp{#include "Person.SSN.AVLMap.h"} in the application program.All other files are included in the right ways automatically.One last consideration, peculiar to Maps, is to pick a reasonabledefault contents when declaring an AVLMap. Zero might be appropriatehere, so you might declare a Map,@examplePersonSSNAVLMap m((SSN)0);@end exampleSuppose you wanted a @code{VHMap} instead of an @code{AVLMap} Besidesgenerating different implementations, there are two differences inhow you should prepare the @file{defs} file. First, because a VHMapuses a C++ array internally, and because C++ array slots are initializeddifferently than single elements, you must ensure that class Personcontains (1) a no-argument constructor, and (2) an assignment operator.You could arrange this via@smallexampleclass Person @{ ...; Person() @{@} void operator = (const Person& p) @{ nm = p.nm; addr = p.addr; @}@};@end smallexample(The lack of action in the constructor is OK here because @code{Strings}possess usable no-argument constructors.)You also need to edit @file{Person.defs.h} to indicate a usable hashfunction and default capacity, via something like@example#include <builtin.h>#define <T>HASH(x) (hashpjw(x.name().chars()))#define DEFAULT_INITIAL_CAPACITY 1000@end exampleSince the @code{hashpjw} function from @file{builtin.h} isappropriate here. Changing the default capacity to a valueexpected to exceed the actual capacity helps to avoid``hidden'' inefficiencies when a new VHMap is created withoutoverriding the default, which is all too easy to do.Otherwise, everything is the same as above, substituting@code{VHMap} for @code{AVLMap}.@node Representations, Expressions, Proto, Top@chapter Variable-Sized Object RepresentationOne of the first goals of the GNU C++ library is to enrich the kinds ofbasic classes that may be considered as (nearly) ``built into'' C++. A gooddeal of the inspiration for these efforts is derived from consideringfeatures of other type-rich languages, particularly Common Lisp and Scheme.The general characteristics of most class and friend operators andfunctions supported by these classes has been heavily influencedby such languages.Four of these types, Strings, Integers, BitSets, and BitStrings (as well asassociated and/or derived classes) require representations suitable formanaging variable-sized objects on the free-store. The basic technique usedfor all of these is the same, although various details necessarily differfrom class to class.The general strategy for representing such objects is to create chunks ofmemory that include both header information (e.g., the size of the object),as well as the variable-size data (an array of some sort) at the endof the chunk. Generally the maximum size of an object is limited tosomething less than all of addressable memory, as a safeguard. The minimumsize is also limited so as not to waste allocations expanding very smallchunks. Internally, chunks are allocated in blocks well-tuned to theperformance of the @code{new} operator.Class elements themselves are merely pointers to these chunks.Most class operations are performed via inline ``translation''functions that perform the required operation on the correspondingrepresentation. However, constructors and assignments operate bycopying entire representations, not just pointers.No attempt is made to control temporary creation in expressionsand functions involving these classes. Users of previous versionsof the classes will note the disappearance of both ``Tmp'' classesand reference counting. These were dropped because, while theydid improve performance in some cases, they obscure classmechanics, lead programmers into the false belief that they need notworry about such things, and occasionally have paradoxical behavior.These variable-sized object classes are integrated as well as possibleinto C++. Most such classes possess converters that allow automaticcoercion both from and to builtin basic types. (e.g., char* to and fromString, long int to and from Integer, etc.). There are pro's and con'sto circular converters, since they can sometimes lead to the conversionfrom a builtin type through to a class function and back to a builtintype without any special attention on the part of the programmer, bothfor better and worse.Most of these classes also provide special-case operators and functionsmixing basic with class types, as a way to avoid constructors in caseswhere the operations do not rely on anything special about therepresentations. For example, there is a special case concatenationoperator for a String concatenated with a char, since building theresult does not rely on anything about the String header. Again, thereare arguments both for and against this approach. Supporting these casesadds a non-trivial degree of (mainly inline) function proliferation, butresults in more efficient operations. Efficiency wins out over parsimonyhere, as part of the goal to produce classes that provide sufficientfunctionality and efficiency so that programmers are not tempted to tryto manipulate or bypass the underlying representations.@node Expressions, Pix, Representations, Top@chapter Some guidelines for using expression-oriented classesThe fact that C++ allows operators to be overloaded for user-definedclasses can make programming with library classes like @code{Integer},@code{String}, and so on very convenient. However, it is worthbecoming familiar with some of the inherent limitations and problemsassociated with such operators.Many operators are @emph{constructive}, i.e., create a new objectbased on some function of some arguments. Sometimes the creationof such objects is wasteful. Most library classes supportingexpressions contain facilities that help you avoid such waste.For example, for @code{Integer a, b, c; ...; c = a + b + a;}, theplus operator is called to sum a and b, creating a new temporary objectas its result. This temporary is then added with a, creating anothertemporary, which is finally copied into c, and the temporaries are thendeleted. In other words, this code might have an effect similar to@code{Integer a, b, c; ...; Integer t1(a); t1 += b; Integer t2(t1);t2 += a; c = t2;}.For small objects, simple operators, and/or non-time/space criticalprograms, creation of temporaries is not a big problem. However, often,when fine-tuning a program, it may be a good idea to rewrite suchcode in a less pleasant, but more efficient manner.For builtin types like ints, and floats, C and C++ compilers alreadyknow how to optimize such expressions to reduce the need fortemporaries. Unfortunately, this is not true for C++ user definedtypes, for the simple (but very annoying, in this context) reason thatnothing at all is guaranteed about the semantics of overloaded operatorsand their interrelations. For example, if the above expression justinvolved ints, not Integers, a compiler might internally convert thestatement into something like @code{ c += a; c += b; c+= a; }, orperhaps something even more clever. But since C++ does not know thatInteger operator += has any relation to Integer operator +, A C++compiler cannot do this kind of expression optimization itself.In many cases, you can avoid construction of temporaries simply byusing the assignment versions of operators whenever possible, sincethese versions create no temporaries. However, for maximum flexibility,most classes provide a set of ``embedded assembly code'' proceduresthat you can use to fully control time, space, and evaluation strategies.Most of these procedures are ``three-address'' procedures that taketwo @code{const} source arguments, and a destination argument. Theprocedures perform the appropriate actions, placing the results inthe destination (which is may involve overwriting old contents). Theseprocedures are designed to be fast and robust. In particular, aliasingis always handled correctly, so that, for example@code{add(x, x, x); } is perfectly OK. (The names of these proceduresare listed along with the classes.)For example, suppose you had an Integer expression@code{ a = (b - a) * -(d / c); }This would be compiled as if it were@code{ Integer t1=b-a; Integer t2=d/c; Integer t3=-t2; Integer t4=t1*t3; a=t4;}But, with some manual cleverness, you might yourself some up with@code{ sub(a, b, a); mul(a, d, a); div(a, c, a); }A related phenomenon occurs when creating your own constructivefunctions returning instances of such types. Suppose you wantedto write function @code{Integer f(const Integer& a) @{ Integer r = a; r += a; return r; @}}This function, when called (as in @code{ a = f(a); }) demonstrates a similar kind of wasted copy. The returned value r must be copiedout of the function before it can be used by the caller. In GNUC++, there is an alternative via the use of named return values.Named return values allow you to manipulate the returned objectdirectly, rather than requiring you to create a local insidea function and then copy it out as the returned value. In thisexample, this can be done via@code{Integer f(const Integer& a) return r(a) @{ r += a; return; @}}A final guideline: The overloaded operators are very convenient, andmuch clearer to use than procedural code. It is almost always a goodidea to make it right, @emph{then} make it fast, by translating expression code into procedural code after it is known to be correct.@node Pix, Headers, Expressions, Top@chapter Pseudo-indexesMany useful classes operate as containers of elements. Techniques foraccessing these elements from a container differ from class to class.In the GNU C++ library, access methods have been partially standardizedacross different classes via the use of pseudo-indexes called@code{Pixes}. A @code{Pix} acts in some ways like an index, and in someways like a pointer. (Their underlying representations are just@code{void*} pointers). A @code{Pix} is a kind of ``key'' that istranslated into an element access by the class. In virtually all cases,@code{Pixes} are pointers to some kind internal storage cells. Thecontainers use these pointers to extract items.@code{Pixes} support traversal and inspection of elements in acollection using analogs of array indexing. However, they arepointer-like in that @code{0} is treated as an invalid @code{Pix}, andunsafe insofar as programmers can attempt to access nonexistent elementsvia dangling or otherwise invalid @code{Pixes} without first checkingfor their validity. In general it is a very bad idea to perform traversals in the the midstof destructive modifications to containers.Typical applications might include code using the idiom@examplefor (Pix i = a.first(); i != 0; a.next(i)) use(a(i));@end examplefor some container @code{a} and function @code{use}.Classes supporting the use of @code{Pixes} always contain the following methods, assuming a container @code{a} of element types of @code{Base}.@table @code@item Pix i = a.first()Set i to index the first element of a or 0 if a is empty.@item a.next(i)advance i to the next element of a or 0 if there is no next element;@item Base x = a(i); a(i) = x;a(i) returns a reference to the element indexed by i.@item int present = a.owns(i)returns true if Pix i is a valid Pix in a. This is often arelatively slow operation, since the collection must usually traverse through elements to see if any correspond to the Pix.@end tableSome container classes also support backwards traversal via@table @code@item Pix i = a.last()Set i to the last element of a or 0 if a is empty.@item a.prev(i)sets i to the previous element in a, or 0 if there is none.@end tableCollections supporting elements with an equality operation possess@table @code@item Pix j = a.seek(x)sets j to the index of the first occurrence of x, or 0 if x is not contained in a.@end tableBag classes possess@table @code@item Pix j = a.seek(x, Pix from = 0)sets j to the index of the next occurrence of x following i,or 0 if x is not contained in a. If i == 0, the first occurrence is returned.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -