Table of Contents

  1. Reference
  2. Pointer
  3. const
  4. constexpr

1. Reference ↑top

Reference is an alias to an already-existing object or function.

(1) Features:

(2) Usages

//int& ref1 =1;//non-const lvalue reference cannot bind to a temporary
const int& ref2 = 2; //OK
int val = 3;
int& ref3 = val; //OK

2. Pointer ↑top

A pointer is a type pointing to anothe type. A pointer is basically the same as common variables, storing a piece of data. Unlike normal variable which stores a value, e.g. int, double or char, a pointer stores a memory address.

Pointers are simply addresses. Deferencing a pointer involves copyting that pointer into a register, and then using this register in a memory reference.

(1) pointer vs. reference

(2) void* pointer

A special pointer type that can hold the address of any object of any data type. But, the type of the object at that address is unkown.

We cannot use a void* to operate on the object it addresses.

Generally, we use a void* pointer to deal with memory as memory, rather than using the pointer to access object stored in that memory.

int value = 1024;
void *voidPtr = &value;
//cout << *voidPtr << endl; //ERROR: canot dereference
int *intPtr = static_cast<int*>(voidPtr);
cout << *intPtr << endl; //now, dereference as normal

(3) References to pointers

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

int i =42;
int *p; //p is a pointer to int
/* read from right to left:
 * (a). &r means r is a reference
 * (b). * means r refers to a pointer
 * (c). int means pointer points to an int
 * together: r is a reference to a pointer to an int
 */
int *&r = p; //r is a reference to the pointer p

r = &i; //r refers to a pointer; assigning &i to r makes p point to i
*r = 0; //dereferencing r yields i, pointed by p; changes i to 0

(4) Pointer and array

Array can hold objects of most any type, including pointers.
Array is an object, and thus we can define both pointers and references to arrays.

(i) complicated array decl

int *ptrs[10]; //ptrs is an arr of ten ptrs to int
int &refs[10] = /* ? */; //ERROR: no arr of refs
int (*Parray)[10] = &arr; //Parray points to an arr of ten ints
int (&arrRef)[10] = arr; //arrRef refers to an arr of ten ints

int *(&arry)[10] = ptrs; //arry is a reference to an arr of ten points to int

(ii) pointer to array

Operations on arrays are often really operations on ptrs.

Pointers are iterators

int *p = &ia[2]; //p points to the ele indexed by 2
int j = p[1]; //p[1] i.e. *(p+1), ia[3]
int k = p[-2]; //p[-2] is the same ele as ia[0]

(iii) pointer and multiD array

MultiD array is actually array of arrays.

int ia[3][4]; //array if size 3; each ele is an arr of ints of size 4
int (*p)[4] = ia; //p points to an arr of 4 ints
p = &ia[2]; //p now points to the last ele in ia

(5) Pointer and function

A function ptr is just that - a ptr that denotes a func rather than an obj.
Like any other ptr, a func ptr points to a particular type.
A func's type is determined by its return type and the types of its paras.
The func's name is not part of its scope.

//type: bool(const string&, const string&)
bool lengthCompare(const string &, const string &);
-----------
//form: return_type (*func)(parameter list)
bool (*pf)(const string &, const string &); //uninit

(i) using function pointers

When we use the name of a func as a value, the func is automatically converted to a pointer.

pf = lengthCompare; //pf points to the func
pf = &lengthCompare; //'&' is optional

We can use a ptr to a func to call the func to which the ptr points.

bool b1 = pf("hello", "goodbye"); //calls lengthCompare
bool b2 = (*pf)("hello", "goodbye"); //ditto
bool b3 = lengthCompare("hello", "goodbye"); //ditto

(ii) pointers to overloaded functions

void ff(int*);
void ff(unsigned int);
void (*pf1)(unsigned int) = ff; //pf1 points to ff(unsigned)

The compiler uses the type of the ptr to decide which overloaded func to use.

(iii) functions pointer parameters

