B. Stroustrup - The C++ Programming Language (794319), страница 18
Текст из файла (страница 18)
Similarly,we did not provide a mechanism to ‘‘give back’’ the array of doubles acquired by new; §3.2.1.2shows how to use a destructor to elegantly do that.2.3.3 EnumerationsIn addition to classes, C++ supports a simple form of user-defined type for which we can enumerate the values:enum class Color { red, blue, green };enum class Traffic_light { green, yellow, red };Color col = Color::red;Traffic_light light = Traffic_light::red;Note that enumerators (e.g., red) are in the scope of their enum class, so that they can be usedrepeatedly in different enum classes without confusion. For example, Color::red is Color’s redwhich is different from Traffic_light::red.Enumerations are used to represent small sets of integer values.
They are used to make codemore readable and less error-prone than it would have been had the symbolic (and mnemonic) enumerator names not been used.The class after the enum specifies that an enumeration is strongly typed and that its enumeratorsare scoped. Being separate types, enum classes help prevent accidental misuses of constants.
Inparticular, we cannot mix Traffic_light and Color values:Color x = red;Color y = Traffic_light::red;Color z = Color::red;// error : which red?// error : that red is not a Color// OKSimilarly, we cannot implicitly mix Color and integer values:int i = Color::red;Color c = 2;// error : Color ::red is not an int// error : 2 is not a ColorIf you don’t want to explicitly qualify enumerator names and want enumerator values to be ints(without the need for an explicit conversion), you can remove the class from enum class to get a‘‘plain enum’’ (§8.4.2).By default, an enum class has only assignment, initialization, and comparisons (e.g., == and <;§2.2.2) defined.
However, an enumeration is a user-defined type so we can define operators for it:Section 2.3.3Enumerations51Traffic_light& operator++(Traffic_light& t)// prefix increment: ++{switch (t) {case Traffic_light::green:return t=Traffic_light::yellow;case Traffic_light::yellow:return t=Traffic_light::red;case Traffic_light::red:return t=Traffic_light::green;}}Traffic_light next = ++light;// next becomes Traffic_light::greenC++ also offers a less strongly typed ‘‘plain’’ enum (§8.4.2).2.4 ModularityA C++ program consists of many separately developed parts, such as functions (§2.2.1, Chapter12), user-defined types (§2.3, §3.2, Chapter 16), class hierarchies (§3.2.4, Chapter 20), and templates (§3.4, Chapter 23). The key to managing this is to clearly define the interactions amongthose parts.
The first and most important step is to distinguish between the interface to a part andits implementation. At the language level, C++ represents interfaces by declarations. A declaration specifies all that’s needed to use a function or a type. For example:double sqrt(double);// the square root function takes a double and returns a doubleclass Vector {public:Vector(int s);double& operator[](int i);int size();private:double∗ elem; // elem points to an array of sz doublesint sz;};The key point here is that the function bodies, the function definitions, are ‘‘elsewhere.’’ For thisexample, we might like for the representation of Vector to be ‘‘elsewhere’’ also, but we will dealwith that later (abstract types; §3.2.2). The definition of sqrt() will look like this:double sqrt(double d)// definition of sqrt(){// ...
algorithm as found in math textbook ...}For Vector, we need to define all three member functions:Vector::Vector(int s):elem{new double[s]}, sz{s}{}// definition of the constructor// initialize members52A Tour of C++: The BasicsChapter 2double& Vector::operator[](int i){return elem[i];}// definition of subscriptingint Vector::size(){return sz;}// definition of size()We must define Vector’s functions, but not sqrt() because it is part of the standard library. However,that makes no real difference: a library is simply some ‘‘other code we happen to use’’ written withthe same language facilities as we use.2.4.1 Separate CompilationC++ supports a notion of separate compilation where user code sees only declarations of types andfunctions used.
The definitions of those types and functions are in separate source files and compiled separately. This can be used to organize a program into a set of semi-independent code fragments. Such separation can be used to minimize compilation times and to strictly enforce separation of logically distinct parts of a program (thus minimizing the chance of errors). A library isoften a separately compiled code fragments (e.g., functions).Typically, we place the declarations that specify the interface to a module in a file with a nameindicating its intended use.
For example:// Vector.h:class Vector {public:Vector(int s);double& operator[](int i);int size();private:double∗ elem;// elem points to an array of sz doublesint sz;};This declaration would be placed in a filefile, to access that interface. For example:Vector.h,and users will include that file, called a header// user.cpp:#include "Vector.h"#include <cmath>using namespace std;// get Vector’s interface// get the the standard-librar y math function interface including sqrt()// make std members visible (§2.4.2)Section 2.4.1Separate Compilationdouble sqrt_sum(Vector& v){double sum = 0;for (int i=0; i!=v.size(); ++i)sum+=sqrt(v[i]);return sum;}53// sum of square rootsTo help the compiler ensure consistency, thealso include the .h file providing its interface:.cppfile providing the implementation ofVectorwill// Vector.cpp:#include "Vector.h" // get the interfaceVector::Vector(int s):elem{new double[s]}, sz{s}{}double& Vector::operator[](int i){return elem[i];}int Vector::size(){return sz;}The code in user.cpp and Vector.cpp shares the Vector interface information presented in Vector.h,but the two files are otherwise independent and can be separately compiled.
Graphically, the program fragments can be represented like this:Vector.h:Vectoruser.cpp:#include "Vector.h"use VectorinterfaceVector.cpp:#include "Vector.h"define VectorStrictly speaking, using separate compilation isn’t a language issue; it is an issue of how best totake advantage of a particular language implementation. However, it is of great practical importance. The best approach is to maximize modularity, represent that modularity logically throughlanguage features, and then exploit the modularity physically through files for effective separatecompilation (Chapter 14, Chapter 15).54A Tour of C++: The BasicsChapter 22.4.2 NamespacesIn addition to functions (§2.2.1, Chapter 12), classes (Chapter 16), and enumerations (§2.3.3, §8.4),C++ offers namespaces (Chapter 14) as a mechanism for expressing that some declarations belongtogether and that their names shouldn’t clash with other names.
For example, I might want toexperiment with my own complex number type (§3.2.1.1, §18.3, §40.4):namespace My_code {class complex { /* ... */ };complex sqrt(complex);// ...int main();}int My_code::main(){complex z {1,2};auto z2 = sqrt(z);std::cout << '{' << z2.real() << ',' << z2.imag() << "}\n";// ...};int main(){return My_code::main();}By putting my code into the namespace My_code, I make sure that my names do not conflict withthe standard-library names in namespace std (§4.1.2). The precaution is wise, because the standardlibrary does provide support for complex arithmetic (§3.2.1.1, §40.4).The simplest way to access a name in another namespace is to qualify it with the namespacename (e.g., std::cout and My_code::main).
The ‘‘real main()’’ is defined in the global namespace,that is, not local to a defined namespace, class, or function. To gain access to names in the standard-library namespace, we can use a using-directive (§14.2.3):using namespace std;Namespaces are primarily used to organize larger program components, such as libraries. Theysimplify the composition of a program out of separately developed parts.2.4.3 Error HandlingError handling is a large and complex topic with concerns and ramifications that go far beyond language facilities into programming techniques and tools.
However, C++ provides a few features tohelp. The major tool is the type system itself. Instead of painstakingly building up our applicationsfrom the built-in types (e.g., char, int, and double) and statements (e.g., if, while, and for), we buildmore types that are appropriate for our applications (e.g., string, map, and regex) and algorithms(e.g., sort(), find_if(), and draw_all()). Such higher level constructs simplify our programming, limitour opportunities for mistakes (e.g., you are unlikely to try to apply a tree traversal to a dialog box),Section 2.4.3Error Handling55and increase the compiler’s chances of catching such errors.