1.Because we can't change the value of a const obeject after we create it, it must be initialized.
int i = 42; const int ci = i; // ok:the value in i is copied int ci int j = ci; // ok: the value in ci is copied into j
2.When a const object is initialized from a compile-time constant, the compiler will usually replace uses of the variable with its corresponding value during compilation.
const int bufSize = 512; // the compiler will generate code using the value 512 in the places that our code uses bufSize.
3.To define a single instance of a const variable, we use the keyword extern on both its definition and declarations.
// file_1.cc defines and initializes a constthat is accesable to other files extern const in bufSize = fcn(); // file_1.h extern const in bufSize; // same bufSize as defined in file_1.cc
1.We use a reference to const, which is a reference that refers to a const type.
Term: const Reference is a Reference to const
C++ programmers tend to abbreviate the phrase "reference to cosnt" as "const reference".
Technically speaking, there are no const reference. A reference is not an object, so we cannot make a reference itself const.
const in ci = 1024; const int &r1 = ci; // ok: both refernce and underlying object are const. r1 = 42; // error: r1 is a reference to const int &r2 = ci; // error: nonconst refernce to a const object
int i = 42 int &r1 = i; // r1 bound to i const int &r2 = i; // r2 also bound to i; but cannot be used to change i r1 = 0; // r2 is not const; i is now 0 r2 = 0; // error: r2 is a reference to const
2.With one exception of two, we can initialize a reference to const from any expression that can be converted to the type of the reference.
int i = 42; const int &r1 = i; // we can bind a const int& to a plain int object const int &r2 = 42; // ok: r1 is a reference to const const int &r3 = r1 * 2; // ok: r3 is reference to const int &r4 = r * 2; // error: r4 is a plain, nonconst reference
double dval = 3.14; const int &ri = dval; // the compiler transforms this code into something like const int tem = dval; // create a temporary const int from the double const int &ri = temp; // bind ri to that temporary // If ri weren't const, we could assign to ri. // Doing so would change the object to which ri is bound. // That object is a temporary, not dval.
By default, a reference may be bound only to an object, not to a literal or to the result of a more general expressions.
The type of a reference and the object to which the reference refers must match exactly.
int &refVal4 = 10; // error: initializer must be an object double dval = 3.14; int &refVal5 = dval; // error: initializer must be an in object
Term: What is an Object?
In this book, we'll follow the more general usage that an object is a region of memory that has a type.
1.Like a reference to const, a pointer to const says nothing about whether the object to which the pointer points is const.
#include<iostream> using namespace std; int main() { int a = 4; const int& q1 = a; int& q2 = a; q1 = 10; // error: expression must be a modifiable lvalue. q2 = 10; const int* p1 = &a; int* p2 = &a; *p1 = 10; // error: expression must be a modifiable lvalue. *p2 = 10; cout << "q1 = " << q1 << endl; cout << "q2 = " << q2 << endl; cout << "p1 = " << *p1 << endl; cout << "p2 = " << *p2 << endl; // output: // q1 = 10 // q2 = 10 // p1 = 10 // p2 = 10 }
Pointers:
Unlike reference, a pointer is an object in its own right.
Pointers can be assigned and copied.
By default, the types of the pointer and the object to which it points must match.
double dval; double *pd = &dval; // ok: initializer is the address of a double double *pd2 = pd; // ok: initializer is a pointer to double int *pi = pd; // error: types of pi and pd differ pi = &dval; // error: assigning the address of a double to a pointer to int
2.Like any other const object, a const pointer must be initialized, and once initialized, its value( the address that it holds )may not be changed.
1.The distinction between top-level (the pointer itself is a const) and low-level (a pointer can poin to a const obj) matters when we copy an object.
int i = 0; int *const p1 = &i; // we can't change the value of p1; const is top-level const int ci = 42; // we can't change ci; const is top-level const int *p2 = &ci; // we can change p2; const is low-level const int *const p3 = p2; // right-most const is top-level, left-most is not const int &r = ci; // const in reference types is always low-level
i = ci; // ok: copying the value of ci; top-level const in ci is ignored p2 = p3; // ok: pointed-to type matches; top-level const in p3 is ignored
int *p = p3; // error: p3 has a low-level const but p doesn't int &r = ci; // error: can't bind an ordinary int& to a const int object p2 = p3; // ok: p2 has the same low-level cont qualification as p3 p2 = &i; // ok: we can convert int* to const int* const int &r2 = i; // ok: can bind const int& to plain int
1.A constant expression is an expression whose value cannot change and that can be evaluated at compile time.
const int limit = max_files + 1; // limit is a constant expression const int sz = get_size(); // sz is not a contant expression (initialized at run time)
int n = 1; std::array<int, n> a1; // error: n is not a constant expression const int cn = 2; std::array<int, cn> a2; // ok: cn is a constant expression
2.Literal types are the types of constexpr variables and they can be constructed, manipulated, and returned from constexpr functions. (by "Literal types" at cppreference.com)
3.Constexpr imposes a top-level const on the objects it defines.
const int *p = nullptr; // p is pointer to a const int constexpr int *q = nullptr; // q is a const pointer to int