[C++ Primer Reading Notes] Day 3

Scope

Chapter 2.3-2.4

Review

Chapter 2.3-2.4 mainly introduce compound types and const qualifier. The rules for using them seem very complicated. However, all the rules are reasonable, and we can remember them once we understand what they really are.

  • a reference is an alternative name for an object, which is not an object
  • a pointer is an object (variable) that hold another variable’s address
  • a reference to const is a reference that refers to a variable. We cannot change that variable through the reference to const. It is not a const.
  • a pointer to const is a pointer that points to another variable. We cannot change that variable through the pointer to const. It is not a const.
  • a const pointer is a pointer that points to another variable. Once it is initialized to the of that variable, the address this pointer holds can not be changed. It is a const.

Notes

1. the type of a compound type

A compound type is a type that is defined in terms of another type. C++ has several compound types, two of which are references and pointers.

For the declaration of compound types, the type modifier (* or &) is applied to the variable name, and the type of the variables are defined both the base type and the type modifier. For example,

int i = 1;
int *p = &i;  // the type of p is defined by both "*" abd int
int &r = i;  // the type of r is defined by both "&" abd int

Caution:
A type modifier can only be used to modify a variable.

int *p1, *p2;

2. about the reference

2.1 the inner character of a reference

A reference defines an alternative name for an object.
A reference is not an object.
Because references are not objects, we may not define a reference to a reference.
A reference may be bound only to an object, not to literal or to the result of a more general expression.

After a reference has been defined, all operators on that reference are actually operations on the object to which the reference is bound.

2.2 definition and initialization of a reference

When we define a reference, instead of copying the initializer’s value, we bind the reference to its initializer. Once initialized, a reference remains bound to its object.

There is no way to rebind a reference to refer to a different object. Because there is no way to rebind a reference, references must be initialized.

3. about the pointer

3.1 the inner character of a pointer

A pointer is an object in its own right.

Pointers can be assigned and copied; A single pointer can point to several different objects over its lifetime.

Because references are not objects, they don’t have addresses. Hence, we may not define a pointer to a reference.

3.2 operators

3.2.1 the address-of operator (& operator)

We get the address of an object by using the address-of operator (& operator).

3.2.2 the dereference operator (* operator)

When a pointer points to an object, we can use the dereference operator (* operator) to access that object.
We can assign to that object by assigning to the result of the dereference.
We may dereference only a valid pointer that points to an object.

3.2.3 how to distinguish an operator from a type modifier (* or &) ?

When a * or & is used in a declaration, it is a type modifier. When a * or & is used in an expression, it is an operator.

3.3 null pointers

There are three ways to obtain a null pointer:

int *p1 = nullptr;  // nullptr is a literal that has a special type that can be covered to any other pointer type.
int *p2 = 0;  
int *p3 = NULL;  //not recommended to use in C++. it must #include cstdlib

Caution:
It is illegal to assign an int variable to a pointer, even if the variable’s value happens to be 0.

Under most compilers, when we use an uninitialized pointer, the bits in the memory in which the pointer resides are used as an address. Using an uninitialized pointer is a request to access a supposed object at that supposed location. There is no way to distinguish a valid address from an invalid one formed from the bits that happen to be in the memory in which the pointer was allocated.

That mechanism that the bits in the memory remain unchanged is similar to type conversion from a negative value to an unsigned type.

3.4 pointers used in a condition

So long as the pointer has a valid value, we can use a pointer in a condition.

Two pointers hold the same address if they are both null, if they address the same object, or if they are both pointers one past the same object.

Caution:
When two pointers are the same, it is possible that both of the two pointers are null. We should be careful about this possibility.

3.5 void pointers

The type void* is a special pointer type that can hold the address of any object.

We cannot use a void* to operate on the object it addresses–we don’t know that object’s type, and determines what operators we can perform on the object.

3.6 pointers to pointers

In general, there are no limits to how many type modifiers can be applied to a declarator.

We write ** for a pointer to pointer, *** for a pointer to a pointer to a pointer.

int ival;
int *pi = &ival;
int **ppi = π

3.7 references to pointers

A reference is not an object. Hence, we may not have a pointer to a reference. However, because a pointer is an object, we can define a reference to a pointer.

int i = 42;
int *p;
int *&r = p;  // r is a reference to the pointer p
r = &i; // r is the same as p, pointer p points to i
*r = 0;  // *r is the same as *p, change i to 0

The easiest way to understand the type of r is to read the definition right to left. The symbol closest to the name of the variable (in this case the & or *) is the one that has the most immediate effect on the variable’s type. Thus, we know that r is a reference. The rest of the declarator determines the type to which r refers.

4. similarities and differences between references and pointers

similarities:
used for indirect access to other objects

differences:

referencespointers
not an objectis an object
once initialized, cannot be reassignedcan be reassigned
have to be initialized when being defindeddo not have to be initialized

5. basic introduction to const qualifier

We can make a variable unchangeable by defining the variable’s type as const.

Because we can’t change the value of a const after we create it, it must be initialized.

For all kinds of const variables, including const pointers, have to be initialized. Pointers to const are not const variables, so they don’t have to be initialized, but references to const are references so they have to be initialized.

