📄 libg++.texinfo
字号:
@itemWhen a function could be usefully defined either as amember or a friend, it is generally a member if it modifiesand/or returns itself, else it is a friend. There are caseswhere naturalness of expression wins out over this rule.@itemClass declaration files are formatted so that it is easyto quickly check them to determine function names, parameters,and so on. Because of the different kinds of things that mayappear in class declarations, there is no perfect way to dothis. Any suggestions on developing a common classdeclaration formatting style are welcome.@itemAll classes use the same simple error (exception) handling strategy.Almost every class has a member function named @code{error(char* msg)}that invokes an associated error handler function via a pointer to thatfunction, so that the error handling function may be reset byprogrammers. By default nearly all call @code{*lib_error_handler}, whichprints the message and then aborts execution. This system is subjectto change. In general, errors are assumed to be non-recoverable:Library classes do not include code that allows graceful continuationafter exceptions.@end itemize@node OK, Proto, Conventions, Top@chapter Support for representation invariantsMost GNU C++ library classes possess a method named @code{OK()},that is useful in helping to verify correct performance of classoperations.The @code{OK()} operations checks the ``representation invariant'' of aclass object. This is a test to check whether the object is in a validstate. In effect, it is a (sometimes partial) verification of thelibrary's promise that (1) class operations always leave objects invalid states, and (2) the class protects itself so that client functionscannot corrupt this state.While no simple validation technique can assure that all operationsperform correctly, calls to @code{OK()} can at least verify thatoperations do not corrupt representations. For example for @code{Stringa, b, c; ... a = b + c;}, a call to @code{a.OK();} will guarantee that@code{a} is a valid @code{String}, but does not guarantee that itcontains the concatenation of @code{b + c}. However, given that @code{a}is known to be valid, it is possible to further verify its properties,for example via @code{a.after(b) == c && a.before(c) == b}. In otherwords, @code{OK()} generally checks only those internal representationproperties that are otherwise inaccessible to users of the class. Otherclass operations are often useful for further validation.Failed calls to @code{OK()} call a class's @code{error} method ifone exists, else directly call @code{abort}. Failure indicatesan implementation error that should be reported.With only rare exceptions, the internal support functions for a classnever themselves call @code{OK()} (although many of the test filesin the distribution call @code{OK()} extensively).Verification of representational invariants can sometimes bevery time consuming for complicated data structures. @node Proto, Representations, OK, Top@chapter Introduction to container class prototypesAs a temporary mechanism enabling the support of generic classes, the GNUC++ Library distribution contains a directory (@file{g++-include}) of filesdesigned to serve as the basis for generating container classes ofspecified elements. These files can be used to generate @file{.h} and@file{.cc} files in the current directory via a supplied shell scriptprogram that performs simple textual substitution to create specificclasses.While these classes are generated independently, and thus share no code,it is possible to create versions that do share code among subclasses. Forexample, using @code{typedef void* ent}, and then generating a@code{entList} class, other derived classes could be created using the@code{void*} coercion method described in Stroustrup, pp204-210.This very simple class-generation facility is useful enough to servecurrent purposes, but will be replaced with a more coherent mechanism forhandling C++ generics in a way that minimally disrupts current usage.Without knowing exactly when or how parametric classes might beadded to the C++ language, provision of this simplest possiblemechanism, textual substitution, appears to be the safest strategy,although it does require certain redundancies and awkward constructions.Specific classes may be generated via the @file{genclass} shell scriptprogram. This program has arguments specifying the kinds of base types(s)to be used. Specifying base types requires two arguments. The first is thename of the base type, which may be any named type, like @code{int} or@code{String}. Only named types are supported; things like @code{int*} arenot accepted. However, pointers like this may be used by supplying theappropriate typedefs (e.g., editing the resulting files to include@code{typedef int* intp;}). The type name must be followed by one of thewords @code{val} or @code{ref}, to indicate whether the base elementsshould be passed to functions by-value or by-reference. You can specify basic container classes using @code{genclass base[val,ref] proto}, where @code{proto} is the name of the class beinggenerated. Container classes like dictionaries and maps that requiretwo types may be specified via @code{genclass -2 keytype [val, ref],basetype [val, ref] proto}, where the key type is specified first andthe contents type second. The resulting classnames and filenames aregenerated by prepending the specified type names to the prototype names,and separating the filename parts with dots. For example,@code{genclass int val List} generates class @code{intList} residing infiles @file{int.List.h} and @file{int.List.cc}. @code{genclass -2 Stringref int val VHMap} generates (the awkward, but unavoidable) class name@code{StringintVHMap}. Of course, programmers may use @code{typedef} orsimple editing to create more appropriate names. The existence of dotseperators in file names allows the use of GNU make to help automateconfiguration and recompilation. An example Makefile exploiting suchcapabilities may be found in the @file{libg++/proto-kit} directory.The @code{genclass} utility operates via simple text substitution using@code{sed}. All occurrences of the pseudo-types @code{<T>} and @code{<C>} (if there are two types) are replaced with the indicated type, andoccurrences of @code{<T&>} and @code{<C&>} are replaced by just the types,if @code{val} is specified, or types followed by ``&'' if @code{ref} isspecified.Programmers will frequently need to edit the @file{.h} file in order toinsert additional @code{#include} directives or other modifications. Asimple utility, @file{prepend-header} to prepend other @file{.h} filesto generated files is provided in the distribution. One dubious virtue of the prototyping mechanism is that, because sources files,not archived library classes, are generated, it is relatively simple forprogrammers to modify container classes in the common case where slightvariations of standard container classes are required.It is often a good idea for programmers to archive (via @code{ar})generated classes into @file{.a} files so that only those classfunctions actually used in a given application will be loaded.The test subdirectory of the distribution shows an example of this.Because of @code{#pragma interface} directives, the @file{.cc} filesshould be compiled with @code{-O} or @code{-DUSE_LIBGXX_INLINES}enabled.Many container classes require specifications over and above the baseclass type. For example, classes that maintain some kind of ordering ofelements require specification of a comparison function upon which tobase the ordering. This is accomplished via a prototype file@file{defs.hP} that contains macros for these functions. While thesemacros default to perform reasonable actions, they can and should bechanged in particular cases. Most prototypes require only one or a fewof these. No harm is done if unused macros are defined to performnonsensical actions. The macros are:@table @code@item DEFAULT_INITIAL_CAPACITYThe initial capacity for containers (e.g., hash tables) that require an initial capacity argument for constructors. Default: 100@item <T>EQ(a, b)return true if a is considered equal to b for the purposes oflocating, etc., an element in a container. Default: (a == b)@item <T>LE(a, b)return true if a is less than or equal to bDefault: (a <= b)@item <T>CMP(a, b)return an integer < 0 if a<b, 0 if a==b, or > 0 if a>b.Default: (a <= b)? (a==b)? 0 : -1 : 1@item <T>HASH(a)return an unsigned integer representing the hash of a.Default: hash(a) ; where extern unsigned int hash(<T&>).(note: several useful hash functions are declared in builtin.hand defined in hash.cc)@end tableNearly all prototypes container classes support containertraversal via @code{Pix} pseudo indices, as described elsewhere.All object containers must perform either a @code{X::X(X&)} (or@code{X::X()} followed by @code{X::operator =(X&)}) to copy objects intocontainers. (The latter form is used for containers built from C++arrays, like @code{VHSets}). When containers are destroyed, they invoke@code{X::~X()}. Any objects used in containers must have well behavedconstructors and destructors. If you want to create containers thatmerely reference (point to) objects that reside elsewhere, and are notcopied or destroyed inside the container, you must use containersof pointers, not containers of objects.All prototypes are designed to generate @emph{HOMOGENOUS} containerclasses. There is no universally applicable method in C++ to supportheterogenous object collections with elements of various subclasses ofsome specified base class. The only way to get heterogenous structuresis to use collections of pointers-to-objects, not collections of objects(which also requires you to take responsibility for managing storage forthe objects pointed to yourself).For example, the following usage illustrates a commonly encountereddanger in trying to use container classes for heterogenous structures:@smallexampleclass Base @{ int x; ...@}class Derived : public Base @{ int y; ... @}BaseVHSet s; // class BaseVHSet generated via something like // `genclass Base ref VHSet'void f()@{ Base b; s.add(b); // OK Derived d; s.add(d); // (CHOP!)@}@end smallexampleAt the line flagged with @samp{(CHOP!)}, a @code{Base::Base(Base&)} iscalled inside @code{Set::add(Base&)}---@emph{not}@code{Derived::Derived(Derived&)}. Actually, in @code{VHSet}, a@code{Base::operator =(Base&)}, is used instead to place the element inan array slot, but with the same effect. So only the Base part iscopied as a @code{VHSet} element (a so-called chopped-copy). In thiscase, it has an @code{x} part, but no @code{y} part; and a Base, notDerived, vtable. Objects formed via chopped copies are rarelysensible.@refillTo avoid this, you must resort to pointers:@smallexampletypedef Base* BasePtr;BasePtrVHSet s; // class BaseVHSet generated via something like // `genclass BasePtr val VHSet'void f()@{ Base* bp = new Base; s.add(b); Base* dp = new Derived; s.add(d); // works fine. // Don't forget to delete bp and dp sometime. // The VHSet won't do this for you.@}@end smallexample@section ExampleThe prototypes can be difficult to use on first attempt. Here is anexample that may be helpful. The utilities in the @file{proto-kit}simplify much of the actions described, but are not used here.Suppose you create a class @code{Person}, and want to make an Map thatlinks the social security numbers associated with each person. You startoff with a file @file{Person.h}@example#include <String.h>class Person@{ String nm; String addr; //...public: const String& name() @{ return nm; @} const String& address() @{ return addr; @} void print() @{ ... @} //...@}@end exampleAnd in file @file{SSN.h},@exampletypedef unsigned int SSN;@end exampleYour first decision is what storage/usage strategy to use. There areseveral reasonable alternatives here: You might create an ``objectcollection'' of Persons, a ``pointer collection'' ofpointers-to-Persons, or even a simple String map, housing either copiesof pointers to the names of Persons, since other fields are unused forpurposes of the Map. In an object collection, instances of class Person``live'' inside the Map, while in a pointer collection, the instanceslive elsewhere. Also, as above, if instances of subclasses of Person areto be used inside the Map, you must use pointers. In a String Map, thesame difference holds, but now only for the name fields. Any of thesechoices might make sense in particular applications. The second choice is the Map implementation strategy. Either a treeor a hash table might make sense. Suppose you want an AVL tree Map.There are two things to now check. First, as an object collection,the AVLMap requires that the elsement class contain an @code{X(X&)}constructor. In C++, if you don't specify such a constructor, oneis constructed for you, but it is a very good idea to always do thisyourself, to avoid surprises. In this example, you'd use something like@exampleclass Person @{ ...; Person(const Person& p) :nm(p.nm), addr(p.addr) @{@}@};@end exampleAlso, an AVLMap requires a comparison function for elements in orderto maintain order. Rather than requiring you to write a particularcomparison function, a @file{defs} file is consulted to determine how tocompare items. You must create and edit such a file.Before creating @file{Person.defs.h}, you must first make one additionaldecision. Should the Map member functions like @code{m.contains(p)}take arguments (@code{p}) by reference (i.e., typed as @code{int Map::contains(const Person& p)} or by value (i.e., typed as@code{int Map::contains(const Person p)}. Generally, for user-definedclasses, you want to pass by reference, and for builtins and pointers,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -