# Lecture 3

## C++ basic data types

- int, long, short, char (signed, integer arithmetic)
  - char is only 1 byte for all platforms
  - other types are platform dependent
  - can determine the size of the type by using `sizeof()`, `<climits> INT_MAX`
- float, double (floating point arithmetic)
  - more expensive in space and time
  - useful when you need to describe continuous quantities
- bool (boolean logic)

### User-defined types

- (unscoped or scoped) enum
  - maps a sequence of integer values to named constants
- function and operators
  - function is a named sequence of statements, for example `int main()`
- struct and class
  - similar to abstractions in cpp, extend C struct

### struct and class

- struct is public by default
- class is private by default
- both can have
  - member variables
  - member functions
  - constructors
  - destructors
- common practice:
  - use struct for simple data structures
  - use class for more complex data structures with non-trivial functionality

```cpp
struct My_Data{
    My_Data(int x, int y): x_(x), y_(y) {}
    int x_;
    int y_;
};
```

```cpp
class My_Data{
    public:
        My_Object(int x, int y): x_(x), y_(y) {}
        ~My_Object(){}
    private:
        int x_;
        int y_;
};
```

### More about native and user-defined types

- Pointer
  - raw memory address of an object
  - its type constrains what types it can point to
  - can take on a value of 0 (null pointer)
- Reference
  - alias for an existing object
  - its type constrains what types it can refer to
  - cannot take on a value of 0 (**always** refer to a valid object)
- Mutable (default) vs. const types (read right to left)
  - `const int x;` is a read-only variable
  - `int j` is a read-write declaration

## Scopes

Each variable is associated with a scope, which is a region of the program where the variable is valid

- the entire program is a global scope
- a namespace is a scope
- member of a class is a scope
- a function is a scope
- a block is a scope

```cpp
int g_x; // global scope
namespace my_namespace{
    int n_x; // namespace scope
}
class My_Class{
    int c_x; // class scope
    int my_function(){
        int f_x; // function scope
        {
            int b_x; // block scope
        }
        return 0;
    }
}
```

A symbol is only visible within its scope

- helps hide unneeded details (abstraction)
- helps avoid name collisions (encapsulation)

## Motivation for pointer and reference

We often need to _refer_ to an object, but don't want to copy it

There are two common ways to do this:

- Indirectly, via a pointer
  - This gives the address of the object
  - Requires the code to do extra work. eg, dereferencing
  - Like going to the address of the object
- Directly, via a reference
  - Acts as an alias for the object
  - Code interacts with reference as if it were the object itself

## Pointer and reference syntax

### Pointer

A pointer is a variable that holds the address of an object

can be untyped. eg, `void *p;`

usually typed. eg, `int *p;` so that it can be checked by the compiler

If typed, the type constrains what it can point to, a int pointer can only point to an int. `int *p;`

A pointer can be null, eg, `int *p = nullptr;`

We can change to what it points to, eg, `p = &x;`

### Reference

A reference is an alias for an existing object, also holds the address of the object, but is only created on compile time.

Usually with nicer interface than pointers.

Must be typed, and its type constrains what types it can refer to. `int &r;`

Always refers to a valid object, so cannot be null. `int &r = nullptr;` is invalid.

Note: **reference cannot be reassigned to refer to a different object.**

|symbol|used in declaration|used in definition|
|---|---|---|
|unary `&`|reference, eg, `int &r;`|address-of, eg, `int &r = &x;`|
|unary `*`|pointer, eg, `int *p;`|dereference, eg, `int *p = *q;`|
|`->`|member access, eg, `p->x;`|member access via pointer, eg, `p->second;`|
|`.`|member access, eg, `p.x;`|member access via reference, eg, `p.second;`|

## Aliasing via pointers and references

### Aliasing via reference

Example:

```cpp
int main(int argc, char *argv[]){
    int i=0;
    int j=1;
    int &r = i;
    int &s = i;
    r = 8; // do not need to dereference r, just use it as an alias for i
    cout << "i: " << i << ", j: " << j << ", r: " << r << ", s: " << s << endl;
    // should print: i: 8, j: 1, r: 8, s: 8
    return 0;
}
```

### Aliasing via pointer

Example:

```cpp
int main(int argc, char *argv[]){
    int i=0;
    int j=1;
    int *p = &i;
    int *q = &i;
    *q = 6; // need to dereference q to access the value of j
    cout << "i: " << i << ", j: " << j << ", p: " << *p << ", q: " << *q << endl;
    // should print: i: 6, j: 1, p: 6, q: 6
    return 0;
}
```

### Reference to Pointer

Example:

```cpp
int main(int argc, char *argv[]){
    int j = 1;
    int &r = j; // r is a **reference** to j
    int *p = &r; // p is a **pointer** to the address of r, here & is the address-of operator, which returns the address of the object
    int * &t = p; // t is a **reference** to pointer p, here & is the reference operator, which returns the reference of the object. 
    cout << "j: " << j << ", r: " << r << ", p: " << *p << ", t: " << *t << endl;
    // should print: j: 1, r: 1, p: 1, t: [address of p]
    return 0;
}
```

Notice that we cannot have a pointer to a reference. But we can have a reference to a pointer.

### Reference to Constant

Example:

```cpp
int main(int argc, char *argv[]){
    const int i = 0;
    int j = 1;
    int &r = j; // r cannot refer to i, because i is a constant (if true, alter i through r should be valid)
    const int &s=i; // s can refer to i, because s is a constant reference (we don't reassign s)
    const int &t=j; // t can refer to j, because t is a constant reference (we don't reassign t)
    cout << "i: " << i << ", j: " << j << ", r: " << r << ", s: " << s << ", t: " << t << endl;
    // should print: i: 0, j: 1, r: 1, s: 0
    return 0;
}
```

Notice that we cannot have a non-constant reference to a constant object. But we can have a constant reference to a non-constant object.

### Pointer to Constant

Example:

```cpp
int main(int argc, char *argv[]){
    const int i = 0;
    int j = 1;
    int k = 2;
    
    // pointer to int
    int *w = &j;
    
    // const pointer to int
    int *const x = &j;
    
    // pointer to const int
    const int *y = &i;
    
    // const pointer to const int, notice that we cannot change the value of the int that z is pointing to, in this case j **via pointer z**, nor the address that z is pointing to. But we can change the value of j via pointer w or j itself.
    const int *const z = &j;
}
```

- Read declaration from right to left, eg, `int *w = &j;` means `w` is a pointer to an `int` that is the address of `j`.
- Make promises via the `const` keyword, two options:
  - `const int *p;` means `p` is a pointer to a constant `int`, so we cannot change the value of the `int` that `p` is pointing to, but we can change the address that `p` is pointing to.
  - `int *const p;` means `p` is a constant pointer to an `int`, so we cannot change the address that `p` is pointing to, but we can change the value of the `int` that `p` is pointing to.
- A pointer to non-constant cannot point to a const variable.
  - neither `w = &i;` nor `x = &i;` is valid.
  - any of the pointer can points to `j`.

## Pass by value, pass by reference, and type inference

Example:

```cpp
int main(int argc, char *argv[]){
    int h = -1;
    int i = 0;
    int j = 1;
    int k = 2;
    return func(h, i, j, &k);
}

int func(int a, const int &b, int &c, int *d){
    ++a; // [int] pass by value, a is a copy of h, so a is not the same as h
    c = b; // [int &] pass by reference, c is an alias for j, the value of c is the same as the value of b (or i), but we cannot change the value of b (or i) through c (const int &b)
    *d = a; // [int *] pass by value, d is a pointer to k, so *d is the value of k, a is assigned to value of k.
    ++d; // d is a pointer to k, but pass by value, so ++d doesn't change the value of k.
    return 0;
}
```

### More type declaration keywords

`typedef` keyword introduces a "type alias" for a type.

```cpp
typedef Foo * Foo_ptr; // Foo_ptr is a type alias for Foo *

// the following two variables are of the same type
Foo_ptr p1 = 0;
Foo *p2 = 0;
```

`auto` keyword allows the compiler to deduce the type of a variable from the initializer.

```cpp
int x = 0; // x is of type int
float y = 1.0; // y is of type float
auto z = x + y; // z is of type float, with initialized value 1.0
```

`decltype` keyword allows the compiler to deduce the type of a variable from the type of an expression.

```cpp
int x = 0;
double y = 0.0;
float z = 0.0f;

decltype(x) a; // a is of type int, value is not initialized
decltype(y) b; // b is of type double, value is not initialized
decltype(z) c; // c is of type float, value is not initialized
```

