Skip to Content
CSE332SObject-Oriented Programming Lab (Lecture 3)

CSE332S Object-Oriented Programming in C++ (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
struct My_Data{ My_Data(int x, int y): x_(x), y_(y) {} int x_; int y_; };
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
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.

symbolused in declarationused 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:

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:

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:

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:

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:

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:

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.

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.

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.

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
Last updated on