//3rd para is a func type and is auto treated as a ptr to func
void useBigger(const string &s1, const string &s2,
                bool pf(const string &, const string &));
//equival decl: explicitly define the para as a ptr to func
void useBigger(const string &s1, const string &s2,
                bool (*pf)(const string &, const string &));
--------------
//auto converts the func to a ptr to func
useBigger(s1, s2, lengthCompare);

Type aliases, along with decltype enable simpler code to use func ptrs:

//Func and Func2 have func types
typedef bool Func(const string&, const string&);
typedef decltype(lengthCompare) Func2; //same type
//FuncP and FuncP2 have ptr to func type
typedef bool(*FuncP)(const string&, const string&);
typedef decltype(lengthCompare) *FuncP2; //same type

(iv) returning a pointer to function

using F = int(int*, int); //F is a func type, not a ptr
using PF = int(*)(int*, int); //PF is a ptr type

Unlike what happens to paras that have func type, the return type is not auto converted to a ptr type.
Thus, we must explicitly specify that the return type is a ptr type:

PF f1(int); //OK: pf is a ptr to func; f1 returns a ptr to func
F f1(int); //ERROR: F is a func type; f1 can't return a func
F *f1(int); //OK: explicitly specify that the return type is a ptr to func
------------------
//we can directly declare f1
int (*f1(int))(int*, int);
//we can also simplify the decl using a trailing return
auto f1(int) -> int (*)(int*, int)

3. const ↑top

Const is to make a variable unchangeable.

(1) Reference to const

a reference that refers to a const type. A reference to const cannot be used to change the bound object.

const int ci = 1024;
const int &r1 = ci; //OK: both ref and obj are const
r1 = 42; //ERROR: r1 is a reference to const
int &r2 = ci; //ERROR: nonconst ref to const obj

A reference to const may refer to an object that is NOT const
a reference to const resticts ONLY what we can do through that reference, but says nothing about whether the underlying object itself is const or not.

int i = 42;
int &r1 = i; //r1 bound to i
const int &r2 = i; //r2 also bound to i; but cannot change i
r1 = 0; //OK
r2 = 0; //ERROR: r2 is a reference to const

(2) Pointers and const

const double pi = 3.14; //pi is const, val cannot be changed
double *ptr = &pi; //ERROR: ptr is a plain pointer
const double *cptr = &pi; //OK: cptr points to a double that is const
*cptr = 42; //ERROR: cannot assign to *cptr

double dval = 3.14; //a plain double
cptr = &dval; //OK: but cannot change dval via cptr
int errNumb = 0;
int *const curErr = &errNumb; //curErr always points to errNumb
const double pi = 3.14;
const double *const pip = &pi; //pip is a const ptr to a const obj

(3) const in object

In OOP, a 'method' of an object has access to the member variables.

class Class1{
    void Method1();
    int MemberVariable;
}
//method1() has no explicit paras but it can still alter 'MemberVariable'
void Class1::Method1(){
    MemberVariable1 += 1;
}

To avoid this, we can put const after the para list:

class Class2{
    void Method1() const;
    int MemberVariable;
}

const int* const Method3(const int* const&) const;
const-1: returns a pointer to const
const-2: the returned pointer itself is const
const-3 and -4: parameter is a const pointer to a pointer, which points to a const int
const-5: the method cannot alter member fields.

(4) Top-level const

A pointer is an object that can point to a different object. As a result, we can talk independently about whether a pointer is const and whether the objects to which it can point are const.

int i = 0;
int *const p1 = &i; //top-level, cannot change p1
const int ci = 42; //top-level, cannot change ci
const int *p2 = &ci; //low-level, cannot change *p2
const int *const p3 = p2; //left-low, right-top
const int &r = i; //low-level, cannot change i via r

4. constexpr ↑top

Conceptually, constexpr indicates a value that's not only constant, it's known during compilation.

const int *p = nullptr; //p is a ptr to a const int
constexpr int *q = nullptr; //q is a const ptr to int