Scope operator. the scope operator is used to access names in a namespace. For example, std::cout says to use the name cout from the namespace std.
作用域操作符。:: 操作符用于在命名空间中访问名字。例如,std::cout 表示使用命名空间 std 中的名字 cout。
In C++, as in most programming languages, the type of an object determines what operations can be performed on it. Depending on the type of the objects involved, a statement might or might not be legal. Some languages, notably Smalltalk and Python, check the types involved in expressions at run time. In contrast, C++ is a statically typed language; type-checking is done at compile time. As a consequence, the compiler must be told the type of every name used in the program before that name can be used.
和大多数程序设计语言一样,C++ 中对象的类型决定了该对象可以执行的操作。语句正确与否取决于该语句中对象的类型。一些程序设计语言,特别是 Smalltalk 和 Python,在运行时才检查语句中对象的类型。相反,C++ 是静态类型(statically typed)语言,在编译时执行类型检查。结果是程序中使用某个名字之前,必须先告知编译器该名字的类型。
C++ provides a set of built-in data types, operators to manipulate those types, and a small set of statements for program flow control. These elements form an alphabet with which many large, complex real-world systems can and have been written. At this basic level, C++ is a simple language. Its expressive power arises from its support for mechanisms that allow the programmer to define new data structures.
C++ 提供了一组内置数据类型、操纵这些类型的操作符和一组少量的程序流控制语句。这些元素形成了一个“词汇表”,使用这个词汇表可以而且已经编写出许多大型、复杂的实际系统。从这个基本层面来看,C++ 是一门简单的语言。C++ 的表达能力是通过支持一些允许程序员定义新数据结构的机制来提升的。
The standard guarantees a minimum size for each of the arithmetic types, but it does not prevent compilers from using larger sizes.
C++标准规定了每个算术类型的最小存储空间,但它并不阻止编译器使用更大的存储空间。
The types short, int, and long represent integer values of potentially different sizes. Typically, shorts are represented in half a machine word, ints in a machine word, and longs in either one or two machine words (on 32-bit machines, ints and longs are usually the same size).
short、int 和 long 类型都表示整型值,存储空间的大小不同。一般, short 类型为半个机器字长,int 类型为一个机器字长,而 long 类型为一个或两个机器字长(在 32 位机器中 int 类型和 long 类型通常字长是相同的)。
Machine-Level Representation of The Built-in Types
内置类型的机器级表示
The C++ built-in types are closely tied to their representation in the computer's memory. Computers store data as a sequence of bits, each of which holds either 0 or 1. A segment of memory might hold
C++ 的内置类型与其在计算机的存储器中的表示方式紧密相关。计算机以位序列存储数据,每一位存储 0 或 1。一段内存可能存储着
00011011011100010110010000111011 ...
At the bit level, memory has no structure and no meaning.
在位这一级上,存储器是没有结构和意义的。
The most primitive way we impose structure on memory is by processing it in chunks. Most computers deal with memory as chunks of bits of particular sizes, usually powers of 2. They usually make it easy to process 8, 16, or 32 bits at a time, and chunks of 64 and 128 bits are becoming more common. Although the exact sizes can vary from one machine to another, we usually refer to a chunk of 8 bits as a "byte" and 32 bits, or 4 bytes, as a "word."
让存储具有结构的最基本方法是用块(chunk)处理存储。大部分计算机都使用特定位数的块来处理存储,块的位数一般是 2 的幂,因为这样可以一次处理 8、16 或 32 位。64 和 128 位的块如今也变得更为普遍。虽然确切的大小因机器不同而不同,但是通常将 8 位的块作为一个字节,32 位或 4 个字节作为一个“字(word)”。
Most computers associate a numbercalled an addresswith each byte in memory.
大多数计算机将存储器中的每一个字节和一个称为地址的数关联起来。
The integers, int, short, and long, are all signed by default. To get an unsigned type, the type must be specified as unsigned, such as unsigned long. The unsigned int type may be abbreviated as unsigned. That is, unsigned with no other type implies unsigned int.
整型 int、short 和 long 都默认为带符号型。要获得无符号型则必须指定该类型为 unsigned,比如 unsigned long。unsigned int 类型可以简写为 unsigned,也就是说,unsigned 后不加其他类型说明符意味着是 unsigned int 。
Unlike the other integral types, there are three distinct types for char: plain char, signed char, and unsigned char. Although there are three distinct types, there are only two ways a char can be represented. The char type is respresented using either the signed char or unsigned char version. Which representation is used for char varies by compiler.
和其他整型不同,char 有三种不同的类型:plain char 、unsigned char 和 signed char。虽然 char 有三种不同的类型,但只有两种表示方式。可以使用 unsigned char 或 signed char 表示 char 类型。使用哪种 char 表示方式由编译器而定。
Some characters are nonprintable. A nonprintable character is a character for which there is no visible image, such as backspace or a control character. Other characters have special meaning in the language, such as the single and double quotation marks, and the backslash. Nonprintable characters and special characters are written using an escape sequence. An escape sequence begins with a backslash.
有些字符是不可打印的。不可打印字符实际上是不可显示的字符,比如退格或者控制符。还有一些在语言中有特殊意义的字符,例如单引号、双引号和反斜线符号。不可打印字符和特殊字符都用转义字符书写。转义字符都以反斜线符号开始.
Strong Static Typing
强静态类型
C++ is a statically typed language, which means that types are checked at compile time. The process by which types are checked is referred to as type-checking.
C++ 是一门静态类型语言,在编译时会作类型检查。
In most languages, the type of an object constrains the operations that the object can perform. If the type does not support a given operation, then an object of that type cannot perform that operation.
在大多数语言中,对象的类型限制了对象可以执行的操作。如果某种类型不支持某种操作,那么这种类型的对象也就不能执行该操作。
In C++, whether an operation is legal or not is checked at compile time. When we write an expression, the compiler checks that the objects used in the expression are used in ways that are defined by the type of the objects. If not, the compiler generates an error message; an executable file is not produced.
在 C++ 中,操作是否合法是在编译时检查的。当编写表达式时,编译器检查表达式中的对象是否按该对象的类型定义的使用方式使用。如果不是的话,那么编译器会提示错误,而不产生可执行文件。
As our programs, and the types we use, get more complicated, we'll see that static type checking helps find bugs in our programs earlier. A consequence of static checking is that the type of every entity used in our programs must be known to the compiler. Hence, we must define the type of a variable before we can use that variable in our programs.
随着程序和使用的类型变得越来越复杂,我们将看到静态类型检查能帮助我们更早地发现错误。静态类型检查使得编译器必须能识别程序中的每个实体的类型。因此,程序中使用变量前必须先定义变量的类型。
Initialization happens when a variable is created and gives that variable its initial value. Assignment involves obliterating an object's current value and replacing that value with a new one.
初始化指创建变量并给它赋初始值,而赋值则是擦除对象的当前值并用新值代替。
A declaration makes known the type and name of the variable to the program. A definition is also a declaration: When we define a variable, we declare its name and type. We can declare a name without defining it by using the extern keyword. A declaration that is not also a definition consists of the object's name and its type preceded by the keyword extern:
声明用于向程序表明变量的类型和名字。定义也是声明:当定义变量时我们声明了它的类型和名字。可以通过使用extern关键字声明变量名而不定义它。不定义变量的声明包括对象名、对象类型和对象类型前的关键字extern:
extern int i; // declares but does not define i
extern int i; // declares but does not define i
extern int i; // declares but does not define i
extern int i; // declares but does not define i
int i; // declares and defines i
int i; // declares and defines i
int i; // declares and defines i
int i; // declares and defines i
Most scopes in C++ are delimited by curly braces. Generally, names are visible from their point of declaration until the end the scope in which the declaration appears.
C++ 语言中,大多数作用域是用花括号来界定的。一般来说,名字从其声明点开始直到其声明所在的作用域结束处都是可见的。
Headers Are for Declarations, Not Definitions
头文件用于声明而不是用于定义
When designing a header it is essential to remember the difference between definitions, which may only occur once, and declarations, which may occur multiple times.
当设计头文件时,记住定义和声明的区别是很重要的。定义只可以出现一次,而声明则可以出现多次。
There are three exceptions to the rule that headers should not contain definitions: classes, const objects whose value is known at compile time, and inline functions are all defined in headers. These entities may be defined in more than one source file as long as the definitions in each file are exactly the same.
对于头文件不应该含有定义这一规则,有三个例外。头文件可以定义类、值在编译时就已知道的 const 对象和 inline 函数。这些实体可在多个源文件中定义,只要每个源文件中的定义是相同的。
These entities are defined in headers because the compiler needs their definitions (not just declarations) to generate code.
在头文件中定义这些实体,是因为编译器需要它们的定义(不只是声明)来产生代码。
A compound type is a type that is defined in terms of another type.
复合类型是用其他类型定义的类型。
declaration(声明), Asserts the existence of a variable, function, or type defined elsewhere in the program. Some declarations are also definitions; only definitions allocate storage for variables. A variable may be declared by preceeding its type with the keyword extern. Names may not be used until they are defined or declared.
表明在程序中其他地方定义的变量、函数或类型的存在性。有些声明也是定义。只有定义才为变量分配存储空间。可以通过在类型前添加关键字 extern 来声明变量。名字直到定义或声明后才能使用。
central property of vectors (and the other library containers) is that they are required to be implemented so that it is efficient to add elements to them at run time. Because vectors grow efficiently, it is usually best to let the vector grow by adding elements to it dynamically as the element values are known.
vector 对象(以及其他标准库容器对象)的重要属性就在于可以在运行时高效地添加元素。因为 vector 增长的效率高,在元素值已知的情况下,最好是动态地添加元素。
……
使用数组初始化 vector 对象,必须指出用于初始化式的第一个元素以及数组最后一个元素的下一位置的地址:
const size_t arr_size = 6;
const size_t arr_size = 6;
int int_arr[arr_size] = {0, 1, 2, 3, 4, 5};
int int_arr[arr_size] = {0, 1, 2, 3, 4, 5};
// ivec has 6 elements: each a copy of the corresponding element in int_arr
// ivec has 6 elements: each a copy of the corresponding element in int_arr
vector ivec(int_arr, int_arr + arr_size);
vector ivec(int_arr, int_arr + arr_size);
size_t
size_t
Machine-dependent unsigned integral type defined in cstddef header that is large enough to hold the size of the largest possible array.
在 cstddef 头文件中定义的与机器相关的无符号整型,它具有足够的大小存储一个可能的最大数组。
The logical AND and OR operators always evaluate their left operand before the right. The right operand is evaluated only if the left operand does not determine the result. This evaluation strategy is often referred to as "short-circuit evaluation."
逻辑与和逻辑或操作符总是先计算其左操作数,然后再计算其右操作数。只有在仅靠左操作数的值无法确定该逻辑表达式的结果时,才会求解其右操作数。我们常常称这种求值策略为“短路求值(short-circuit evaluation)”。
A dynamic_cast supports the run-time identification of objects addressed either by a pointer or reference.
dynamic_cast 支持运行时识别指针或引用所指向的对象。
A const_cast, as its name implies, casts away the constness of its expression.
const_cast ,顾名思义,将转换掉表达式的 const 性质。
Any type conversion that the compiler performs implicitly can be explicitly requested by using a static_cast:
编译器隐式执行的任何类型转换都可以由 static_cast 显式完成:
double d = 97.0;
double d = 97.0;
// cast specified to indicate that the conversion is intentional
// cast specified to indicate that the conversion is intentional
char ch = static_cast(d);
char ch = static_cast(d);
Such casts are useful when assigning a larger arithmetic type to a smaller type. The cast informs both the reader of the program and the compiler that we are aware of and are not concerned about the potential loss of precision. Compilers often generate a warning for assignments of a larger arithmetic type to a smaller type. When we provide the explicit cast, the warning message is turned off.
当需要将一个较大的算术类型赋值给较小的类型时,使用强制转换非常有用。此时,强制类型转换告诉程序的读者和编译器:我们知道并且不关心潜在的精度损失。对于从一个较大的算术类型到一个较小类型的赋值,编译器通常会产生警告。当我们显式地提供强制类型转换时,警告信息就会被关闭。
A static_cast is also useful to perform a conversion that the compiler will not generate automatically. For example, we can use a static_cast to retrieve a pointer value that was stored in a void* pointer:
如果编译器不提供自动转换,使用 static_cast 来执行类型转换也是很有用的。例如,下面的程序使用 static_cast 找回存放在 void* 指针中的值:
void* p = &d; // ok: address of any data object can be stored in a void*
void* p = &d; // ok: address of any data object can be stored in a void*
// ok: converts void* back to the original pointer type
// ok: converts void* back to the original pointer type
double *dp = static_cast<double*>(p);
double *dp = static_cast<double*>(p);
When we store a pointer in a void* and then use a static_cast to cast the pointer back to its original type, we are guaranteed that the pointer value is preserved. That is, the result of the cast will be equal to the original address value.
……
By using a cast, the programmer turns off or dampens normal type-checking. We strongly recommend that programmers avoid casts and believe that most well-formed C++ programs can be written without relying on casts.
强制类型转换关闭或挂起了正常的类型检查。强烈建议程序员避免使用强制类型转换,不依赖强制类型转换也能写出很好的 C++ 程序。
Prior to the introduction of named cast operators, an explicit cast was performed by enclosing a type in parentheses:
在引入命名的强制类型转换操作符之前,显式强制转换用圆括号将类型括起来实现:
char *pc = (char*) ip;
char *pc = (char*) ip;
The effect of this cast is the same as using the reinterpret_cast notation. However, the visibility of this cast is considerably less, making it even more difficult to track down the rogue cast.
效果与使用 reinterpret_cast 符号相同,但这种强制转换的可视性比较差,难以跟踪错误的转换。
Standard C++ introduced the named cast operators to make casts more visible and to give the programmer a more finely tuned tool to use when casts are necessary. For example, nonpointer static_casts and const_casts tend to be safer than reinterpret_casts. As a result, the programmer (as well as readers and tools operating on the program) can clearly identify the potential risk level of each explicit cast in code.
标准 C++ 为了加强类型转换的可视性,引入命名的强制转换操作符,为程序员在必须使用强制转换时提供了更好的工具。例如,非指针的 static_cast 和 const_cast 要比 reinterpret_cast 更安全。结果使程序员(以及读者和操纵程序的工具)可清楚地辨别代码中每个显式的强制转换潜在的风险级别。
A conversion from one arithmetic type to another. In the context of the binary arithmetic operators, arithmetic conversions usually attempt to preserve precision by converting a smaller type to a larger type (e.g., small integral types, such as char and short, are converted to int).
算术类型之间的转换。在使用二元算术操作符的地方,算术转换通常将较小的类型转换为较大的类型,以确保精度(例如,将小的整型 char 型和 short 型转换为 int 型)。
Determines how operators of the same precedence are grouped. Operators can be either right associative (operators are grouped from right to left) or left associative (operators are grouped from left to right).
决定同一优先级的操作符如何结合。C++ 的操作符要么是左结合(操作符从左向右结合)要么是右结合(操作符从右向左结合)。
Compound expression:An expression involving more than one operator.
复合表达式:含有多个操作符的表达式。
A null statement is useful where the language requires a statement but the program's logic does not. Such usage is most common when a loop's work can be done within the condition.
如果在程序的某个地方,语法上需要一个语句,但逻辑上并不需要,此时应该使用空语句。这种用法常见于在循环条件判断部分就能完成全部循环工作的情况。
A compound statement, usually referred to as a block, is a (possibly empty) sequence of statements surrounded by a pair of curly braces. A block is a scope. Names introduced within a block are accessible only from within that block or from blocks nested inside the block. As usual, a name is visible only from its point of definition until the end of the enclosing block.
复合语句,通常被称为块,是用一对花括号括起来的语句序列(也可能是空的)。块标识了一个作用域,在块中引入的名字只能在该块内部或嵌套在块中的子块里访问。通常,一个名字只从其定义处到该块的结尾这段范围内可见。
Compound statements can be used where the rules of the language require a single statement, but the logic of our program needs to execute more than one. For example, the body of a while or for loop must be a single statement. Yet, we often need to execute more than one statement in the body of a loop. We can do so by enclosing the statements in a pair of braces, thus turning the sequence of statements into a block.
复合语句用在语法规则要求使用单个语句但程序逻辑却需要不止一个语句的地方。例如,while 或 for 语句的循环体必须是单个语句。然而,大多数情况都需要在循环体里执行多个语句。因而可使用一对花括号将语句序列括起来,使其成为块语句。
One advantage of limiting the scope of variables defined within a control statement to that statement is that the names of such variables can be reused without worrying about whether their current value is correct at each use. If the name is not in scope, then it is impossible to use that name with an incorrect, leftover value.
对于在控制语句中定义的变量,限制其作用域的一个好处是,这些变量名可以重复使用而不必担心它们的当前值在每一次使用时是否正确。对于作用域外的变量,是不可能用到其在作用域内的残留值的。
flow control:
flow control:
if(),if true then
if(),if true then
if(!),if false then
if(!),if false then
switch(sm){case rval:……,if sm's rvalue is rval,then……
switch(sm){case rval:……,if sm's rvalue is rval,then……
while true, for true……
while true, for true……
do……until while false
do……until while false
……
预处理器还定义了其余四种在调试时非常有用的常量:
__FILE__ name of the file.
__FILE__ name of the file.
__FILE__ 文件名
__FILE__ 文件名
__LINE__ current line number.
__LINE__ current line number.
__LINE__ 当前行号
__LINE__ 当前行号
__TIME__ time the file was compiled.
__TIME__ time the file was compiled.
__TIME__ 文件被编译的时间
__TIME__ 文件被编译的时间
__DATE__ date the file was compiled.
__DATE__ date the file was compiled.
__DATE__ 文件被编译的日期
__DATE__ 文件被编译的日期
cout<< "File: " << __FILE__
cout<< "File: " << __FILE__
<< " : line " << __LINE__ << endl
<< " : line " << __LINE__ << endl
<< " Compiled on " << __DATE__
<< " Compiled on " << __DATE__
<< " at " << __TIME__ << endl;
<< " at " << __TIME__ << endl;
……
实现两个指针的交换。已知需用 * 定义指针,用 & 定义引用。
// swap values of two pointers to int
// swap values of two pointers to int
void ptrswap(int *&v1, int *&v2)
void ptrswap(int *&v1, int *&v2)
{
{
int *tmp = v2;
int *tmp = v2;
v2 = v1;
v2 = v1;
v1 = tmp;
v1 = tmp;
}
}
Part of the work of designing a function with default arguments is ordering the parameters so that those least likely to use a default value appear first and those most likely to use a default appear last.
设计带有默认实参的函数,其中部分工作就是排列形参,使最少使用默认实参的形参排在最前,最可能使用默认实参的形参排在最后。
Default arguments ordinarily should be specified with the declaration for the function and placed in an appropriate header.
通常,应在函数声明中指定默认实参,并将该声明放在合适的头文件中。
Automatic objects, including parameters, are destroyed at the end of the block in which they were defined. Parameters are defined in the function's block and so are destroyed when the function terminates. When a function exits, its local storage is deallocated. After the function exits, the values of its automatic objects and parameters are no longer accessible.
自动对象,包括形参,都在定义它们的块语句结束时撤销。形参在函数块中定义,因此当函数的执行结束时撤销。当函数结束时,会释放它的局部存储空间。在函数结束后,自动对象和形参的值都不能再访问了。
Unlike other function definitions, inlines should be defined in header files.
内联函数应该在头文件中定义,这一点不同于其他函数。
An inline function may be defined more than once in a program as long as the definition appears only once in a given source file and the definition is exactly the same in each source file. By putting inline functions in headers, we ensure that the same definition is used whenever the function is called and that the compiler has the function definition available at the point of call.
inline 函数可能要在程序中定义不止一次,只要 inline 函数的定义在某个源文件中只出现一次,而且在所有源文件中,其定义必须是完全相同的。把 inline 函数的定义放在头文件中,可以确保在调用函数时所使用的定义是相同的,并且保证在调用点该函数的定义对编译器可见。
The colon and the following text up to the open curly is the constructor initializer list. A constructor initializer list specifies initial values for one or more data members of the class. It follows the constructor parameter list and begins with a colon. The constructor initializer is a list of member names, each of which is followed by that member's initial value in parentheses. Multiple member initializations are separated by commas.
在冒号和花括号之间的代码称为构造函数的初始化列表。构造函数的初始化列表为类的一个或多个数据成员指定初值。它跟在构造函数的形参表之后,以冒号开关。构造函数的初始化式是一系列成员名,每个成员后面是括在圆括号中的初始值。多个成员的初始化用逗号分隔。
The compiler-created default constructor is known as a synthesized default constructor. It initializes each member using the same rules as are applied for variable initializations. Members that are of class type, such as isbn, are initialized by using the default constructor of the member's own class. The initial value of members of built-in type depend on how the object is defined. If the object is defined at global scope (outside any function) or is a local static object, then these members will be initialized to 0. If the object is defined at local scope, these members are uninitialized. As usual, using an uninitialized member for any purpose other than giving it a value is undefined.
由编译器创建的默认构造函数通常称为默认构造函数,它将依据如同变量初始化的规则初始化类中所有成员。对于具有类类型的成员,如 isbn,则会调用该成员所属类自身的默认构造函数实现初始化。内置类型成员的初值依赖于对象如何定义。如果对象在全局作用域中定义(即不在任何函数中)或定义为静态局部对象,则这些成员将被初始化为 0。如果对象在局部作用域中定义,则这些成员没有初始化。除了给它们赋值之外,出于其他任何目的对未初始化成员的使用都没有定义。
Two functions that appear in the same scope are overloaded if they have the same name but have different parameter lists.
出现在相同作用域中的两个函数,如果具有相同的名字而形参表不同,则称为重载函数。
There is more than one function that matches and none of the matches is clearly best. This case is also an error; the call is ambiguous.
存在多个与实参匹配的函数,但没有一个是明显的最佳选择。这种情况也是,该调用具有二义性。
This call is therefore ambiguous: Each viable function is a better match on one of the arguments to the call. The compiler will generate an error. We could force a match by explicitly casting one of our arguments:
因此,这个调用有二义性:每个可行函数都对函数调用的一个实参实现更好的匹配。编译器将产生错误。解决这样的二义性,可通过显式的强制类型转换强制函数匹配:
f(static_cast(42), 2.56); // calls f(double, double)
f(static_cast(42), 2.56); // calls f(double, double)
f(42, static_cast(2.56)); // calls f(int, int)
f(42, static_cast(2.56)); // calls f(int, int)
Overloading and const Parameters
重载和 const 形参
Whether a parameter is const only matters when the parameter is a reference or pointer.
仅当形参是引用或指针时,形参是否为 const 才有影响。
It is worth noting that we cannot overload based on whether the pointer itself is const:
注意不能基于指针本身是否为 const 来实现函数的重载:
f(int *);
f(int *);
f(int *const); // redeclaration
f(int *const); // redeclaration
Returning a Pointer to Function
返回指向函数的指针
// ff is a function taking an int and returning a function pointer
// ff is a function taking an int and returning a function pointer
// the function pointed to returns an int and takes an int* and an int
// the function pointed to returns an int and takes an int* and an int
int (*ff(int))(int*, int);
int (*ff(int))(int*, int);
……
使用 typedef 可使该定义更简明易懂:
// PF is a pointer to a function returning an int, taking an int* and an int
// PF is a pointer to a function returning an int, taking an int* and an int
typedef int (*PF)(int*, int);
typedef int (*PF)(int*, int);
PF ff(int); // ff returns a pointer to function
PF ff(int); // ff returns a pointer to function
We can define a parameter as a function type. A function return type must be a pointer to function; it cannot be a function.
允许将形参定义为函数类型,但函数的返回类型则必须是指向函数的指针,而不能是函数。
An argument to a parameter that has a function type is automatically converted to the corresponding pointer to function type. The same conversion does not happen when returning a function:
具有函数类型的形参所对应的实参将被自动转换为指向相应函数类型的指针。但是,当返回的是函数时,同样的转换操作则无法实现:
// func is a function type, not a pointer to function!
// func is a function type, not a pointer to function!
typedef int func(int*, int);
typedef int func(int*, int);
void f1(func); // ok: f1 has a parameter of function type
void f1(func); // ok: f1 has a parameter of function type
func f2(int); // error: f2 has a return type of function type
func f2(int); // error: f2 has a return type of function type
func *f3(int); // ok: f3 returns a pointer to function type
func *f3(int); // ok: f3 returns a pointer to function type
Functions are named units of computation and are essential to structuring even modest programs. They are defined by specifying a return type, a name, a (possibly empty) list of parameters, and a function body. The function body is a block that is executed when the function is called. When a function is called, the arguments passed to the function must be compatible with the types of the corresponding parameters.
函数是有名字的计算单元,对程序(就算是小程序)的结构化至关重要。函数的定义由返回类型、函数名、形参表(可能为空)以及函数体组成。函数体是调用函数时执行的语句块。在调用函数时,传递给函数的实参必须与相应的形参类型兼容。
Passing an argument to a function follows the same rules as initializing a variable. Each parameter that has nonreference type is initialized as a copy of the corresponding argument. Any changes made to a (nonreference) parameter are made to the local copy, not to the argument itself.
给函数传递实参遵循变量初始化的规则。非引用类型的形参以相应实参的副本初始化。对(非引用)形参的任何修改仅作用于局部副本,并不影响实参本身。
Copying large, complex values can be expensive. To avoid the overhead of passing a copy, parameters can be specified as references. Changes made to reference parameters are reflected in the argument itself. A reference parameter that does not need to change its argument should be const reference.
复制庞大而复杂的值有昂贵的开销。为了避免传递副本的开销,可将形参指定为引用类型。对引用形参的任何修改会直接影响实参本身。应将不需要修改相应实参的引用形参定义为 const 引用。
In C++, functions may be overloaded. The same name may be used to define different functions as long as the number or types of the parameters in the functions differ. The compiler automatically figures out which function to call based the arguments in a call. The process of selecting the right function from a set of overloaded functions is referred to as function matching.
在 C++ 中,函数可以重载。只要函数中形参的个数或类型不同,则同一个函数名可用于定义不同的函数。编译器将根据函数调用时的实参确定调用哪一个函数。在重载函数集合中选择适合的函数的过程称为函数匹配。
C++ provides two special kinds of functions: inline and member functions. Specifying inline on a function is a hint to the compiler to expand the function into code directly at the call point. Inline functions avoid the overhead associated with calling a function. Member functions are just that: class members that are functions.
C++ 提供了两种特殊的函数:内联函数和成员函数。将函数指定为内联是建议编译器在调用点直接把函数代码展开。内联函数避免了调用函数的代价。成员函数则是身为类成员的函数。
arguments
实参
Values supplied when calling a function. These values are used to initialize the corresponding parameters in the same way that variables of the same type are initialized.
调用函数时提供的值。这些值用于初始化相应的形参,其方式类似于初始化同类型变量的方法。
const member function
常量成员函数
Function that is member of a class and that may be called for const objects of that type. const member functions may not change the data members of the object on which they operate.
类的成员函数,并可以由该类类型的常量对象调用。常量成员函数不能修改所操纵的对象的数据成员。
constructor initializer list
构造函数初始化列表
List used in a constructor to specify initial values for data members. The initializer list appears in the definition of a constructor between the parameter list and the constructor body. The list consists of a colon followed by a comma-separated list of member names, each of which is followed by that member's initial value in parentheses.
在构造函数中用于为数据成员指定初值的表。初始化列表出现在构造函数的定义中,位于构造函数体与形参表之间。该表由冒号和冒号后面的一组用逗号分隔的成员名组成,每一个成员名后面跟着用圆括号括起来的该成员的初值。
function prototype
函数原型
Synonym for function declaration. The name, return type, and parameter types of a function. To call a function, its prototype must have been declared before the point of call.
函数声明的同义词。包括了函数的名字、返回类型和形参类型。调用函数时,必须在调用点之前声明函数原型。
inline function
内联函数
Function that is expanded at the point of call, if possible. Inline functions avoid the normal function-calling overhead by replacing the call by the function's code.
如果可能的话,将在调用点展开的函数。内联函数直接以函数代码替代了函数调用点展开的函数。内联函数直接以函数代码替代了函数调用语句,从而避免了一般函数调用的开销。
local static objects
局部静态对象
Local object that is created and initialized once before the function is first called and whose value persists across invocations of the function.
在函数第一次调用前就已经创建和初始化的局部对象,其值在函数的调用之间保持有效。
parameters
形参
Variables local to a function whose initial values are supplied when the function is called.
函数的局部变量,其初值由函数调用提供。
temporary object
临时对象
Unnamed object automatically created by the compiler in the course of evaluating an expression. The phrase temporary object is usually abreviated as temporary. A temporary persists until the end of the largest expression that encloses the expression for which it was created.
在求解表达式的过程中由编译器自动创建的没有名字的对象。“临时对象”这个术语通常简称为“临时”。临时对象一直存在直到最大表达式结束为止,最大表达式指的是包含创建该临时对象的表达式的最大范围内的表达式。
this pointer
this 指针
Implicit parameter of a member function. this points to the object on which the function is invoked. It is a pointer to the class type. In a const member function the pointer is a pointer to const.
成员函数的隐式形参。this 指针指向调用该函数的对象,是指向类类型的指针。在 const 成员函数中,该指针也指向 const 对象。
In C++, input/output is provided through the library. The library defines a family of types that support IO to and from devices such as files and console windows. Additional types allow strings to act like files, which gives us a way to convert data to and from character forms without also doing IO. Each of these IO types defines how to read and write values of the built-in data types. In addition, class designers generally use the library IO facilities to read and write objects of the classes that they define. Class types are usually read and written using the same operators and conventions that the IO library defines for the built-in types.
C++ 的输入/输出(input/output)由标准库提供。标准库定义了一族类型,支持对文件和控制窗口等设备的读写(IO)。还定义了其他一些类型,使 string 对象能够像文件一样操作,从而使我们无须 IO 就能实现数据与字符之间的转换。这些 IO 类型都定义了如何读写内置数据类型的值。此外,一般来说,类的设计者还可以很方便地使用 IO 标准库设施读写自定义类的对象。类类型通常使用 IO 标准库为内置类型定义的操作符和规则来进行读写。
getline function, which takes a reference to an istream and a reference to a string and reads a word from the istream into the string
getline 函数,需要分别取 istream 类型和 string 类型的两个引用形参,其功能是从 istream 对象读取一个单词,然后写入 string 对象中。
The IO types and objects we've used so far read and write streams of data and are used to interact with a user's console window. Of course, real programs cannot be limited to doing IO solely to or from a console window. Programs often need to read or write named files. Moreover, it can be quite convenient to use the IO operations to format data in memory, thereby avoiding the complexity and run-time expense of reading or writing to a disk or other device. Applications also may have to read and write languages that require wide-character support.
迄今为止,我们已经使用 IO 类型和对象读写数据流,它们常用于与用户控制窗口的交互。当然,实际的程序不能仅限于对控制窗口的 IO,通常还需要读或写已命名的文件。此外,程序还应该能方便地使用 IO 操作格式化内存中的数据,从而避免读写磁盘或其他设备的复杂性和运行代价。应用程序还需要支持宽字符(wide-character)语言的读写。
Using inheritance for the IO types has another important implication: When we have a function that takes a reference to a base-class type, we can pass an object of a derived type to that function. This fact means that a function written to operate on istream& can be called with an ifstream or istringstream object. Similarly, a function that takes an ostream& can be called with an ofstream or ostringstream object. Because the IO types are related by inheritance, we can write one function and apply it to all three kinds of streams: console, disk files, or string streams.
对 IO 类型使用继承还有另外一个重要的含义:如果函数有基类类型的引用形参时,可以给函数传递其派生类型的对象。这就意味着:对 istream& 进行操作的函数,也可使用 ifstream 或者 istringstream 对象来调用。类似地,形参为 ostream& 类型的函数也可用 ofstream 或者 ostringstream 对象调用。因为 IO 类型通过继承关联,所以可以只编写一个函数,而将它应用到三种类型的流上:控制台、磁盘文件或者字符串流(string streams)。
the comma operator executes by evaluating each operand and returns its rightmost operand as its result.
逗号操作符的求解过程:首先计算它的每一个操作数,然后返回最右边操作数作为整个操作的结果。
For historical reasons, the IO library uses C-style character strings rather than C++ strings to refer to file names. When we call open or use a file name as the initializer when creating an fstream object, the argument we pass is a C-style string, not a library string. Often our programs obtain file names by reading the standard input. As usual, it is a good idea to read into a string, not a C-style character array. Assuming that the name of the file we wish to use is in a string, we can use the c_str member to obtain a C-style string.
由于历史原因,IO 标准库使用 C 风格字符串而不是 C++ strings 类型的字符串作为文件名。在创建 fstream 对象时,如果调用 open 或使用文件名作初始化式,需要传递的实参应为 C 风格字符串,而不是标准库 strings 对象。程序常常从标准输入获得文件名。通常,比较好的方法是将文件名读入 string 对象,而不是 C 风格字符数组。假设要使用的文件名保存在 string 对象中,则可调用 c_str 成员获取 C 风格字符串。
By default, files associated with an ifstream are opened in in mode, which is the mode that permits the file to be read. Files opened by an ofstream are opened in out mode, which permits the file to be written. A file opened in out mode is truncated: All data stored in the file is discarded.
默认时,与 ifstream 流对象关联的文件将以 in 模式打开,该模式允许文件做读的操作:与 ofstream 关联的文件则以 out 模式打开,使文件可写。以 out 模式打开的文件会被清空:丢弃该文件存储的所有数据。
In effect, specifying out mode for an ofstream is equivalent to specifying both out and trunc.
从效果来看,为 ofstream 对象指定 out 模式等效于同时指定了 out 和 trunc 模式。
……
stringstream 对象的和使用
string line, word; // will hold a line and word from input, respectively
string line, word; // will hold a line and word from input, respectively
while (getline(cin, line)) { // read a line from the input into line
while (getline(cin, line)) { // read a line from the input into line
// do per-line processing
// do per-line processing
istringstream stream(line); // bind to stream to the line we read
istringstream stream(line); // bind to stream to the line we read
while (stream >> word){ // read a word from line
while (stream >> word){ // read a word from line
// do per-word processing
// do per-word processing
}
}
}
}
Here we use getline to get an entire line from the input. To get the words in each line, we bind an istringstream to the line that we read. We can then use the normal string input operator to read the words from each line.
这里,使用 getline 函数从输入读取整行内容。然后为了获得每行中的单词,将一个 istringstream 对象与所读取的行绑定起来,这样只需要使用普通的 string 输入操作符即可读出每行中的单词。
Containers define only a small number of operations. Many additional operations are provided by the algorithms librar. For those operations that are defined by the containers, the library imposes a common interface. The containers vary as to which operations they provide, but if two containers provide the same operation, then the interface (name and number of arguments) will be the same for both container types. The set of operations on the container types form a kind of hierarchy:
容器只定义了少量操作。大多数额外操作则由算法库提供。标准库为由容器类型定义的操作强加了公共的接口。这些容器类型的差别在于它们提供哪些操作,但是如果两个容器提供了相同的操作,则它们的接口(函数名字和参数个数)应该相同。容器类型的操作集合形成了以下层次结构:
Some operations are supported by all container types.
一些操作适用于所有容器类型。
Other operations are common to only the sequential or only the associative containers.
另外一些操作则只适用于顺序或关联容器类型。
Still others are common to only a subset of either the sequential or associative containers.
还有一些操作只适用于顺序或关联容器类型的一个子集。
Because the containers meet the constraints on element types, we can define a container whose element type is itself a container type. For example, we might define lines as a vector whose elements are a vector of strings:
因为容器受容器元素类型的约束,所以可定义元素是容器类型的容器。例如,可以定义 vector 类型的容器 lines,其元素为 string 类型的 vector 对象:
// note spacing: use ">>" not ">>" when specifying a container element type
// note spacing: use ">>" not ">>" when specifying a container element type
vector< vector > lines; // vector of vectors
vector< vector > lines; // vector of vectors
Note the spacing used when specifying a container element type as a container:
注意,在指定容器元素为容器类型时,必须如下使用空格:
vector< vector > lines; // ok: space required between close >
vector< vector > lines; // ok: space required between close >
vector< vector> lines; // error: >> treated as shift operator
vector< vector> lines; // error: >> treated as shift operator
The begin and end operations yield iterators that refer to the first and one past the last element in the container. These iterators are most often used to form an iterator range that encompasses all the elements in the container.
begin 和 end 操作产生指向容器内第一个元素和最后一个元素的下一位置的迭代器。这两个迭代器通常用于标记包含容器中所有元素的迭代器范围。
Iterators may be invalidated after doing any insert or push operation on a vector or deque. When writing loops that insert elements into a vector or a deque, the program must ensure that the iterator is refreshed on each trip through the loop.
任何 insert 或 push 操作都可能导致迭代器失效。当编写循环将元素插入到 vector 或 deque 容器中时,程序必须确保迭代器在每次循环后都得到更新。
In addition to the sequential containers, the library provides three sequential container adaptors: queue, priority_queue, and stack. An adaptor is a general concept in the library. There are container, iterator, and function adaptors. Essentially, an adaptor is a mechanism for making one thing act like another. A container adaptor takes an existing container type and makes it act like a different abstract type. For example, the stack adaptor takes any of the sequential containers and makes it operate as if it were a stack.
除了顺序容器,标准库还提供了三种顺序容器适配器:queue、priority_queue 和 stack。适配器(adaptor)是标准库中通用的概念,包括容器适配器、迭代器适配器和函数适配器。本质上,适配器是使一事物的行为类似于另一事物的行为的一种机制。容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。例如,stack(栈)适配器可使任何一种顺序容器以栈的方式工作。
By default both stack and queue are implemented in terms of deque, and a priority_queue is implemented on a vector. We can override the default container type by naming a sequential container as a second type argument when creating the adaptor:
默认的 stack 和 queue 都基于 deque 容器实现,而 priority_queue 则在 vector 容器上实现。在创建适配器时,通过将一个顺序容器指定为适配器的第二个类型实参,可覆盖其关联的基础容器类型:
// empty stack implemented on top of vector
// empty stack implemented on top of vector
stack< string, vector > str_stk;
stack< string, vector > str_stk;
// str_stk2 is implemented on top of vector and holds a copy of svec
// str_stk2 is implemented on top of vector and holds a copy of svec
stack > str_stk2(svec);
stack > str_stk2(svec);
There are constraints on which containers can be used for a given adapator. We can use any of the sequential containers as the underlying container for a stack. Thus, a stack can be built on a vector, list, or deque. The queue adapator requires push_front in its underlying container, and so could be built on a list but not on a vector. A priority_queue requires random access and so can be built on a vector or a deque but not on a list.
对于给定的适配器,其关联的容器必须满足一定的约束条件。stack 适配器所关联的基础容器可以是任意一种顺序容器类型。因此,stack 栈可以建立在 vector、list 或者 deque 容器之上。而 queue 适配器要求其关联的基础容器必须提供 push_front 运算,因此只能建立在 list 容器上,而不能建立在 vector 容器上。priority_queue 适配器要求提供随机访问功能,因此可建立在 vector 或 deque 容器上,但不能建立在 list 容器上。
A priority_queue lets us establish a priority among the elements held in the queue. Rather than place a newly entered item at the back of the queue, the item is placed ahead of all those items with a lower priority. By default, the library uses the < operator on the element type to determine relative priorities.
priority_queue 允许用户为队列中存储的元素设置优先级。这种队列不是直接将新元素放置在队列尾部,而是放在比它优先级低的元素前面。标准库默认使用元素类型的 < 操作符来确定它们之间的优先级关系。
container
容器
A type that holds a collection of objects of a given type. Each library container type is a template type. To define a container, we must specify the type of the elements stored in the container. The library containers are variable-sized.
一种存储给定类型对象集合的类型。所有标准库容器类型都是模板类型。定义容器时,必须指定在该容器中存储的元素是什么类型。标准库容器具有可变的长度。
list
列表
Sequential container. Elements in a list may be accessed only sequentiallystarting from a given element, we can get to another element by incrementing or decrementing across each element between them. Supports fast insertion (or deletion) anywhere in the list. Adding elements does not affect other elements in the list; iterators remain valid when new elements are added. When an element is removed, only the iterators to that element are invalidated.
一种顺序容器。list 中的元素只能顺序访问——从给定元素开始,要获取另一个元素,则必须通过自增或自减迭代器的操作遍历这两个元素之间的所有元素。list 容器支持在容器的任何位置实现快速插入(或删除)运算。新元素的插入不会影响 list 中的其他元素。插入元素时,迭代器保持有效;删除元素时,只有指向该元素的迭代器失效。
priority_queue
优先级队列
Adaptor for the sequential containers that yields a queue in which elements are inserted, not at the end but according to a specified priority level. By default, priority is determined by using the less-than operator for the element type.
一种顺序容器适配器。在这种队列中,新元素不是在队列尾部插入,而是根据指定的优先级级别插入。默认情况下,元素的优先级由元素类型的小于操作符决定。
queue
队列
Adaptor for the sequential containers that yields a type that lets us add elements to the back and remove elements from the front.
一种顺序容器适配器。在这种队列中,保证只在队尾插入新元素,而且只在队首删除元素。
sequential container
顺序容器
A type that holds an ordered collection of objects of a single type. Elements in a sequential container are accessed by position.
以有序集合的方式存储单一类型对象的类型。顺序容器中的元素可通过下标访问。
stack
栈
Adaptor for the sequential containers that yields a type that lets us add and remove elements from one end only.
一种顺序容器适配器,这种类型只能在一端插入和删除元素。
vector
向量
Sequential container. Elements in a vector are accessed by their positional index. We add elements to a vector by calling push_back or insert. Adding elements to a vector might cause it be reallocated, invalidating all iterators into the vector. Adding (or removing) an element in the middle of a vector invalidates all iterators to elements after the insertion (or deletion) point.
一种顺序容器。vector 中的元素通过其位置下标访问。可通过调用 push_back 或 insert 函数在 vector 中添加元素。在 vector 中添加元素可能会导致重新为容器分配内存空间,也可能会使所有的迭代器失效。在 vector 容器中间添加(或删除)元素将使所有指向插入(或删除)点后面的元素的迭代器失效。
Associative containers differ in a fundamental respect from the sequential containers: Elements in an associative container are stored and retrieved by a key, in contrast to elements in a sequential container, which are stored and accessed sequentially by their position within the container.
关联容器和顺序容器的本质差别在于:关联容器通过键(key)存储和读取元素,而顺序容器则通过元素在容器中的位置顺序存储和访问元素。
……
对于分书问题,第i个人是否喜欢第j本书,可以用一个二维数组来表示:int like[i][j]。
……
int take[5]={0,0,0,0,0};//记录每一本书的分配情况
An object of the map or set type may contain only a single element with a given key. There is no way to add a second element with the same key. If we need to have multiple instances with a single key, then we can use multimap or multi set, which do allow multiple elements with a given key.
set 和 map 类型的对象所包含的元素都具有不同的键,不允许为同一个键添加第二个元素。如果一个键必须对应多个实例,则需使用 multimap 或 multi set,这两种类型允许多个元素拥有相同的键。
The associative containers support many of the same operations as do the sequential containers. They also provide specialized operations that manage or use the key.
关联容器支持很多顺序容器也提供的相同操作,此外,还提供管理或使用键的特殊操作。
map: Associative array; elements stored and retrieved by key
关联数组:元素通过键来存储和读取
set: Variable-sized collection with fast retrieval by key
大小可变的集合,支持通过键实现的快速读取
There is one important consequence of the fact that elements are ordered by key: When we iterate across an associative container, we are guaranteed that the elements are accessed in key order, irrespective of the order in which the elements were placed in the container.
“容器元素根据键的次序排列”这一事实就是一个重要的结论:在迭代遍历关联容器时,我们可确保按键的顺序的访问元素,而与元素在容器中的存放位置完全无关。
A map is a collection of keyvalue pairs. The map type is often referred to as an associative array: It is like the built-in array type, in that the key can be used as an index to fetch a value. It is associative in that values are associated with a particular key rather than being fetched by position in the array.
map 是键-值对的集合。map 类型通常可理解为关联数组(associative array):可使用键作为下标来获取一个值,正如内置数组类型一样。而关联的本质在于元素的值与某个特定的键相关联,而并非通过元素在数组中的位置来获取。
Whenever we use an associative container, its keys have not only a type, but also an associated comparison function. By default, the library uses the < operator for the key type to compare the keys.
在使用关联容器时,它的键不但有一个类型,而且还有一个相关的比较函数。默认情况下,标准库使用键类型定义的 < 操作符来实现键(key type)的比较。
Both map and set can contain only a single instance of each key. The types multiset and a multimap allow multiple instances of a key. In a phone directory, for example, someone might wish to provide a separate listing for each phone number associated with an individual. A listing of available texts by an author might provide a separate listing for each title. The multimap and multiset types are defined in the same headers as the corresponding single-element versions: the map and set headers, respectively.
map 和 set 容器中,一个键只能对应一个实例。而 multiset 和 multimap 类型则允许一个键对应多个实例。例如,在电话簿中,每个人可能有单独的电话号码列表。在作者的文章集中,每位作者可能有单独的文章标题列表。multimap 和 multiset 类型与相应的单元素版本具有相同的头文件定义:分别是 map 和 set 头文件。
The operations supported by multimap and multiset are the same as those on map and set, respectively, with one exception: multimap does not support subscripting. We cannot subscript a multimap because there may be more than one value associated with a given key. The operations that are common to both map and multimap or set and multiset change in various ways to reflect the fact that there can be multiple keys. When using either a multimap or multiset, we must be prepared to handle multiple values, not just a single value.
multimap 和 multiset 所支持的操作分别与 map 和 set 的操作相同,只有一个例外:multimap 不支持下标运算。不能对 multimap 对象使用下标操作,因为在这类容器中,某个键可能对应多个值。为了顺应一个键可以对应多个值这一性质,map 和 multimap,或 set 和 multiset 中相同的操作都以不同的方式做出了一定的修改。在使用 multimap 或 multiset 时,对于某个键,必须做好处理多个值的准备,而非只有单一的值。
We noted that maps and sets store their elements in order. The multimap and multiset types do so as well. As a result, when a multimap or multiset has multiple instances of a given key, those instances will be adjacent elements within the container.
注意到,关联容器 map 和 set 的元素是按顺序存储的。而 multimap 和 multset 也一样。因此,在 multimap 和 multiset 容器中,如果某个键对应多个实例,则这些实例在容器中将相邻存放。
The elements in an associative container are ordered and accessed by key. The associative containers support efficient lookup and retrieval of elements by key. The use of a key distinguishes them from the sequential containers, in which elements are accessed positionally.
关联容器的元素按键排序和访问。关联容器支持通过键高效地查找和读取元素。键的使用,使关联容器区别于顺序容器,顺序容器的元素是根据位置访问的。
The map and multimap types store elements that are keyvalue pairs. These types use the library pair class, defined in the utility header, to represent these pairs. Dereferencing a map or multimap iterator yields a value that is a pair. The first member of the pair is a const key, and the second member is a value associated with that key. The set and multiset types store keys. The map and set types allow only one element with a given key. The multimap and multiset types allow multiple elements with the same key.
map 和 multimap 类型存储的元素是键-值对。它们使用在 utility 头文件中定义的标准库 pair 类,来表示这些键-值对元素。对 map 或 multimap 迭代器进行解引用将获得 pair 类型的值。pair 对象的 first 成员是一个 const 键,而 second 成员则是该键所关联的值。set 和 multiset 类型则专门用于存储键。在 map 和 set 类型中,一个键只能关联一个元素。而 multimap 和 multiset 类型则允许多个元素拥有相同的键。
The associative containers share many operations with the sequential containers. However, the associative containers define some new operations and redefine the meaning or return types of some operations that are in common with the sequential containers. The differences in the operations reflect the use of keys in associative containers.
关联容器共享了顺序容器的许多操作。除此之外,关联容器还定义一些新操作,并对某些顺序容器同样提供的操作重新定义了其含义或返回类型,这些操作的差别体现了关联容器中键的使用。
Elements in an associative container can be accessed by iterators. The library guarantees that iterators access elements in order by key. The begin operation yields the element with the lowest key. Incrementing that iterator yields elements in nondescending order.
关联容器的元素可用迭代器访问。标准库保证迭代器按照键的次序访问元素。begin 操作将获得拥有最小键的元素,对此迭代器作自增运算则可以按非降序依次访问各个元素。
associative array
关联数组
Array whose elements are indexed by key rather than positionally. We say that the array maps a key to its associated value.
由键而不是位置来索引元素的数组。通常描述为:此类数组将键映射到其关联的值上。
associative container
关联容器
A type that holds a collection of objects that supports efficient lookup by key.
存储对象集合的类型,支持通过键的高效查询。
key_type
键类型
Type defined by the associative containers that is the type for the keys used to store and retrieve values. For a map, key_type is the type used to index the map. For set, key_type and value_type are the same.
关联容器定义的类型,表示该容器在存储或读取值时所使用的键的类型。对于 map 容器,key_type 是用于索引该容器的类型。对于 set 容器,key_type 与 value_type 相同。
map
映射
Associative container type that defines an associative array. Like vector, map is a class template. A map, however, is defined with two types: the type of the key and the type of the associated value. In a map a given key may appear only once. Each key is associated with a particular value. Dereferencing a map iterator yields a pair that holds a const key and its associated value.
定义关联数组的关联容器类型。与 vector 容器一样,map 也是类模板。但是,map 容器定义了两种类型:键类型及其关联的值类型。在 map 中,每个键只能出现一次,并关联某一具体的值。对 map 容器的迭代器进行解引用将获得一个 pair 对象,该对象存储了一个 const 键和它所关联的值。
pair
pair
Type that holds two public data members named first and second. The pair type is a template type that takes two type parameters that are used as the types of these members.
一种类型,有两个 public 数据成员,分别名为 first 和 second。pair 类型是带有两个类型形参的模板类型,它的类型形参用作数据成员的类型。
set
set
Associative container that holds keys. In a set, a given key may appear only once.
只存储键的关联容器。在 set 容器中,一个键只能出现一次。
strict weak ordering
严格弱排序
Relationship among the keys used in an associative container. In a strict weak ordering, it is possible to compare any two values and determine which of the two is less than the other. If neither value is less than the other, then the two values are considered equal.
关联容器所使用的键之间的比较关系。在这种关系下,任意两个元素都可比较,并能确定两者之间谁比谁小。如果两个值都不比对方小,则这两个值相等。
The library containers define a surprisingly small set of operations. Rather than adding lots of functionality to the containers, the library instead provides a set of algorithms, most of which are independent of any particular container type. These algorithms are "generic:" They operate on different types of containers and on elements of various types.
标准库容器定义的操作非常少。标准库没有给容器添加大量的功能函数,而是选择提供一组算法,这些算法大都不依赖特定的容器类型,是“泛型”的,可作用在不同类型的容器和不同类型的元素上。
The standard containers define few operations. For the most part they allow us to add and remove elements; to access the first or last element; to obtain and in some cases reset the size of the container; and to obtain iterators to the first and one past the last element.
标准容器(the standard container)定义了很少的操作。大部分容器都支持添加和删除元素;访问第一个和最后一个元素;获取容器的大小,并在某些情况下重设容器的大小;以及获取指向第一个元素和最后一个元素的下一位位置的迭代器。
We can imagine many other useful operations one might want to do on the elements of a container. We might want to sort a sequential container, or find a particular element, or find the largest or smallest element, and so on. Rather than define each of these operations as members of each container type, the standard library defines a set of generic algorithms: "algorithms" because they implement common operations; and "generic" because they operate across multiple container typesnot only library types such as vector or list, but also the built-in array type, and, as we shall see, over other kinds of sequences as well. The algorithms also can be used on container types we might define ourselves, as long as our types conform to the standard library conventions.
可以想像,用户可能还希望对容器元素进行更多其他有用的操作:也许需要给顺序容器排序,或者查找某个特定的元素,或者查找最大或最小的元素,等等。标准库并没有为每种容器类型都定义实现这些操作的成员函数,而是定义了一组泛型算法:因为它们实现共同的操作,所以称之为“算法”;而“泛型”指的是它们可以操作在多种容器类型上——不但可作用于 vector 或 list 这些标准库类型,还可用在内置数组类型、甚至其他类型的序列上。自定义的容器类型只要与标准库兼容,同样可以使用这些泛型算法。
Most of the algorithms operate by traversing a range of elements bounded by two iterators. Typically, as the algorithm traverses the elements in the range, it operates on each element. The algorithms obtain access to the elements through the iterators that denote the range of elements to traverse.
大多数算法是通过遍历由两个迭代器标记的一段元素来实现其功能。典型情况下,算法在遍历一段元素范围时,操纵其中的每一个元素。算法通过迭代器访问元素,这些迭代器标记了要遍历的元素范围。
Key Concept: Algorithms Never Execute Container Operations
关键概念:算法永不执行容器提供的操作
The generic algorithms do not themselves execute container operations. They operate solely in terms of iterators and iterator operations. The fact that the algorithms operate in terms of iterators and not container operations has a perhaps surprising but essential implication: When used on "ordinary" iterators, algorithms never change the size of the underlying container. As we'll see, algorithms may change the values of the elements stored in the container, and they may move elements around within the container. They do not, however, ever add or remove elements directly.
泛型算法本身从不执行容器操作,只是单独依赖迭代器和迭代器操作实现。算法基于迭代器及其操作实现,而并非基于容器操作。这个事实也许比较意外,但本质上暗示了:使用“普通”的迭代器时,算法从不修改基础容器的大小。正如我们所看到的,算法也许会改变存储在容器中的元素的值,也许会在容器内移动元素,但是,算法从不直接添加或删除元素。
Although the map and set types provide bidirectional iterators, we can use only a subset of the algorithms on associative containers. The problem is that the key in an associative container is const. Hence, any algorithm that writes to elements in the sequence cannot be used on an associative container. We may use iterators bound to associative containers only to supply arguments that will be read.
尽管 map 和 set 类型提供双向迭代器,但关联容器只能使用算法的一个子集。问题在于:关联容器的键是 const 对象。因此,关联容器不能使用任何写序列元素的算法。只能使用与关联容器绑在一起的迭代器来提供用于读操作的实参。
Most fundamentally, a class defines a new type and a new scope.
最简单地说,类就是定义了一个新的类型和一个新作用域。
Data abstraction is a programming (and design) technique that relies on the separation of interface and implementation. The class designer must worry about how a class is implemented, but programmers that use the class need not know about these details. Instead, programmers who use a type need to know only the type's interface; they can think abstractly about what the type does rather than concretely about how the type works.
数据抽象是一种依赖于接口和实现分离的编程(和设计)技术。类设计者必须关心类是如何实现的,但使用该类的程序员不必了解这些细节。相反,使用一个类型的程序员仅需了解类型的接口,他们可以抽象地考虑该类型做什么,而不必具体地考虑该类型如何工作。
We must use an initializer for any const or reference member or for any member of a class type that does not have a default constructor.
必须对任何 const 或引用类型成员以及没有默认构造函数的类类型的任何成员使用初始化式。
abstract data type
抽象数据类型
A data structure that uses encapsulation to hide its implementation, allowing programmers using the type to think abstractly about what the type does rather than concretely about how the type is represented. Classes in C++ can be used to define abstract data types.
使用封装来隐藏其实现的数据结构,允许使用类型的程序员抽象地考虑该类型做什么,而不是具体地考虑类型如何表示。C++ 中的类可用来定义抽象数据类型。
conversion constructor
转换构造函数
A nonexplicit constructor that can be called with a single argument. A conversion constructor is used implicitly to convert from the argument's type to the class type.
可用单个实参调用的非 explicit 构造函数。隐式使用转换构造函数将实参的类型转换为类类型。
The copy and direct forms of initialization, when applied to objects of class type, are subtly different. Direct-initialization directly invokes the constructor matched by the arguments. Copy-initialization always involves the copy constructor. Copy-initialization first uses the indicated constructor to create a temporary object. It then uses the copy constructor to copy that temporary into the one we are creating.
当用于类类型对象时,初始化的复制形式和直接形式有所不同:直接初始化直接调用与实参匹配的构造函数,复制初始化总是调用复制构造函数。复制初始化首先使用指定构造函数创建一个临时对象,然后用复制构造函数将那个临时对象复制到正在创建的对象。
As we know, when a parameter is a nonreference type, the argument is copied. Similarly, a nonreference return value is returned by copying the value in the return statement.
正如我们所知,当形参为非引用类型的时候,将复制实参的值。类似地,以非引用类型作返回值时,将返回 return 语句中的值的副本。
When the parameter or return type is a class type, the copy is done by the copy constructor. For example.
当形参或返回值为类类型时,由复制构造函数进行复制。
If we do not otherwise define the copy constructor, the compiler synthesizes one for us. Unlike the synthesized default constructor, a copy constructor is synthesized even if we define other constructors. The behavior of the synthesized copy constructor is to memberwise initialize the new object as a copy of the original object.
如果我们没有定义复制构造函数,编译器就会为我们合成一个。与合成的默认构造函数不同,即使我们定义了其他构造函数,也会合成复制构造函数。合成复制构造函数的行为是,执行逐个成员初始化,将新对象初始化为原对象的副本。
However, some classes must take control of what happens when objects are copied. Such classes often have a data member that is a pointer or that represents another resource that is allocated in the constructor. Other classes have bookkeeping that must be done whenever a new object is created. In both these cases, the copy constructor must be defined.
然而,有些类必须对复制对象时发生的事情加以控制。这样的类经常有一个数据成员是指针,或者有成员表示在构造函数中分配的其他资源。而另一些类在创建新对象时必须做一些特定工作。这两种情况下,都必须定义复制构造函数。
Overloaded operators are functions that have the name operator followed by the symbol for the operator being defined. Hence, we define assignment by defining a function named operator=. Like any other function, an operator function has a return type and a parameter list. The parameter list must have the same number of parameters (including the implicit this parameter if the operator is a member) as the operator has operands. Assignment is binary, so the operator function has two parameters: The first parameter corresponds to the left-hand operand, and the second to the right-hand operand.
重载操作符是一些函数,其名字为 operator 后跟着所定义的操作符的符号。因此,通过定义名为 operator= 的函数,我们可以对赋值进行定义。像任何其他函数一样,操作符函数有一个返回值和一个形参表。形参表必须具有与该操作符数目相同的形参(如果操作符是一个类成员,则包括隐式 this 形参)。赋值是二元运算,所以该操作符函数有两个形参:第一个形参对应着左操作数,第二个形参对应右操作数。
Most operators may be defined as member or nonmember functions. When an operator is a member function, its first operand is implicitly bound to the this pointer. Some operators, assignment among them, must be members of the class for which the operator is defined. Because assignment must be a member of its class, this is bound to a pointer to the left-hand operand. The assignment operator, therefore, takes a single parameter that is an object of the same class type. Usually, the right-hand operand is passed as a const reference.
大多数操作符可以定义为成员函数或非成员函数。当操作符为成员函数时,它的第一个操作数隐式绑定到 this 指针。有些操作符(包括赋值操作符)必须是定义自己的类的成员。因为赋值必须是类的成员,所以 this 绑定到指向左操作数的指针。因此,赋值操作符接受单个形参,且该形参是同一类类型的对象。右操作数一般作为 const 引用传递。
The synthesized assignment operator assigns each member in turn, using the built-in or class-defined assignment operator as appropriate to the type of the member. The operator returns *this, which is a reference to the left-hand object.
合成赋值操作符根据成员类型使用适合的内置或类定义的赋值操作符,依次给每个成员赋值,该操作符返回 *this,它是对左操作数对象的引用。
A useful rule of thumb is that if a class needs a destructor, it will also need the assignment operator and a copy constructor. This rule is often referred to as the Rule of Three, indicating that if you need a destructor, then you need all three copy-control members.
如果类需要析构函数,则它也需要赋值操作符和复制构造函数,这是一个有用的经验法则。这个规则常称为三法则,指的是如果需要析构函数,则需要所有这三个复制控制成员。
Classes that contain pointers require careful attention to copy control. The reason they must do so is that copying a pointer copies only the address in the pointer. Copying a pointer does not copy the object to which the pointer points.
包含指针的类需要特别注意复制控制,原因是复制指针时只复制指针中的地址,而不会复制指针指向的对象。
When designing a class with a pointer member, the first decision a class author must make is what behavior that pointer should provide. When we copy one pointer to another, the two pointers point to the same object. When two pointers point to the same object, it is possible to use either pointer to change the underlying object. Similarly, it is possible for one pointer to delete the object even though the user of the other pointer still thinks the underlying object exists.
设计具有指针成员的类时,类设计者必须首先需要决定的是该指针应提供什么行为。将一个指针复制到另一个指针时,两个指针指向同一对象。当两个指针指向同一对象时,可能使用任一指针改变基础对象。类似地,很可能一个指针删除了一对象时,另一指针的用户还认为基础对象仍然存在。
To manage a class with pointer members, we must define all three copy-control members: the copy constructor, assignment operator, and the destructor. These members can define either pointerlike or valuelike behavior for the pointer member.
为了管理具有指针成员的类,必须定义三个复制控制成员:复制构造函数、赋值操作符和析构函数。这些成员可以定义指针成员的指针型行为或值型行为。
Valuelike classes give each object its own copy of the underlying values pointed to by pointer members. The copy constructor allocates a new element and copies the value from the object it is copying. The assignment operator destroys the existing object it holds and copies the value from its right-hand operand into its left-hand operand. The destructor destroys the object.
值型类将指针成员所指基础值的副本给每个对象。复制构造函数分配新元素并从被复制对象处复制值,赋值操作符撤销所保存的原对象并从右操作数向左操作数复制值,析构函数撤销对象。
smart pointer
智能指针
A class that behaves like a pointer but provides other functionality as well. One common form of smart pointer takes a pointer to a dynamically allocated object and assumes responsibility for deleting that object. The user allocates the object, but the smart pointer class deletes it. Smart pointer classes require that the class implement the copy-control members to manage a pointer to the shared object. That object is deleted only when the last smart pointer pointing to it is destroyed. Use counting is the most popular way to implement smart pointer classes.
一个行为类似指针但也提供其他功能的类。智能指针的一个通用形式接受指向动态分配对象的指针并负责删除该对象。用户分配对象,但由智能指针类删除它。智能指针类需要实现复制控制成员来管理指向共享对象的指针。只有在撤销了指向共享对象的最后一个智能指针后,才能删除该共享对象。使用计数是实现智能指针类最常用的方式。
Overloaded functions that are members of a class may appear to have one less parameter than the number of operands. Operators that are member functions have an implicit this parameter that is bound to the first operand.
作为类成员的重载函数,其形参看起来比操作数数目少 1。作为成员函数的操作符有一个隐含的 this 形参,限定为第一个操作数。
An overloaded unary operator has no (explicit) parameter if it is a member function and one parameter if it is a nonmember function. Similarly, an overloaded binary operator would have one parameter when defined as a member and two parameters when defined as a nonmember function.
重载一元操作符如果作为成员函数就没有(显式)形参,如果作为非成员函数就有一个形参。类似地,重载二元操作符定义为成员时有一个形参,定义为非成员函数时有两个形参。
When we define an input or output operator that conforms to the conventions of the iostream library, we must make it a nonmember operator. Why
当定义符合标准库 iostream 规范的输入或输出操作符的时候,必须使它成为非成员操作符,为什么需要这样做呢?
We cannot make the operator a member of our own class. If we did, then the left-hand operand would have to be an object of our class type:
我们不能将该操作符定义为类的成员,否则,左操作数将只能是该类类型的对象:
// if operator<< is a member of Sales_item
// if operator<< is a member of Sales_item
Sales_item item;
Sales_item item;
item << cout;
item << cout;
This usage is the opposite of the normal way we use output operators defined for other types.
这个用法与为其他类型定义的输出操作符的正常使用方式相反。
Most overloaded operators can be defined as class members or as ordinary non-member functions. The assignment, subscript, call, and arrow operators must be class members. When an operator is defined as a member, it is a normal member function. In particular, member operators have an implicit this pointer, which is bound to the first (only operand for unary operators, left-hand operand for binary operators) operand.
大多数重载操作符可以定义为类成员或普通非成员函数,赋值操作符、下标操作符、调用操作符和箭头操作符必须为类成员。操作符定义为成员时,它是普通成员函数。具体而言,成员操作符有一个隐式 this 指针,该指针一定是第一个操作数,即,一元操作符唯一的操作数,二元操作符的左操作数。
Classes can define conversions that will be applied automatically when an object of one type is used where an object of a different type is needed. Constructors that take a single parameter and are not designated as explicit define conversions from the class type to other types. Overloaded operator conversion functions define conversions from other types to the class type. Conversion operators must be members of the class that they convert. They have no parameters and define no return value. Conversion operators return a value of the type of the operatorfor example, operator int returns an int.
类可以定义转换,当一个类型的对象用在需要另一不同类型对象的地方时,自动应用这些转换。接受单个形参且未指定为 explicit的构造函数定义了从其他类型到类类型的转换,重载操作符转换函数则定义了从类类型到其他类型的转换。转换操作符必须为所转换类的成员,没有形参并且不定义返回值,转换操作符返回操作符所具有类型的值,例如,operator int 返回 int。
Objects of classes that overload operator(), the function call operator, are known as "function objects." Such objects are often used to define predicate functions to be used in combination with the standard algorithms.
重载了 operator()(即,函数调用操作符)的类的对象,称为“函数对象”。这种对象通常用于定义与标准算法结合使用的谓词函数。
class-type conversion
类类型转换
Conversions to or from class types. Non-explicit constructors that take a single parameter define a conversion from the parameter type to the class type. Conversion operators define conversions from the class type to the type specified by the operator.
到类类型或从类类型的转换。接受一个形参的非显式构造函数定义从形参类型到类类型的转换。转换操作符定义从类类型到操作符所指定类型的转换。
A derived-class synthesized default constructor differs from a nonderived constructor in only one way: In addition to initializing the data members of the derived class, it also initializes the base part of its object. The base part is initialized by the default constructor of the base class.
派生类的合成默认构造函数与非派生的构造函数只有一点不同:除了初始化派生类的数据成员之外,它还初始化派生类对象的基类部分。基类部分由基类的默认构造函数初始化。
……
派生类构造函数的初始化列表。
class Bulk_item : public Item_base {
class Bulk_item : public Item_base {
public:
public:
Bulk_item(const std::string& book, double sales_price,
Bulk_item(const std::string& book, double sales_price,
std::size_t qty = 0, double disc_rate = 0.0):
std::size_t qty = 0, double disc_rate = 0.0):
Item_base(book, sales_price),
Item_base(book, sales_price),
min_qty(qty), discount(disc_rate) { }
min_qty(qty), discount(disc_rate) { }
};
};
Base classes usually should define a virtual destructor even if the class otherwise has no need for a destructor. The destructor must be virtual if a pointer to a base is ever deleted when it actually addresses a derived-type object.
即使另外不需要析构函数,基类通常也应定义一个虚析构函数。如果经常会在指向基类的指针实际指向派生类对象时删除它,析构函数就必须为虚函数。
When we define a member template outside the scope of a class template, we must include both template parameter lists:
……
template template
template template
void Queue::assign(Iter beg, Iter end)
void Queue::assign(Iter beg, Iter end)
{
{
destroy(); // remove existing elements in this Queue
destroy(); // remove existing elements in this Queue
copy_elems(beg, end); // copy elements from the input range
copy_elems(beg, end); // copy elements from the input range
}
}
Large-scale programming places greater demands on programming languages than do the needs of systems that can be developed by small teams of programmers. Among the needs that distinguish large-scale applications are:
相对于小的程序员团队所能开发的系统需求而言,大规模编程对程序设计语言的要求更高。大规模应用程序往往具有下列特殊要求:
Stricter up-time requirements and the need for more robust error detection and error handling. Error handling often must span independently developed subsystems.
更严格的正常运转时间以及更健壮的错误检测和错误处理。错误处理经常必须跨越独立开发的多个子系统进行。
The ability to structure programs that are composed of libraries developed more or less independently.
能够用各种库(可能包含独立开发的库)构造程序。
The need to deal with more complicated application concepts.
能够处理更复杂的应用概念。
Three features in C++ are aimed at these needs: exception handling, namespaces, and multiple inheritance. This chapter looks at these three facilities.
C++ 中有下列三个特征分别针对这些要求:异常处理、命名空间和多重继承。
Namespaces are a mechanism for managing large complex applications built from code produced by independent suppliers. A namespace is a scope in which objects, types, functions, template, and other namespaces may be defined. The standard library is defined inside the namespace named std.
命名空间是一种机制,用于管理用独立供应商开发的代码建立的大型复杂应用程序。一个命名空间就是一个作用域,其中可以定义对象、类型、函数、模板和其他命名空间。标准库就定义在名为 std 的命名空间中。
Unions are a special kind of class that may contain only simple data members. An object of a union type may define a value for only one of its data members at any one time. Unions are most often nested inside another class type.
联合,是只能包含简单数据成员的一种特殊类。union 类型的对象在任意时刻只能为它的一个数据成员定义值。联合经常嵌套在其他类类型内部。