⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ec7.htm

📁 一个非常适合初学者入门的有关c++的文档
💻 HTM
📖 第 1 页 / 共 4 页
字号:
           class traits = char_traits<charT>,           class Allocator = allocator<charT> >     class basic_string;  typedef basic_string<char> string;}Notice how basic_string has default values for its traits and Allocator parameters. This is typical of the standard library. It offers flexibility to those who need it, but "typical" clients who just want to do the "normal" thing can ignore the complexity that makes possible the flexibility. In other words, if you just want string objects that act more or less like C strings, you can use string objects and remain merrily ignorant of the fact that you're really using objects of type basic_string<char, char_traits<char>, allocator<char> >.Well, usually you can. Sometimes you have to peek under the hood a bit. For example, Item 34 discusses the advantages of declaring a class without providing its definition, and it remarks that the following is the wrong way to declare the string type: class string;                   // this will compile, but                                // you don't want to do itSetting aside namespace considerations for a moment, the real problem here is that string isn't a class, it's a typedef. It would be nice if you could solve the problem this way: typedef basic_string<char> string;but that won't compile. "What is this basic_string of which you speak?," your compilers will wonder, though they'll probably phrase the question rather differently. No, to declare string, you would first have to declare all the templates on which it depends. If you could do it, it would look something like this: template<class charT> struct char_traits;template<class T> class allocator;  template<class charT,           class traits = char_traits<charT>,           class Allocator = allocator<charT> >     class basic_string;typedef basic_string<char> string;However, you can't declare string. At least you shouldn't. That's because library implementers are allowed to declare string (or anything else in the std namespace) differently from what's specified in the standard as long as the result offers standard-conforming behavior. For example, a basic_string implementation could add a fourth template parameter, but that parameter's default value would have to yield code that acts as the standard says an unadorned basic_string must.End result? Don't try to manually declare string (or any other part of the standard library). Instead, just include the appropriate header, e.g. <string>.With this background on headers and templates under our belts, we're in a position to survey the primary components of the standard C++ library: The standard C library. It's still there, and you can still use it. A few minor things have been tweaked here and there, but for all intents and purposes, it's the same C library that's been around for years.Iostreams. Compared to "traditional" iostream implementations, it's been templatized, its inheritance hierarchy has been modified, it's been augmented with the ability to throw exceptions, and it's been updated to support strings (via the stringstream classes) and internationalization (via locales see below). Still, most everything you've come to expect from the iostream library continues to exist. In particular, it still supports stream buffers, formatters, manipulators, and files, plus the objects cin, cout, cerr, and clog. That means you can treat strings and files as streams, and you have extensive control over stream behavior, including buffering and formatting.Strings. string objects were designed to eliminate the need to use char* pointers in most applications. They support the operations you'd expect (e.g., concatenation, constant-time access to individual characters via operator[], etc.), they're convertible to char*s for compatibility with legacy code, and they handle memory management automatically. Some string implementations employ reference counting (see Item M29), which can lead to better performance (in both time and space) than char*-based strings.Containers. Stop writing your own basic container classes! The library offers efficient implementations of vectors (they act like dynamically extensible arrays), lists (doubly-linked), queues, stacks, deques, maps, sets, and bitsets. Alas, there are no hash tables in the library (though many vendors offer them as extensions), but compensating somewhat is the fact that strings are containers. That's important, because it means anything you can do to a container (see below), you can also do to a string.What's that? You want to know how I know the library implementations are efficient? Easy: the library specifies each class's interface, and part of each interface specification is a set of performance guarantees. So, for example, no matter how vector is implemented, it's not enough to offer just access to its elements, it must offer constant-time access. If it doesn't, it's not a valid vector implementation.In many C++ programs, dynamically allocated strings and arrays account for most uses of new and delete, and new/delete errors especially leaks caused by failure to delete newed memory are distressingly common. If you use string and vector objects (both of which perform their own memory management) instead of char*s and pointers to dynamically allocated arrays, many of your news and deletes will vanish, and so will the difficulties that frequently accompany their use (e.g., Items 6 and 11). Algorithms. Having standard containers is nice, but it's even nicer when there's an easy way to do things with them. The standard library offers over two dozen easy ways (i.e., predefined functions, officially known as algorithms they're really function templates), most of which work with all the containers in the library as well as with built-in arrays!Algorithms treat the contents of a container as a sequence, and each algorithm may be applied to either the sequence corresponding to all the values in a container or to a subsequence. Among the standard algorithms are for_each (apply a function to each element of a sequence), find (find the first location in a sequence holding a given value Item M35 shows its implementation), count_if (count the number of elements in a sequence for which a given predicate is true), equal (determine whether two sequences hold equal-valued elements), search (find the first position in one sequence where a second sequence occurs as a subsequence), copy (copy one sequence into another), unique (remove duplicate values from a sequence), rotate (rotate the values in a sequence) and sort (sort the values in a sequence). Note that this is just a sampling of the algorithms available; the library contains many others.Just as container operations come with performance guarantees, so do algorithms. For example, the stable_sort algorithm is required to perform no more than O(N log N) comparisons. (If the "Big O" notation in the previous sentence is foreign to you, don't sweat it. What it really means is that, broadly speaking, stable_sort must offer performance at the same level as the most efficient general-purpose serial sorting algorithms.) Support for internationalization. Different cultures do things in different ways. Like the C library, the C++ library offers features to facilitate the production of internationalized software, but the C++ approach, though conceptually akin to that of C, is different. It should not surprise you, for example, to learn that C++'s support for internationalization makes extensive use of templates, and it takes advantage of inheritance and virtual functions, too.The primary library components supporting internationalization are facets and locales. Facets describe how particular characteristics of a culture should be handled, including collation rules (i.e., how strings in the local character set should be sorted), how dates and times should be expressed, how numeric and monetary values should be presented, how to map from message identifiers to (natural) language-specific messages, etc. Locales bundle together sets of facets. For example, a locale for the United States would include facets describing how to sort strings in American English, read and write dates and times, read and write monetary and numeric values, etc., in a way appropriate for people in the USA. A locale for France, on the other hand, would describe how to perform these tasks in a manner to which the French are accustomed. C++ allows multiple locales to be active within a single program, so different parts of an application may employ different conventions.Support for numeric processing. The end for FORTRAN may finally be near. The C++ library offers a template for complex number classes (the precision of the real and imaginary parts may be float, double, or long double) as well as for special array types specifically designed to facilitate numeric programming. Objects of type valarray, for example, are defined to hold elements that are free from aliasing. This allows compilers to be much more aggressive in their optimizations, especially for vector machines. The library also offers support for two different types of array slices, as well as providing algorithms to compute inner products, partial sums, adjacent differences, and more.Diagnostic support. The standard library offers support for three ways to report errors: via C's assertions (see Item 7), via error numbers, and via exceptions. To help provide some structure to exception types, the library defines the following hierarchy of exception classes:Exceptions of type logic_error (or its subclasses) represent errors in the logic of software. In theory, such errors could have been prevented by more careful programming. Exceptions of type runtime_error (or its derived classes) represent errors detectable only at runtime.You may use these classes as is, you may inherit from them to create your own exception classes, or you may ignore them. Their use is not mandatory.This list doesn't describe everything in the standard library. Remember, the specification runs over 300 pages. Still, it should give you the basic lay of the land.The part of the library pertaining to containers and algorithms is commonly known as Standard Template Library (the STL see Item M35). There is actually a third component to the STL Iterators that I haven't described. Iterators are pointer-like objects that allow STL algorithms and containers to work together. You need not understand iterators for the high-level description of the standard library I give here. If you're interested in them, however, you can find examples of their use in Items 39 and M35.The STL is the most revolutionary part of the standard library, not because of the containers and algorithms it offers (though they are undeniably useful), but because of its architecture. Simply put, the architecture is extensible: you can add to the STL. Of course, the components of the standard library itself are fixed, but if you follow the conventions on which the STL is built, you can write your own containers, algorithms, and iterators that work as well with the standard STL components as the STL components work with one another. You can also take advantage of STL-compliant containers, algorithms, and iterators written by others, just as they can take advantage of yours. What makes the STL revolutionary is that it's not really software, it's a set of conventions. The STL components in the standard library are simply manifestations of the good that can come from following those conventions.By using the components in the standard library, you can generally dispense with designing your own from-the-ground-up mechanisms for stream I/O, strings, containers (including iteration and common manipulations), internationalization, numeric data structures, and diagnostics. That leaves you a lot more time and energy for the really important part of software development: implementing the things that distinguish your wares from those of your competitors. Back to Item 49Continue to AfterwordItem 50: Improve your understanding of C++.There's a lot of stuff in C++. C stuff. Overloading stuff. Object-oriented stuff. Template stuff. Exception stuff. Namespace stuff. Stuff, stuff, stuff! Sometimes it can be overwhelming. How do you make sense of all that stuff?It's not that hard once you understand the design goals that forged C++ into what it is. Foremost amongst those goals are the following: Compatibility with C. Lots and lots of C exists, as do lots and lots of C programmers. C++ takes advantage of and builds on er, I mean it "leverages" that base. Efficiency. Bjarne Stroustrup, the designer and first implementer of C++, knew from the outset that the C programmers he hoped to win over wouldn't look twice if they had to pay a performance penalty for switching languages. As a result, he made sure C++ was competitive with C when it came to efficiency like within 5%. Compatibility with traditional tools and environments. Fancy development environments run here and there, but compilers, linkers, and editors run almost everywhere. C++ is designed to work in environments from mice to mainframes, so it brings along as little baggage as possible. You want to port C++? You port a language and take advantage of existing tools on the target platform. (However, it is often possible to provide a better implementation if, for example, the linker can be modified to address some of the more demanding aspects of inlining and templates.) Applicability to real problems. C++ wasn't designed to be a nice, pure language, good for teaching students how to program, it was designed to be a powerful tool for professional programmers solving real problems in diverse domains. The real world has some rough edges, so it's no surprise there's the occasional scratch marring the finish of the tools on which the pros rely.These goals explain a multitude of language details that might otherwise merely chafe. Why do implicitly-generated copy constructors and assignment operators behave the way they do, especially for pointers (see Items 11 and 45)? Because that's how C copies and assigns structs, and compatibility with C is important. Why aren't destructors automatically virtual (see Item 14), and why must implementation details appear in class definitions (see Item 34)? Because doing otherwise would impose a performance penalty, and efficiency is important. Why can't C++ detect initialization dependencies between non-local static objects (see Item 47)? Because C++ supports separate translation (i.e., the ability to compile source modules separately, then link several object files together to form an executable), relies on existing linkers, and doesn't mandate the existence of program databases. As a result, C++ compilers almost never know everything about an entire program. Finally, why doesn't C++ free programmers from tiresome duties like memory management (see Items 5-10) and low-level pointer manipulations? Because some programmers need those capabilities, and the needs of real programmers are of paramount importance.This barely hints at how the design goals behind C++ shape the behavior of the language. To cover everything would take an entire book, so it's convenient that Stroustrup wrote one. That book is The Design and Evolution of C++ (Addison-Wesley, 1994), sometimes known as simply "D&E." Read it, and you'll see what features were added to C++, in what order, and why. You'll also learn about features that were rejected, and why. You'll even get the inside story on how the dynamic_cast feature (see Items 39 and M2) was considered, rejected, reconsidered, then accepted and why. If you're having trouble making sense of C++, D&E should dispel much of your confusion.The Design and Evolution of C++ offers a wealth of insights into how C++ came to be what it is, but it's nothing like a formal specification for the language. For that you must turn to the international standard for C++, an impressive exercise in formalese running some 700 pages. There you can read such riveting prose as this:A virtual function call uses the default arguments in the declaration of the virtual function determined by the static type of the pointer or reference denoting the object. An overriding function in a derived class does not acquire default arguments from the function it overrides.This paragraph is the basis for Item 38 ("Never redefine an inherited default parameter value"), but I hope my treatment of the topic is somewhat more accessible than the text above.The standard is hardly bedtime reading, but it's your best recourse your standard recourse if you and someone else (a compiler vendor, say, or a developer of some other tool that processes source code) disagree on what is and isn't C++. The whole purpose of a standard is to provide definitive information that settles arguments like that.The standard's official title is a mouthful, but if you need to know it, you need to know it. Here it is: International Standard for Information SystemsProgramming Language C++. It's published by Working Group 21 of the International Organization for Standardization (ISO). (If you insist on being picky about it, it's really published by I am not making this up ISO/IEC JTC1/SC22/WG21.) You can order a copy of the official standard from your national standards body (in the United States, that's ANSI, the American National Standards Institute), but copies of late drafts of the standard which are quite similar (though not identical) to the final document are freely available on the World Wide Web. A good place to look for a copy is at the Cygnus Solutions Draft Standard C++ Page, but given the pace of change in cyberspace, don't be surprised if this link is broken by the time you try it. If it is, your favorite Web search engine will doubtless turn up a URL that works.As I said, The Design and Evolution of C++ is fine for insights into the language's design, and the standard is great for nailing down language details, but it would be nice if there were a comfortable middle ground between D&E's view from 10,000 meters and the standard's micron-level examination. Textbooks are supposed to fill this niche, but they generally drift toward the standard's perspective, whereby what the language is receives a lot more attention than why it's that way.Enter the ARM. The ARM is another book, The Annotated C++ Reference Manual, by Margaret Ellis and Bjarne Stroustrup (Addison-Wesley, 1990). Upon its publication, it became the authority on C++, and the international standard started with the ARM (along with the existing C standard) as its basis. In the intervening years, the language specified by the standard has in some ways parted company with that described by the ARM, so the ARM is no longer the authority it once was. It's still a useful reference, however, because most of what it says is still true, and it's not uncommon for vendors to adhere to the ARM specification in areas of C++ where the standard has only recently settled down.What makes the ARM really useful, however, isn't the RM part (the Reference Manual), it's the A part: the annotations. The ARM provides extensive commentary on why many features of C++ behave the way they do. Some of this information is in D&E, but much of it isn't, and you do want to know it. For instance, here's something that drives most people crazy when they first encounter it: class Base {public:  virtual void f(int x);};class Derived: public Base {public:  virtual void f(double *pd);};Derived *pd = new Derived;pd->f(10);                            // error!The problem is that Derived::f hides Base::f, even though they take different parameter types, so compilers demand that the call to f take a double*, which the literal 10 most certainly is not.This is inconvenient, but the ARM provides an explanation for this behavior. Suppose that when you called f, you really did want to call the version in Derived, but you accidentally used the wrong parameter type. Further suppose that Derived is way down in an inheritance hierarchy and that you were unaware that Derived indirectly inherits from some base class BaseClass, and that BaseClass declares a virtual function f that takes an int. In that case, you would have inadvertently called BaseClass::f, a function you didn't even know existed! This kind of error could occur frequently where large class hierarchies are used, so Stroustrup decided to nip it in the bud by having derived class members hide base class members on a per-name basis.Note, by the way, that if the writer of Derived wants to allow clients to access Base::f, this is easily accomplished via a simple using declaration: class Derived: public Base {public:  using Base::f;                   // import Base::f into                                   // Derived's scope  virtual void f(double *pd);};Derived *pd = new Derived;pd->f(10);                         // fine, calls Base::fFor compilers not yet supporting using declarations, an alternative is to employ an inline function: class Derived: public Base {public:  virtual void f(int x) { Base::f(x); }  virtual void f(double *pd);};Derived *pd = new Derived;pd->f(10);                 // fine, calls Derived::f(int),                           // which calls Base::f(int)Between D&E and the ARM, you'll gain insights into the design and implementation of C++ that make it possible to appreciate the sound, no-nonsense architecture behind a sometimes baroque-looking facade. Fortify those insights with the detailed information in the standard, and you've got a foundation for software development that leads to truly effective C++. Back to Item 50: Improve your understanding of C++.Continue to Afterword 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -