Post on 28-Dec-2015
Chakrabarti
Variable storage thus far Never used global variables All variables allocated inside functions,
passed by value or reference to other functions
In case of non-collection variables, the storage was always taken from the stack
In case of collection variables, storage was taken from the stack and the heap, but we did not need to understand how, or interfere
Chakrabarti
Want a variable to outlive its scope Same customer can have a checking
account and a fixed deposit Should not make copies of customer record Supposing address or phone changes
Customer makeCustomer(…) { … }Vs.Customer *makeCustomer(…) { … }
For simplicity let’s start with int variables
Chakrabarti
new and delete
int *pi = new int;
*pi = 5;
cout << pi << ' ' << *pi + 1 << endl;
delete pi;
pi points to an integer
Allocates four bytes fom heap
The value is always accessed
as *pi
Returns four allocated bytes to system: do this
exactly once!
The value is always accessed
as *pi
Chakrabarti
Pointer syntaxTypeName *pointerVariableName; TypeName is int, float, double etc. or the
name of a class like vector<int> etc. * is the “pointer access” or “pointer
dereference” operator To read or write the storage holding the
value of type TypeName, use *pointerVariableNamein either the lhs or rhs
To read or write the pointer itself use pointerVariableName without a star
Chakrabarti
Allocating arrays in the heapint nn = 5;
int *pi = new int[nn];for (int ix=0; ix < nn; ++ix) { cout << pi[ix] << endl;
cout << *(pi+ix) << endl;
}delete [] pi;
Type remains same
Allocates in heap space for nn ints
Returns space back to heap (which remembers size but won’t tell you)
Looks like array access
Contents of cell ix integers after address pi
Chakrabarti
The null pointer Address 0 is not a legal address to read/write Programs reliably fail if they attempt to do so Therefore, int *pa = 0; is a safe
initializer until memory is allocated (or after it is freed)
But 0 is of type int, while pa is of type int * (pointer to int)
Therefore a type cast is requiredint *pa = (int*) 0;
Can do this will any type in place of int
Chakrabarti
Null pointer for classesclass Customer {public: static Customer * const null;};
Customer * const Customer::null = (Customer*) 0;
Can now use Customer::null anywhere Cleaner than (Customer*) 0 everywhere
Chakrabarti
Our own vector class: specificationclass Vector {public: Vector(); Vector(const Vector& other); ~Vector(); void insert(int p, float v); float remove(int p); float get(int p) const; int size() const;}
Promises that these methods will not modify the
Vector in any way
Default constructor: empty vector
Copy constructor
Destructor
Chakrabarti
Vector implementation Three private fields Native array pa of floats allocated on heap int cap recording the number of floats that
can fit in the array (capacity) int siz recording the number of floats
currently in the array (from position 0 onward)
Invariant: siz cap Run out of space: allocate larger array, copy siz<< cap: allocate smaller array, copy
Chakrabarti
insertvoid insert(int p, float v) { if (siz + 1 <= cap) { // enough space } else { float *nPa = new float[siz+1]; int wx = 0, rx = 0; while (rx < p) { nPa[wx++] = pa[rx++]; } nPa[wx++] = v; while (rx < siz) { nPa[wx++] = pa[rx++]; } if (cap > 0) { delete [] pa; } cap = siz = siz + 1; pa = nPa; }}
pa
p
nPa v
Chakrabarti
Our own queue class: specification
class Queue { // of ints, saypublic: bool isEmpty() const; int removeFirst(); void pushLast(int val); void print() const;private: QueueElement *first, *last;};
Chakrabarti
Queue implementation
struct QueueElement { int value; QueueElement *next;
QueueElement(int v) : value(v), next(null) { }}
Chakrabarti
Printing a queue
QueueElement *pqe = first;while (pqe != null) { cout << pqe->value << endl; pqe = pqe->next;}
value next value next
pqe
Chakrabarti
Constructor and destructor
Queue::Queue() { first=last=null; }
Queue::~Queue() { while (first != null) { QueueElement *pQe = first; first = first->next; delete pQe; }}
first next
pQe
Chakrabarti
pushLastvoid Queue::pushLast(int val) { QueueElement *pQe = new QueueElement(val); if (first == null) { first = pQe; }
if (last != null){ last->next=pQe; }
last = pQe;}
last
val
pQe
Chakrabarti
removeFirstint Queue::removeFirst() { int ans = first->value; QueueElement *pQe = first; first = first->next; delete pQe; return ans;}
Chakrabarti
Binary search tree A binary tree is either empty Or it has a root node With two children left and right Each of which is a binary tree Suppose each node contains an int key Assume no duplicate keys for starters The tree is a search tree if
• All keys in left are smaller than the root• All keys in right are larger than the root
Chakrabarti
Search tree from main()TreeNode *root = null;for (;;) { int key; cin >> key; if (root == TreeNode::null) { root = new TreeNode(key); } else { root->insert(key); }}
Chakrabarti
Definition of the tree nodestruct TreeNode { const int key; TreeNode *left, *right; TreeNode(int _key) : key(_key) { left = right = null; } void insert(int nKey) { // while satisfying property }};
Chakrabarti
insert, first cutif (nKey < key) { if (left == null) { left = new TreeNode(nKey); } else { left->insert(nKey); }}else { // repeat above, right instead of left}
Tedious, can be avoided if you have a good understanding of * and &
Chakrabarti
void insert(int nKey)
TreeNode*& child = nKey < key? left : right;if (child == null) { child = new TreeNode(nKey);}else { child->insert(nKey);}
child is a reference to a pointer to a TreeNode
Recursive call from child node
Chakrabarti
How to print the keys in ordervoid print() { if (left != null) left->print(); cout << key << ' '; if (right != null) right->print();}
main() { if (root != null) root->print(); cout << endl;}
1
2
3
Chakrabarti
Adding a parent pointer Walking down from parent to child is easy Sometimes handy to be able to walk up from child
to parent as well, e.g., to find siblings root->up is null struct TreeNode { const int key; TreeNode *left, *right, *up; TreeNode(int _key, TreeNode *_up) : key(_key), up(_up) {
left = right = null; } void insert(int nKey) { … }};
Chakrabarti
Constant pointer vs. pointer to constant Suppose we want the up pointer to be
constant This is declared asTreeNode * const up;
Content of TreeNode can change A pointer to a constant TreeNode would beconst TreeNode * up;
Not the same! Can also haveconst TreeNode * const up;
Chakrabarti
Pointers vs. references
int a = 3, b = 5;int *px = &a;px = &b;// above statements do not// change values in cells// called a and bint &rx = a;rx = b;// this results in a = b = 5
Chakrabarti
Multiple pointers to one record Generally necessary in applications E.g., fixed deposit accounts and checking
accounts pointing to shared customer record Care is needed in destructors Deleting a checking account should not
destroy a customer record Should deleting the last account of a
customer delete the customer record? Clear “home” collection whose destruction
deletes customer record