TexPoint fonts used in EMF. Read the TexPoint manual before you delete this box.: A A A A A A
Dynamic Array Objects CS 1037 Fundamentals of Computer Science II TexPoint fonts used in EMF. Read...
-
Upload
mohammed-copper -
Category
Documents
-
view
215 -
download
0
Transcript of Dynamic Array Objects CS 1037 Fundamentals of Computer Science II TexPoint fonts used in EMF. Read...
Dynamic Array Objects
CS 1037 Fundamentals of Computer Science II
2
A Smarter Dynamic Array
• Built-in dynamic array new[] very limited– manual insert/erase/resize is hard– must remember each array’s size in separate variable– must remember to delete[] each array– no range checking, so bugs very hard to find
• Want something more like this:dynarray_of_int primes(3); // start with 3 itemsprimes[0] = 2; // set item, like an arrayprimes[1] = 3;primes[2] = 5;primes.push_back(7); // add 4th item, so {2,3,5,7}primes.erase(2); // remove 3rd item, {2,3,7}
3
Why C/C++ Arrays So Stupid?
• Performance: most light-weight array possible; this can matter, but rarely
• Simplicity: no interface, no objects
• Philosophical: language should provide basic building blocks; everything else should be a ‘library’ (e.g. the entire STL)– What is built-in to Python/C# is ‘library’ in C++
4
A Unifying Concept
dynarray
soundwave string
bookmarks
polygon
deck_of_cards ...
verticeschart_series edges
5
Interface Version 1 class dynarray_of_double {public: dynarray_of_double(); // start empty dynarray_of_double(int size); // start as non-empty
int size(); // returns current size void push_back(double item); // appends an item void erase(int index); // erases an item void clear(); // resets to empty array
double& operator[](int index); // returns item[index]
// boring details (copy constructor etc.) ~dynarray_of_double(); dynarray_of_double(const dynarray_of_double& src); void operator=(const dynarray_of_double& src); ... };
(for type double)
6
Exact-Size Representationclass dynarray_of_double { ...private: int m_size; // number of items in array double* m_items; // pointer to array data};
primes 3size
2.03.05.0
items
dynarray_of_double primes;primes.push_back(2.0); // add first item primes.push_back(3.0); // add second itemprimes.push_back(5.0); // add third item
(for type double)
7
Exact-Size Representation
• Works fine, but ‘slow’ to add many items. Why?
void dynarray_of_double::push_back(double item){ // allocate array that is 1 slot bigger double* new_items = new double[m_size+1]; for (int i = 0; i < m_size; ++i) new_items[i] = m_items[i]; delete[] m_items; m_items = new_items;
// copy the new item to the last slot of our new array m_items[m_size] = item; m_size++;}
8
Exact-Size Would Be Slow
• Suppose size = n¡1. How much work does push_back do to make size n?
• How much work to build list of n items?
copy item new, delete, size++
really slow for large n
time(n) = an + b
nX
i=1
time(i) =a2
n(n + 1) + bn
= cn2 + dn
9
Realistic dynarray Representation
class dynarray_of_double { ... int m_size; // official number of items added int m_capacity; // actual number of slots reserved double* m_items; // pointer to array data};
primes 3size capacity
2.03.05.0????
4items
dynarray_of_double primes;primes.push_back(2.0); // add first item primes.push_back(3.0); // add second itemprimes.push_back(5.0); // add third item
(for type double)
10
Motivation for capacity
• Advantage: performance1. Appending n items takes ¼ n steps total (good!)
Without capacity, takes ¼ n2 steps (too slow!)
• Disadvantages:1. Wasted space when capacity > size 2. sizeof(dynarray) becomes larger3. Harder to implement (especially array-of-objects)
We will prove this
11
Exercise in Visual Studio
See snippets #1 and #2
12
Real Life™ Example
• Example from Chrome source code// The cookie monster is the system for storing // and retrieving cookies.class CookieMonster: public CookieStore {public: ... typedef std::vector<CanonicalCookie> CookieList;
// Returns all the cookies CookieList GetAllCookiesForURL(const GURL& url);
http://src.chromium.org/viewvc/chrome/trunk/src/net/base/cookie_monster.h?revision=61435&view=markup
std::vector is C++ standard version of dynarray
13
Real Life™ Example
• From UWO research code, defines a graphstruct graph_edge { var_t p,q; // edge goes from vertex p to vertex q cost_t w; // edge weight};
struct energy_defn { varcount_t varcount; // number of variables labelcount_t labelcount; std::vector<graph_edge> edges; ...};
{1,0,20},{1,2,15}
0
1
2
20
15
14
operator[](int)
• Saw this used in A2 vec3 class:class vec3 { ... double& operator[](int index); ... double m_elem[3]; // three elements};
double& vec3::operator[](int index) { return m_elem[index]; // reference to one element}
void main() { vec3 v; v[0] = 5.0; // set 'x' element to 5.0}
15
operator[](int)
• Works same for dynarrayclass dynarray_of_double { ... double& operator[](int index); ... double* m_items; // pointer to items};
double& dynarray_of_double::operator[](int index) { return m_items[index]; // reference to one item}
void main() { dynarray_of_double samples; samples[0] = 5.0; // set first sample to 5.0}
(for type double)
16
Topics for dynarray
We will touch some topics in context of dynarray over next few lectures:
1. Assertions (range checking)
2. More functionality (insert, pop_back)
3. Complexity Analysis (just basics)
4. Arrays-of-objects (advanced C++)
5. Templates (next major topic)
17
Range Checking
• C/C++ arrays let you “shoot self in foot”
• Range checking is huge help finding bugs!• operator[] gives us chance to check
• Such code called a “sanity check”
double* samples = new double[n];for (int i = 0; i <= n; ++i) // nasty bug that quietly samples[i] = 0.0; // messes up memory!
double& dynarray_of_double::operator[](int index) { if (index < 0 || index >= m_size) // check for bug report_error_message(); // notify programmer! return m_items[index];}
18
Sanity Checks – Assertions
• Formal way to express assumption of code that follows. (“assert BLAH is true”)
• If BLAH == false, program will terminate!
#include <cassert> // defines assert()
double square_root(double x) { assert(x >= 0); // check that x >= 0 ...}
y = square_root(-1.0); // assumption violated!
(-1 >= 0) == false
19
Sanity Checks – Assertions
• For arrays, operator[] assumes index within range [0..size), so assert it!
• Buggy code now generates run-time error:
double& dynarray_of_double::operator[](int index) { assert(index >= 0 && index < m_size); // range check return m_items[index]; // index is OK!}
dynarray_of_double samples(n);for (int i = 0; i <= n; ++i) samples[i] = 0.0; // bug detected!!
20
Assertion Examples
double min_value(double* values, int count) { assert(count > 0); // not well-defined for count==0 ...
double arcsin(double x) { assert(x >= -1.0 && x <= 1.0); // defined for [-1,1] ...
rectangle::rectangle(int t, int l, int wd, int ht) { assert(wd >= 0); // width cannot be negative assert(ht >= 0); // height cannot be negative ...
void dynarray_of_double::erase(int index) { assert(index >= 0 && index < m_size); // range check ...
21
dynarray::push_backvoid dynarray_of_double::push_back(double item){ if (m_size == m_capacity) // check if array is full grow_capacity(); // reserve at least one slot m_items[m_size] = item; // copy to next available slot ++m_size; }
class dynarray_of_double { ...private: void grow_capacity(); // internal member function ...};
• Put “grow the array” code in a separate function; makes push_back easier
(for type double)
22
dynarray::push_backvoid dynarray_of_double::grow_capacity(){ if (m_capacity == 0) // choose a larger capacity m_capacity = 1; else m_capacity = m_capacity*2; // double the capacity!
// create bigger array and copy old items double* new_items = new double[m_capacity]; for (int i = 0; i < m_size; ++i) new_items[i] = m_items[i]; // copy old items
delete[] m_items; // delete old array m_items = new_items; // point at new array }
(for type double)
23
dynarray::erasevoid dynarray_of_double::erase(int index){ assert(index >= 0 && index < m_size); for (int i = index; i < m_size-1; ++i) m_items[i] = m_items[i+1]; // copy from next slot m_size--;}
(for type double)
primes.erase(1); // erase second item
primes 3size capacity
2.03.05.0????
4items
before:
primes 2
2.05.05.0????
4after erase:
24
dynarray_of_point
• Just change of item type...class dynarray_of_point {public: ... void push_back(point item); // double --> point void erase(int index); void clear();
point& operator[](int index); // double --> point ...private: int m_size; int m_capacity; point* m_items; // double --> point};
25
dynarray_of_point
void dynarray_of_point::grow_capacity(){ if (m_capacity == 0) m_capacity = 1; else m_capacity = m_capacity*2;
point* new_items = new point[m_capacity]; for (int i = 0; i < m_size; ++i) new_items[i] = m_items[i];
delete[] m_items; m_items = new_items;}
• Just change of item type...
26
dynarray_of_point
• Access (x,y) just like for built-in arrayspoint p(10,20);point q(15,30);
dynarray_of_point polygon(3);polygon[0] = p; // set 1st pointpolygon[1] = q; // set 2nd pointpolygon[2].x = p.x; // set 3rd point's x componentpolygon[2].y = q.y; // set 3rd point's y component
polygon3
size capacity101510
3items
203030
x y
27
Exercise in Visual Studio
See snippet #3
28
dynarray of Pointers
• Motivation: Recall “array of arrays” example (from Review)double** tracks = new double*[num_tracks]; // array offor (int i = 0; i < num_tracks; ++i) // sound tracks[i] = new double[num_samples]; // patterns
tracks
????
????
stack heap
...
????
????
......
????
????
...
29
dynarray_of_doubleptr
• Again, just change of item type...class dynarray_of_doubleptr {public: ... void push_back(double* item); // double --> double* void erase(int index); void clear();
double*& operator[](int index); // double --> double* ...private: int m_size; int m_capacity; double** m_items; // double --> double*};
30
• Again, just change of item type...
dynarray_of_doubleptr
void dynarray_of_doubleptr::grow_capacity(){ if (m_capacity == 0) m_capacity = 1; else m_capacity = m_capacity*2;
double** new_items = new double*[m_capacity]; for (int i = 0; i < m_size; ++i) new_items[i] = m_items[i];
delete[] m_items; m_items = new_items;}
31
dynarray_of_doubleptr
• Each item is a double* variabledouble* piano = new double[1000]; // array of samplesdouble* brass = new double[1000]; // array of samples
dynarray_of_doubleptr tracks;tracks.push_back(piano); // add track for pianotracks.push_back(brass); // add track for trumpet etc.
tracks2
size cap2
items
0.0
0.0
...
brass0.0
0.0
...
piano
32
dynarray_of_doubleptr
• Each item is a double* variabledouble* samples = tracks[0]; // copy ptr to 1st tracksamples[0] = 5.0; // 1st track, 1st sample
tracks[1][0] = 7.0; // 2nd track, 1st sample
tracks2
size cap2
items
5.0
0.0
...
7.0
0.0
...
samples
33
Exercise in Visual Studio
See snippet #4
34
Interface Version 2
... bool empty(); // test if size==0 void resize(int new_size); // set official size void reserve(int capacity); // pre-allocate capacity
void insert(int index, double item); // insert copy of void erase(int index); // item at 'index'
void push_back(double item); void pop_back(); // erase last item
void push_front(double item); // prepend copy of item void pop_front(); // erase first item
double& front(); // shorthand for items[0] double& back(); // shorthand for items[size-1]
• Add more functionality...
dynarray version 2
35
dynarray::insert // inserts item at specified index; items in // range [index..size) will be moved upwards by 1 slot void insert(int index, double item);
primes.insert(1,3.0); // insert 3.0 into 2nd slot
primes 3size capacity
2.05.07.0????
4items
before:
primes 4
2.03.05.07.0
4after insert:
dynarray version 2
36
dynarray::insertvoid dynarray_of_double::insert(int index, double item){ assert(index >= 0 && index <= m_size);
if (m_size == m_capacity) // reserve space if needed grow_capacity();
// move items that follow 'index' to a higher slot for (int i = m_size; i > index; --i) m_items[i] = m_items[i-1];
m_items[index] = item; // copy item to requested index ++m_size;}
dynarray version 2
37
Other Functionality is Easy!void dynarray_of_double::push_back(double item) { insert(m_size,item); // push_back can now call insert!}
void dynarray_of_double::pop_back() { erase(m_size-1);}
void dynarray_of_double::push_front(double item) { insert(0,item);}
void dynarray_of_double::pop_front() { erase(0);}
double& dynarray_of_double::back() { assert(m_size > 0); return m_items[m_size-1];}
dynarray version 2
38
Exercise in Visual Studio
See snippet #5
39
• Suppose size = n¡1. How much work does push_back do to make size n?
• How much work to build list of n items?
Rough Complexity Analysis
time(n) ¼½
n if n ¡ 1 is power of 2;1 otherwise:
nX
i=1
time(i) ¼ n +blogncX
i=0
2i
¼ 3nmuch faster than n2 !
40
• Suppose size = n¡1. How much work does push_front do to make size n?
• How much work to build list of n items?
Rough Complexity Analysis
time(n) ¼½
2n if n ¡ 1 is power of 2;n otherwise:
nX
i=1
time(i) ¼ 12n(n + 1) +
blogncX
i=0
2i
¼ 12n2
very slow for large n
41
dynarray of Objects
• Recall our simple string object...
hi5
'h' 'e' 'l' 'l' 'o' 0
main
globals
call stack
'h' 'e' 'l' 'l' 'o' 0
heap
size chars
empty0 0
void main() { string hi("hello"); // copy of "hello" string empty; // empty string (null-terminated)}
dynarray version 2
42
dynarray of Objects
names2
'L' 'i' 'a' 'm' 0
main
globals
call stack heap
void main() { dynarray_of_string names; names.push_back("Liam"); names.push_back("Elora");}
'E' 'l' 'o' 'r' 'a' 0
2size cap items
4
size chars5
'L' 'i' 'a' 'm' 0
'E' 'l' 'o' 'r' 'a' 0
dynarray version 2
43
dynarray of Objects
• How many string objects should exist?
• How many do exist?
void main() { dynarray_of_string copies; copies.push_back("test"); copies.push_back("test"); copies.push_back("test");}
copies3
't' 'e' 's' 't' 0
main
globals
4size cap items
4
size chars0
44
't' 'e' 's' 't' 0
't' 'e' 's' 't' 0
't' 'e' 's' 't' 0
0
Four!
extra string object!
dynarray version 2
44
dynarray of Objects
• So... is it OK to create >size objects?• Some extra objects seem harmless...– int, double, string, point, double*
• Some extra objects not acceptable...– objects with side-effects in constructor/destructor– objects with no default-constructor (won’t compile)
• Poor design: breaks encapsulation!– capacity is internal implementation detail;
should only affect speed, not behaviour
dynarray version 2
45
Bad Example #1
• Default-constructor wouldn’t be sensible:
• student with no identity not allowed!
class student {public: student(string name, int student_id); ...};
void dynarray_of_student::grow_capacity(){ ... student* new_items = new student[m_capacity]; ...}
dynarray version 2
46
Bad Example #2
• Constructor, destructor w/ side-effects...
• Extra objects can affect program!
int num_animals = 0;
struct animal { animal() { num_animals++; } // keep track of animals ~animal() { num_animals--; } // currently in existence ...};
dynarray_of_animal animals;animals.push_back(load("duck.cfg"));animals.push_back(load("pig.cfg"));animals.push_back(load("chicken.cfg"));cout << num_animals << endl;
?
dynarray version 2
47
Bad Example #3
• Default-constructor can allocate resources
• If capacity doubles, up to 50% of objects are ‘extra’ and waste even more space!
struct sound { int size; double* samples; sound() { *this = load_sound("default.mp3"); } ~sound() { delete[] samples; }};
dynarray version 2
48
Exercise in Visual Studio
See snippet #6
49
An Unacceptable Situation
• Current erase doesn’t destruct objectvoid main() { dynarray_of_string copies; copies.push_back("test"); copies.push_back("test"); copies.erase(1);}
dynarray version 2
copies1
't' 'e' 's' 't' 0
main
globals
2size cap items
4
size chars4
't' 'e' 's' 't' 0
't' 'e' 's' 't' 0
object doesn’t officially exist!
50
Solution: In-Place Destructor
• Need to explicitly call destructor on item!void dynarray_of_string::erase(int index){ assert(index >= 0 && index < m_size); for (int i = index; i < m_size-1; ++i) m_items[i] = m_items[i+1]; // copy from next slot m_size--; m_items[m_size].~string(); // manually destruct item}
copies1
't' 'e' 's' 't' 0
main
globals
2size cap items
4
size chars???? ????
't' 'e' 's' 't' 0
dynarray version 3
51
Also Need In-Place Constructor
• C++ feature beyond scope of this course– strange syntax, obscure-yet-important feature
• Example to give a taste...char* mem = new char[4*sizeof(string)]; // allocate bytesstring* s = (string*)mem; // point at bytesnew (&s[0]) string("test"); // call constructor on 1st slotnew (&s[1]) string("test"); // call constructor on 2nd slot
s
main
???? ????
size chars???? ????
't' 'e' 's' 't' 0
't' 'e' 's' 't' 0mem
44
dynarray version 3
52
dynarray Version 3
• Version 3 is proper array-of-objects:– avoids constructing ‘extra’ objects– destructs objects when they are erased
• dynarray_of_string available from CS1037 web site under “Lectures”
• You will not be tested on in-place constructors or destructors!
53
dynarray Version 4
• We will use C++ templates so that one implementation works for any item type!
dynarray<T>
dynarray_of_int
dynarray_of_double
dynarray_of_doubleptr
dynarray_of_point
dynarray_of_string...