A const type can use most but not all of the same operations that operations as its nonconst version. The one restriction is that we may use only those operations that cannot change an object.

6. using const objects in multiple files

By default, const objects are local to a file.

This is because

the complier will usually replace uses of the variable with its corresponding value during compilation.
To substitute the value for the variables, the compiler has to see the variable’s initializer.
When we split a program into multiple files, every file that uses the const must have access to its initializer.
In order to see the initializer, the variable must be defined in every file that wants to use the variable’s value.
To support this usage, yet avoid multiple definitions of the same variables, const variables are defined as local to the file.

When we want to use the same const in multiple files, we have two choices.
The first one is: to define the const variable in every file.
The second one is used when we have a const variable whose initializer is not a const expression and we don’t want the compiler to generate a separate variable in each file: to define the const in one file and to declare it in other files that use that object.

7. references to const

We can bind a reference to an object type. To do so we use a reference to const.

Whether a reference refers to a const or nonconst type affects what we can do with that reference, not whether we can alter the binding of the reference itself.

The meaning of const of reference to const is that the object bound to the reference cannot be changed.

If we want to define a reference to a const variable, we have to use a reference to const instead of a plain reference. (This can be viewed as part of the principle of copying)

8. pointers to const

Like a reference to const, a pointer to const may not be used to change the object to which the pointer points.

Defining a pointer as a pointer to const affects only what we can do with the pointer.

The meaning of const of pointer to const is that the object being pointed cannot be changed.

We may store the address of a const object only in a pointer to const.

That is to say, if we want to define a pointer to a const variable, we have to use a pointer to const instead of a plain pointer. (This can be viewed as part of the principle of copying)

9. the exception of type matching for references to const and pointers to const

The exception can only be applied when a reference or pointer is to const.

9.1 for references to const

exception1:

We can initialize a reference to const from any expression that can be converted to the type of the reference.

For example,

double dval = 3.14;
const int &ri = dval;  // it is ok

To ensure that the object to which ri is bound is an int, the complier transforms this code into something like

const int temp = dval;  // the temp that compiler automatically create is also const
const int &ri = temp;

exception2:

A reference to const may refer to an object that is not const. Binding a reference to const to an object says nothing about whether the underlying object itself is const.

For example,

int i = 42;
const int &r2 = i;  // it is ok, but cannot be used to change i

9.2 for pointers to const

exception1:

we can use a pointer to point to a nonconst object. Like a reference to const, a pointer to const says nothing about whether the object to which the pointer points it const.

For example,

double dval = 3.14;
const double *cptr = &dval;  // it is ok, but cannot change dval through cptr

10. const pointer

Unlike reference, pointers are objects. Hence, as with any other object type, we can have a pointer that is itself const.

Like other any const object, a const pointer must be initialized and once initialized, its value may not be changed.

It is not difficult to distinguish a const pointer from a pointer to const. For example,

const double pi = 3.14;
const double *const pip = π  

The first const is used to modify the object the pointer points to, which means it is a pointer to const. The second const is used to modify the pointer pip, which means it is a const pointer.

Caution:
Unlike a reference to const, we cannot initialize a pointer to const from a expression that isn’t the type of the pointer points to. A reference to const can do so because the compiler can automatically create a temporary variable to hold the converted variable, but the compiler wouldn’t do that for a pointer to const.

11. top-level const and low-level const

We use the term top-level to indicate that the pointer itself is a const.
More generally, top-level const indicates that an object itself is const. Top-level can appear in any object type.

When a pointer can point to a const object, we refer to that const as a low-level const.
Low-level const appears in the base type of compound types such as pointers or references.

12. the priciple of copying an object

The distinction between top-level and low-level const matters when we copy an object.

When we copy an object, both objects must have the same low-level const qualification or there must be a conversion between the types of the two objects. In general, we can convert a nonconst to const but not the other way round.

If we want to copy a low-level nonconst to a low-level const, it is legal. If we want to copy a low-level const to a low-level nonconst, it is illegal.

If both the two variables don’t have low-level const, they can copy to one another.

13. constant expression and constexpr

A constant expression is an expression whose value cannot change and that can be evaluated at compile time.

When the program is compiled, the constant expressions can be calculated only once, so that they don’t have to be recalculated every time the program runs.

By using constexpr in declarations or definitions, we are telling the compiler that this variable is a constant expression and are asking the compiler to calculate its value.

variables declared as constexpr are implicitly const and must be initialized by constant expressions.

The types we can use in a constexpr are known as “literal types” because they are simple enough to have literal values.
Of all the types we have used so far, the arithmetic, reference, and pointer types are literal types.
Although we can define both pointers and references as constexprs, the objects we used to initialize them are strictly limited. We can initialize a constexpr pointer from the nullptr literal or the literal 0. We can also point to or bind to an object that remains at a fixed address. (variables defined inside a function ordinarily are not stored at a fixed address)

the constexpr specifier applies to the pointer, not the type to which the pointer points.

That is to say, constexpr is used to modify the pointer. For example,

constexpr int *q = nullptr;

We should remember that

constexpr imposes a top-level const on the objects it defines.

Word List

compound type 复合类
address-of operator 取地址操作符
dereference operator 解引用操作符
constant expression 常量表达式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值