C++primer——const总结

2.4. const 限定符

There are two problems with the following for loop, both concerning the use of 512 as an upper bound.

下列 for 循环语句有两个问题,两个都和使用 512 作为循环上界有关。

      for (int index = 0; index != 512; ++index) {
          // ...
      }

 

The first problem is readability. What does it mean to compare index with 512? What is the loop doingthat is, what makes 512 matter? (In this example, 512 is known as amagic number, one whose significance is not evident within the context of its use. It is as if the number had been plucked by magic from thin air.)

第一个问题是程序的可读性。比较 index 与 512 有什么意思呢?循环在做什么呢?也就是说 512 作用何在?[本例中,512 被称为魔数(magic number,它的意义在上下文中没有体现出来。好像这个数是魔术般地从空中出现的。

The second problem is maintainability. Imagine that we have a large program in which the number 512 occurs 100 times. Let's further assume that 80 of these references use 512 to indicate the size of a particular buffer but the other 20 use 512 for different purposes. Now we discover that we need to increase the buffer size to 1024. To make this change, we must examine every one of the places that the number 512 appears. We must determinecorrectly in every casewhich of those uses of 512 refer to the buffer size and which do not. Getting even one instance wrong breaks the program and requires us to go back and reexamine each use.

第二个问题是程序的可维护性。假设这个程序非常庞大,512 出现了 100 次。进一步假设在这 100 次中,有 80 次是表示某一特殊缓冲区的大小,剩余 20 次用于其他目的。现在我们需要把缓冲区的大小增大到 1024。要实现这一改变,必须检查每个 512 出现的位置。我们必须确定(在每种情况下都准确地确定)哪些 512 表示缓冲区大小,而哪些不是。改错一个都会使程序崩溃,又得回过头来重新检查。

The solution to both problems is to use an object initialized to 512:

解决这两个问题的方法是使用一个初始化为 512 的对象:

      int bufSize = 512;    // input buffer size
      for (int index = 0; index != bufSize; ++index) {
          // ...
      }

 

By choosing a mnemonic name, such asbufSize, we make the program more readable. The test is now against the object rather than the literal constant:

通过使用好记的名字如 bufSize,增强了程序的可读性。现在是对对象 bufSize 测试而不是字面值常量 512 测试:

      index != bufSize

 

If we need to change this size, the 80 occurrences no longer need to be found and corrected. Rather, only the one line that initializesbufSize requires change. Not only does this approach require significantly less work, but also the likelihood of making a mistake is greatly reduced.

现在如果想要改变缓冲区大小,就不再需要查找和改正 80 次出现的地方。而只有初始化bufSize 那行需要修改。这种方法不但明显减少了工作量,而且还大大减少了出错的可能性。

定义 const 对象

There is still a serious problem with defining a variable to represent a constant value. The problem is thatbufSize is modifiable. It is possible forbufSize to be changedaccidentally or otherwise. Theconst type qualifier provides a solution: It transforms an object into a constant.

定义一个变量代表某一常数的方法仍然有一个严重的问题。即 bufSize 是可以被修改的。bufSize 可能被有意或无意地修改。const 限定符提供了一个解决办法,它把一个对象转换成一个常量。

      const int bufSize = 512;     // input buffer size

 

defines bufSize to be a constant initialized with the value 512. The variablebufSize is still an lvalue (Section 2.3.1, p.45), but now the lvalue is unmodifiable. Any attempt to write tobufSize results in a compile-time error.

定义 bufSize 为常量并初始化为 512。变量 bufSize 仍然是一个左值(第 2.3.1 节),但是现在这个左值是不可修改的。任何修改bufSize 的尝试都会导致编译错误:

      bufSize = 0; // error: attempt to write to const object

 

Because we cannot subsequently change the value of an object declared to be const, we must initialize it when it is defined:

因为常量在定义后就不能被修改,所以定义时必须初始化:

 

      const std::string hi = "hello!"; // ok: initialized
      const int i, j = 0;  // error: i is uninitialized const

 

 

const 对象默认为文件的局部变量

When we define a nonconst variable at global scope (Section 2.3.6, p.54), it is accessible throughout the program. We can define a nonconst variable in one file andassuming an appropriate declaration has been madecan use that variable in another file:

在全局作用域(第 2.3.6 节)里定义非 const 变量时,它在整个程序中都可以访问。我们可以把一个非const 变更定义在一个文件中,假设已经做了合适的声明,就可在另外的文件中使用这个变量:

      // file_1.cc
      int counter;  // definition
      // file_2.cc
      extern int counter; // uses counter from file_1
      ++counter;          // increments counter defined in file_1

 

Unlike other variables, unless otherwise specified, const variables declared at global scope are local to the file in which the object is defined. The variable exists in that file only and cannot be accessed by other files.

与其他变量不同,除非特别说明,在全局作用域声明的 const 变量是定义该对象的文件的局部变量。此变量只存在于那个文件中,不能被其他文件访问。

We can make a const object accessible throughout the program by specifying that it isextern:

通过指定 const 变更为 extern,就可以在整个程序中访问 const 对象:

      // file_1.cc
      // defines and initializes a const that is accessible to other files
      extern const int bufSize = fcn();
      // file_2.cc
      extern const int bufSize; // uses bufSize from file_1
      // uses bufSize defined in file_1
      for (int index = 0; index != bufSize; ++index)
            // ...

 

In this program,file_1.cc defines and initializesbufSize to the result returned from calling the function namedfcn. The definition ofbufSize isextern, meaning thatbufSize can be used in other files. The declaration infile_2.cc is also madeextern. In this case, theextern signifies thatbufSize is a declaration and hence no initializer is provided.

本程序中,file_1.cc 通过函数 fcn 的返回值来定义和初始化 bufSize。而 bufSize 定义为extern,也就意味着bufSize 可以在其他的文件中使用。file_2.ccextern 的声明同样是extern;这种情况下,extern 标志着bufSize 是一个声明,所以没有初始化式。

We'll see in Section 2.9.1 (p. 69) why const objects are made local to a file.

我们将会在第 2.9.1 节看到为何const 对象局部于文件创建。

Nonconst variables are extern by default. To make a const variable accessible to other files we must explicitly specify that it isextern.

const 变量默认为 extern。要使 const 变量能够在其他的文件中访问,必须地指定它为extern

 

 

7.2 const 形参7.2const Parameters

We can call a function that takes a nonreference, nonconst parameter passing either a const or nonconst argument. Forexample, we could pass two const ints to our gcd function:

在调用函数时,如果该函数使用非引用的非const 形参,则既可给该函数传递 const 实参也可传递非 const 的实参。例如,可以传递两个int 型 const 对象调用 gcd:

     const int i =3, j = 6;

     int k =rgcd(3, 6);   // ok: k initialized to 3

 

This behavior follows from the normal initialization rules for const objects (Section2.4, p.56).Because the initialization copies the value of the initializer, we caninitialize a nonconst object from a const object, or viceversa.

这种行为源于 const 对象的标准初始化规则(第 2.4 节)。因为初始化复制了初始化式的值,所以可用 const 对象初始化非 const 对象,反之亦然。

If we makethe parameter a const nonreference type:

如果将形参定义为非引用的 const 类型:

     void fcn(constint i) { /* fcn can read but not write to i */ }

 

then the function cannot change its local copy of the argument. Theargument is still passed as a copy so we can pass fcn either a const or nonconst object.

则在函数中,不可以改变实参的局部副本。由于实参仍然是以副本的形式传递,因此传递给fcn 的既可以是 const 对象也可以是非 const 对象。

What may be surprising, is that although the parameter is a const inside the function, the compiler otherwise treats the definition of fcn as if we had defined the parameter as a plain int:

令人吃惊的是,尽管函数的形参是 const,但是编译器却将 fcn 的定义视为其形码被声明为普通的 int 型:

     void fcn(constint i) { /* fcn can read but not write to i */ }

     void fcn(inti) { /* ... */ }            // error:redefines fcn(int)

 

This usage exists to support compatibility with the C language, whichmakes no distinction between functions taking const or nonconst parameters.

这种用法是为了支持对 C 语言的兼容,因为在 C 语言中,具有 const 形参或非 const 形参的函数并无区别。

Using (const) References to Avoid Copies

利用 const 引用避免复制

The other circumstance in whichreference parameters are useful is when passing a large object to a function.Although copying an argument is okay for objects of built-in data types and forobjects of class types that are small in size, it is (often) too inefficientfor objects of most class types or large arrays. Moreover, as we'll learn in Chapter13, some class types cannot be copied. By using a reference parameter, thefunction can access the object directly without copying it.

在向函数传递大型对象时,需要使用引用形参,这是引用形参适用的另一种情况。虽然复制实参对于内置数据类型的对象或者规模较小的类类型对象来说没有什么问题,但是对于大部分的类类型或者大型数组,它的效率(通常)太低了;此外,我们将在第十三章学习到,某些类类型是无法复制的。使用引用形参,函数可以直接访问实参对象,而无须复制它。

As an example, we'll write a function that compares the length of two strings. Such a function needs to access the size of each string but has no need to write to the strings. Because strings can be long, we'd like to avoid copying them. Using const references we can avoid the copy:

编写一个比较两个 string 对象长度的函数作为例子。这个函数需要访问每个 string 对象的 size,但不必修改这些对象。由于 string 对象可能相当长,所以我们希望避免复制操作。使用 const 引用就可避免复制:

     // compare thelength of two strings

     boolisShorter(const string &s1, const string &s2)

     {

         returns1.size() < s2.size();

     }

 

Each parameter is a reference to const string. Becausethe parameters are references the arguments are not copied. Because theparameters are const references, isShorter may not use thereferences to change the arguments.

其每一个形参都是 const string 类型的引用。因为形参是引用,所以不复制实参。又因为形参是 const 引用,所以 isShorter 函数不能使用该引用来修改实参。

When the only reason to make a parameter a reference is to avoid copying the argument, the parameter should be const reference.

如果使用引用形参的唯一目的是避免复制实参,则应将形参定义为 const 引用。

 

 

References to const Are More Flexible

更灵活的指向 const 的引用

It should be obvious that a function that takes a plain, nonconst reference may not be called on behalf of a const object. Afterall, the function might change the object it is passed and thus violate the constness of the argument.

如果函数具有普通的非 const 引用形参,则显然不能通过 const 对象进行调用。毕竟,此时函数可以修改传递进来的对象,这样就违背了实参的 const 特性。

What may be less obvisous is that we also cannot call such a function withan rvalue (Section2.3.1, p.45)or with an object of a type that requires a conversion:

但比较容易忽略的是,调用这样的函数时,传递一个右值(第 2.3.1 节)或具有需要转换的类型的对象同样是不允许的:

     // functiontakes a non-const reference parameter

     int incr(int&val)

     {

         return++val;

     }

     int main()

     {

         short v1 =0;

         const intv2 = 42;

         int v3 =incr(v1);   // error: v1 is not an int

         v3 =incr(v2);       // error: v2 is const

         v3 =incr(0);        // error: literals arenot lvalues

         v3 =incr(v1 + v2);  // error: additiondoesn't yield an lvalue

         int v4 =incr(v3);   // ok: v3 is a non constobject type int

     }

 

The problem is that a nonconst reference (Section2.5, p.59)may be bound only to nonconst object of exactly the same type.

问题的关键是非 const 引用形参(第 2.5 节)只能与完全同类型的非 const 对象关联。

Parameters that do not change the value of the corresponding argumentshould be const references. Defining such parameters as nonconst references needlessly restricts the usefulness of a function. As anexample, we might write a program to find a given character in a string:

应该将不修改相应实参的形参定义为 const 引用。如果将这样的形参定义为非 const 引用,则毫无必要地限制了该函数的使用。例如,可编写下面的程序在一个 string 对象中查找一个指定的字符:

     // returnsindex of first occurrence of c in s or s.size() if c isn't in s

     // Note: sdoesn't change, so it should be a reference to const

    string::size_type find_char(string &s, char c)

     {

        string::size_type i = 0;

         while (i!= s.size() && s[i] != c)

            ++i;                   // notfound, look at next character

         return i;

     }

 

This function takes its string argument as a plain (nonconst) reference, even though it doesn't modify that parameter. One problem withthis definition is that we cannot call it on a character string literal:

这个函数将其 string 类型的实参当作普通(非 const)的引用,尽管函数并没有修改这个形参的值。这样的定义带来的问题是不能通过字符串字面值来调用这个函数:

     if(find_char("Hello World", 'o')) // ...

 

This call fails at compile time, even though we can convert the literal toa string.

虽然字符串字面值可以转换为 string 对象,但上述调用仍然会导致编译失败。

Such problems can be surprisingly pervasive. Even if our program has no const objects and we only call find_char on behalf of string objects (as opposed to on a string literal or an expression that yields astring), we can encounter compile-time problems. For example,we might have another function is_sentence that wants to use find_char to determine whether a string represents a sentence:

继续将这个问题延伸下去会发现,即使程序本身没有const 对象,而且只使用 string 对象(而并非字符串字面值或产生 string 对象的表达式)调用 find_char 函数,编译阶段的问题依然会出现。例如,可能有另一个函数 is_sentence 调用 find_char 来判断一个 string 对象是否是句子:

     boolis_sentence (const string &s)

     {

          // ifthere's a period and it's the last character in s

          // then sis a sentence

          return (find_char(s, '.') == s.size() -1);

     }

 

As written, the call to find_char from inside is_sentence is a compile-time error. The parameter to is_sentence is a reference to const string and cannotbe passed to find_char, which expects a reference to a nonconst string.

如上代码,函数 is_sentence 中 find_char 的调用是一个编译错误。传递进 is_sentence 的形参是指向 const string 对象的引用,不能将这种类型的参数传递给 find_char,因为后者期待得到一个指向非 const string 对象的引用。

Reference parameters that are not changed should be references to const. Plain, nonconst reference parameters are less flexible. Such parameters may not be initialized by const objects, or by arguments that are literals or expressions that yield rvalues.

应该将不需要修改的引用形参定义为 const 引用。普通的非 const 引用形参在使用时不太灵活。这样的形参既不能用 const 对象初始化,也不能用字面值或产生右值的表达式实参初始化。


7.7. Class Member Functions 7.7. 类的成员函数


const 成员函数的引入

We now can understand the role of the const that follows the parameter lists in the declarations of theSales_item member functions: Thatconst modifies the type of the implicitthis parameter. When we calltotal.same_isbn(trans), the implicitthis parameter will be a const Sales_Item* that points tototal. It is as if the body ofsame_isbn were written as

现在,可以理解跟在 Sales_item 成员函数声明的形参表后面的 const 所起的作用了:const 改变了隐含的this 形参的类型。在调用total.same_isbn(trans) 时,隐含的this 形参将是一个指向total 对象的 const Sales_Item* 类型的指针。就像如下编写 same_isbn 的函数体一样:

     // pseudo-code illustration of how the implicit this pointer is used
     // This code is illegal: We may not explicitly define the this pointer ourselves
     // Note that this is a pointer to const because same_isbn is a const member
     bool Sales_item::same_isbn(const Sales_item *const this,
                               const Sales_item &rhs) const
     { return (this->isbn == rhs.isbn); }

A function that uses const in this way is called a const member function. Becausethis is a pointer toconst, aconst member function cannot change the object on whose behalf the function is called. Thus,avg_price andsame_isbn may read but not write to the data members of the objects on which they are called.

用这种方式使用 const 的函数称为常量成员函数。由于this 是指向const 对象的指针,const 成员函数不能修改调用该函数的对象。因此,函数avg_price 和函数same_isbn 只能读取而不能修改调用它们的对象的数据成员。

A const object or a pointer or reference to a const object may be used to call onlyconst member functions. It is an error to try to call a nonconst member function on aconst object or through a pointer or reference to a const object.

const 对象、指向 const 对象的指针或引用只能用于调用其 const 成员函数,如果尝试用它们来调用非const 成员函数,则是错误的。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值