C ++ proverbs: Use const whenever possible

Source: Internet
Author: User

One wonderful thing about const is that it allows you to specify a semantic constraint: a specific object should not be modified. The compiler will execute this constraint. It allows you to notify the compiler and other programmers that a value should remain unchanged. If this is the case, you should make it clear, because in this way, you can seek help from the compiler to ensure that this value will not be changed.

The keyword const is very versatile. Outside the class, you can use it for global constants or namespace constants, just like objects declared as static within the scope of files, functions, or modules. Inside the class, you can use it for static and non-static data members. For pointers, you can specify that the pointer itself is a const, or that the data it points to is a const, or that both are, or both are not.

char greeting[] = "Hello";

char *p = greeting; // non-const pointer,
// non-const data

const char *p = greeting; // non-const pointer,
// const data

char * const p = greeting; // const pointer,
// non-const data

const char * const p = greeting; // const pointer,
// const data

The syntax itself is not as capricious as it is on the surface. If const appears on the left side of *, the pointer points to a constant. If const appears on the right side of *, the pointer itself is a constant. If const appears on both sides of *, both are constants.

When the pointer points to a constant, some people put the const before the type, while others put the const before the type. The two functions have no difference in meaning. Therefore, the following two functions have the same parameter type:

void f1(const Widget *pw); // f1 takes a pointer to a
// constant Widget object

void f2(Widget const *pw); // so does f2

Because they all exist in actual code, you should get used to these two forms.

STL iterators uses pointers as the prototype, so an iterator is very similar to a T * pointer in behavior. Declaring an iterator as const is similar to declaring a pointer As const (that is, declaring a T * const pointer): You cannot point iterator to another thing, but what it points to can be changed. If you want an iterator to point to something that cannot be changed (that is, the STL equivalent of const T *), you should use const_iterator:

std::vector<int> vec;
...
const std::vector<int>::iterator iter = // iter acts like a T* const

vec.begin();

*iter = 10; // OK, changes what iter points to

++iter; // error! iter is const

std::vector<int>::const_iterator cIter = //cIter acts like a const T*

vec.begin();

*cIter = 10; // error! *cIter is const

++cIter; // fine, changes cIter

The most powerful usage of const comes from its application in function declaration. In a function declaration, const can be used in both function return values and individual parameters. For a member function, it can also be used in the entire function.

A function returns a constant, which can minimize the impact of customer errors without giving up security and efficiency. For example, consider the declaration of the rational member operator * in Item 24:

class Rational { ... };

const Rational operator*(const Rational& lhs, const Rational& rhs);

For the first time, many people will disagree. Why should the operator * result be a const object? Because if it is not, the customer can commit such violence:

Rational a, b, c;
...
(a * b) = c; // invoke operator= on the
// result of a*b!

I don't know why some programmers assign values to the product of two numbers, but I know many programmers are not incompetent in doing so. All of these may come from a simple input error (requiring this type to be implicitly transformed to bool ):

if (a * b = c) ... // oops, meant to do a comparison!
If a and B are built-in types, such code is obviously invalid. A good user-defined type is characterized by avoiding any inconsistency with the built-in type for no reason, and it seems no reason for me to allow the value assignment to the product of two numbers. This can be avoided by declaring the return value of operator * as const, which is the reason for doing so.

There is nothing special about the const parameters-they act like local const objects, and you should use them whenever you can. Unless you need to change the capabilities of a parameter or local object, make sure to declare it as const. It only requires you to enter six characters to save the annoying error you just saw: "I want to type '= ', but I accidentally typed '= '".

Const member functions

The purpose of a member function declared as const is to ensure that the function may be called by the const object. For two reasons, such a member function is very important. First, it makes the interface of a class easier to understand. It is important to know which function can change the object and which function cannot. Second, they can work with const objects. Writing efficient code has a very important aspect, as explained in Item 20, the basic way to improve the performance of a C ++ program is to pass an object reference to a const parameter. This technique is only available when the const candidate object has a const member function.

Many people have not noticed the fact that member functions can be overloaded only when their constants are different. This is an important feature of C ++. Consider a class that represents text blocks:

class TextBlock {

  public:
   ...
   const char& operator[](std::size_t position) const // operator[] for
   { return text[position]; } // const objects
   char& operator[](std::size_t position) // operator[] for
   { return text[position]; } // non-const objects
  private:
   std::string text;
};

The operator [] s of TextBlock may be used as follows:

TextBlock tb("Hello");

std::cout << tb[0]; // calls non-const
// TextBlock::operator[]

const TextBlock ctb("World");

std::cout << ctb[0]; // calls const TextBlock::operator[]

By the way, the const object is most often used in actual programs as a result of such an operation: Passing pointers or references to the const parameter. The ctb example above is artificial. The following example is more realistic:

void print(const TextBlock& ctb) // in this function, ctb is const
{
  std::cout << ctb[0]; // calls const TextBlock::operator[]
  ...
}

By reloading operator [] and returning different types to different versions, you can perform different operations on TextBlocks of const and non-const:

std::cout << tb[0]; // fine - reading a
// non-const TextBlock

tb[0] = ’x’; // fine - writing a
// non-const TextBlock

std::cout << ctb[0]; // fine - reading a
// const TextBlock

ctb[0] = ’x’; // error! - writing a
// const TextBlock

Note that this error occurs only when operator [] is called, and operator [] is always correct. The error occurs when an attempt is made to assign a value to const char, which is the return type of const operator.

Note that operator [] of the non-const version returns a single-character reference rather than the character itself. If operator [] returns only one character, the following statement cannot be compiled:

tb[0] = ’x’;
Because changing the return value of a built-in function is always invalid. If it is valid, the fact that C ++ returns an object with a value (by value) (see Item 20) means tb. the copy of text [0] is changed, not tb. text [0], which is not what you want.

Let's leave a little time for philosophy. What does a member function mean by const? There are two main concepts: bitwise constness (also known as physical constness) and logical constness ).

The const faction of binary bits insists that a member function, when and only when it cannot change any data member of the object (except for static members), cannot change any binary bits in the object, the member function is const. One advantage of binary constants is that it is easier to monitor violations: the compiler only needs to find values for data members. In fact, binary constants are the definition of constants in C ++. A const member function is not allowed to change any non-static data member of the object that calls it.
Unfortunately, many member functions cannot pass the binary constants test completely. In particular, a member function that often changes the content pointed to by a pointer. Unless this pointer is in this object, this function is a binary const, And the compiler will not raise any objection. For example, suppose we have a class similar to TextBlock, because it needs to deal with a c api that does not know why string, so it needs to store its data as char * rather than string.

class CTextBlock {
  public:
   ...
   char& operator[](std::size_t position) const // inappropriate (but bitwise

   { return pText[position]; } // const) declaration of
   // operator[]
  private:
   char *pText;
};

Although operator [] returns a reference to the internal data of an object, this class (not suitable) declares it as a const member function (Item 28 will talk about an in-depth topic ). Put it aside first and look at the implementation of operator []. It has not changed by any means.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.