Download presentation
Presentation is loading. Please wait.
Published byMilton Christopher Brown Modified over 9 years ago
1
Practice with C++: Writing a concrete class ECE 417/617: Elements of Software Engineering Stan Birchfield Clemson University
2
This exercise We will design and create a class to hold a dynamically resizable array We will call it vector It will function like std::vector in STL This is an example of a concrete class, which – does a single, relatively small thing well and efficiently –hides data members (encapsulation) –provides clean interface –acts like a built-in type –is a “foundation of elegant programming” – Stroustrup We will build it incrementally, to learn the process
3
#1: Design the class Sketch what you think the class should look like –methods –variables –behavior –constraints –...
4
A possible design vector initialize set_length get_length set_value get_value insert_element remove_element push pop copy –––––––––––––– size data We won’t implement all of these, or necessarily use these names
5
#2: Simple fixed-length array Write class called vector Holds an array of integers Keep track of array length size and resize methods to get and set the length Max length of 1000 Make everything public Use coding conventions: m_ to lead member variables Put implementation into class declaration (in the.h file) Don’t worry about const or assert
6
class vector { public: int size() { return m_n; } void resize(int n) { m_n = n; } int m_data[1000]; int m_n; }; Class
7
class vector { public: int size() { return m_n; } void resize(int n) { m_n = n; } int m_data[1000]; int m_n; }; Class (more compact)
8
#3: Test routine Now write a test routine for your class Called ComputeSum, it takes a single argument, the number of elements Test routine initializes class by setting the ith element to i Routine then computes the sum of all the elements
9
class vector { public: int size() { return m_n; } void resize(int n) { m_n = n; } int m_data[1000]; int m_n; }; ClassTest code int ComputeSum(int n) { vector v; v.resize(n); int i; for (i=0 ; i<n ; i++) { v.m_data[i] = i; } int sum = 0; for (i=0 ; i<n ; i++) { sum += v.m_data[i]; } return sum; }
10
#4: Hide the data Make data private GetValue / SetValue methods for accessing elements
11
class vector { public: int size() { return m_n; } void resize(int n) { m_n = n; } int GetValue(int i) { return m_data[i]; } void SetValue(int i, int a) { m_data[i] = a; } private: int m_data[1000]; int m_n; }; Class
12
How does this change the test code?
13
class vector { public: int size(); void resize(int n); int m_data[1000]; int m_n; }; ClassTest code int ComputeSum(int n) { vector v; v.resize(n); int i; for (i=0 ; i<n ; i++) { v.m_data[i] = i; } int sum = 0; for (i=0 ; i<n ; i++) { sum += v.m_data[i]; } return sum; } (Old way: everything public)
14
class vector { public: int size(); void resize(int n); int GetValue(int i); void SetValue(int i, int a); private: int m_data[1000]; int m_n; }; ClassTest code int ComputeSum(int n) { vector v; v.resize(n); int i; for (i=0 ; i<n ; i++) { v.SetValue(i, i); } int sum = 0; for (i=0 ; i<n ; i++) { sum += v.GetValue(i); } return sum; } (New way: data hidden)
15
#5: Assert What is wrong with the class as is? Suppose I call v.GetValue(9999)? To fix this problem, use assert
16
class vector { public: int size() { return m_n; } void resize(int n) { assert( n >= 0 && n <= 1000 ); m_n = n; } int GetValue(int i) { assert( i >= 0 && i < m_n ); return m_data[i]; } void SetValue(int i, int a) { assert( i >= 0 && i < m_n ); m_data[i] = a; }...
17
class vector { public: int size() { return m_n; } void resize(int n) { assert( n >= 0 && n <= 1000 ); if (n 1000) return; m_n = n; } int GetValue(int i) { assert( i >= 0 && i < m_n ); if (i m_n) return -1; return m_data[i]; } void SetValue(int i, int a) { assert( i >= 0 && i < m_n ); if (i m_n) return; m_data[i] = a; }... If you want to be extra careful, use if and assert However, keep in mind: if slows down your program have to make a decision do nothing silently, or throw exception / crash (tradeoff between wrong answer and no answer)
18
#6: Constructor Now add constructor that takes the initial length of the array as an argument Also add default constructor Do not use initializer lists yet
19
class vector { public: vector() { m_n = 0; } vector(int n) { m_n = n; } int size(); void resize(int n); int GetValue(int i); void SetValue(int i, int a); private: int m_data[1000]; int m_n; }; Class
20
How does this change the test code?
21
class vector { public: int size(); void resize(int n); int GetValue(int i); void SetValue(int i, int a); private: int m_data[1000]; int m_n; }; ClassTest code int ComputeSum(int n) { vector v; v.resize(n); int i; for (i=0 ; i<n ; i++) { v.SetValue(i, i); } int sum = 0; for (i=0 ; i<n ; i++) { sum += v.GetValue(i); } return sum; } (Old way: no constructor with parameters)
22
class vector { public: vector() { m_n = 0; } vector(int n) { m_n = n; } int size(); void resize(int n); int GetValue(int i); void SetValue(int i, int a); private: int m_data[1000]; int m_n; }; ClassTest code int ComputeSum(int n) { vector v(n); int i; for (i=0 ; i<n ; i++) { v.SetValue(i, i); } int sum = 0; for (i=0 ; i<n ; i++) { sum += v.GetValue(i); } return sum; } (New way: use non-default constructor)
23
#7: Initializer lists Now modify constructors to use initializer lists
24
class vector { public: vector() { m_n = 0; } vector(int n) { m_n = n; } int size(); void resize(int n); int GetValue(int i); void SetValue(int i, int a); private: int m_data[1000]; int m_n; }; Class (Old way: no initializer lists)
25
class vector { public: vector() : m_n(0) {} vector(int n) : m_n(n) {} int size(); void resize(int n); int GetValue(int i); void SetValue(int i, int a); private: int m_data[1000]; int m_n; }; Class (New way: use initializer lists)
26
#8: Dynamic resizing Remove restriction of max length of 1000 Use realloc / free only free memory in destructor don’t do copy constructor or assignment operator yet
27
class vector { public: vector() : m_n(0) {} vector(int n) : m_n(n) {} int size(); void resize(int n) { m_n = n; } int GetValue(int i); void SetValue(int i, int a); private: int m_data[1000]; int m_n; }; Class (Old way: fixed length of 1000)
28
class vector { public: vector() : m_n(0), m_data(0) {} vector(int n) : m_n(n), m_data(0) { resize(n); } ~vector() { free(m_data); } int size(); void resize(int n) { m_n = n; realloc(m_data, n*sizeof(int)); } int GetValue(int i); void SetValue(int i, int a); private: int* m_data; int m_n; }; Class (New way: dynamic length)
29
What is wrong with the class now? void Test() { vector v; v.resize(100); vector w = v; } What is the result? void Test() { vector v, w; v.resize(100); w = v; } or
30
void Test() { vector v; v.resize(100); vector w = v; } stack:
31
m_n0 m_data0 void Test() { vector v; v.resize(100); vector w = v; } stack:
32
m_n100 m_data void Test() { vector v; v.resize(100); vector w = v; } stack: heap:
33
m_n100 m_data m_n100 m_data void Test() { vector v; v.resize(100); vector w = v; } stack: heap:
34
m_n100 m_data void Test() { vector v; v.resize(100); vector w = v; } stack: heap:
35
m_n100 m_data void Test() { vector v; v.resize(100); vector w = v; } stack: heap: PROGRAM CRASHES!
36
#9: CC and AO Problem is that default behavior performs shallow copy We need to perform deep copy Add copy constructor and assignment operator to perform deep copy
37
class vector { public: vector() : m_n(0), m_data(0) {} vector(int n) : m_n(n), m_data(0) {} ~vector() { free(m_data); }... }; Class (Old way: shallow copy default behavior)
38
class vector { public: vector() : m_n(0), m_data(0) {} vector(int n) : m_n(n), m_data(0) {} vector(const vector& v) : m_n(n), m_data(0) { resize(v.size()); for (int i=0 ; i<m_n ; i++) { SetValue( v.GetValue(i) ); } } operator=(const vector& v) { resize(v.size()); for (int i=0 ; i<m_n ; i++) { SetValue( v.GetValue(i) ); } } ~vector() { free(m_data); }... }; Class (New way: deep copy)
39
class vector { public: vector() : m_n(0), m_data(0) {} vector(int n) : m_n(n), m_data(0) {} vector(const vector& v) : m_n(n), m_data(0) { *this = v; } operator=(const vector& v) { resize(v.size()); for (int i=0 ; i<m_n ; i++) { SetValue( v.GetValue(i) ); } } ~vector() { free(m_data); }... }; Class (Even better – Plauger’s One Right Place)
40
class vector { public: vector() : m_n(0), m_data(0) {} vector(int n) : m_n(n), m_data(0) {} vector(const vector& v) : m_n(n), m_data(0) { *this = v; } operator=(const vector& v) { resize(v.size()); memcpy( m_data, v.m_data, m_n * sizeof(int)); } ~vector() { free(m_data); }... }; Class (.. and faster)
41
#10: operator overloading Now replace GetValue / SetValue element with bracket [] operator Replace both methods with a single method – how to do this? Hint: Do not worry about const yet
42
class vector { public: vector() : m_n(0), m_data(0) {} vector(int n) : m_n(n), m_data(0) {} vector(const vector& v); operator=(const vector& v); ~vector() { free(m_data); } int size(); void resize(int n) { realloc(m_data, n*sizeof(int)); } int GetValue(int i) { assert(); return m_data[i]; } void SetValue(int i, int a) { assert(); m_data[i] = a; } private: int* m_data; int m_n; }; Class (Old way: GetValue / SetValue)
43
class vector { public: vector() : m_n(0), m_data(0) {} vector(int n) : m_n(n), m_data(0) {} vector(const vector& v); operator=(const vector& v); ~vector() { free(m_data); } int size(); void resize(int n) { realloc(m_data, n*sizeof(int)); } int& operator[](int i) { assert(); return m_data[i]; } private: int* m_data; int m_n; }; Class (New way: bracket operator)
44
#11: const Now add const modifier to read-only functions Note that some methods will require two copies
45
class vector { public: vector() : m_n(0), m_data(0) {} vector(int n) : m_n(n), m_data(0) {} vector(const vector& v); operator=(const vector& v); ~vector() { free(m_data); } int size(); void resize(int n) { realloc(m_data, n*sizeof(int)); } int& operator[](int i) { assert(); return m_data[i]; } private: int* m_data; int m_n; }; Class (Old way: const not used)
46
class vector { public: vector() : m_n(0), m_data(0) {} vector(int n) : m_n(n), m_data(0) {} vector(const vector& v); operator=(const vector& v); ~vector() { free(m_data); } int size() const; void resize(int n) { realloc(m_data, n*sizeof(int)); } int operator[](int i) const { assert(); return m_data[i]; } int& operator[](int i) { assert(); return m_data[i]; } private: int* m_data; int m_n; }; Class (New way: const used for read-only methods)
47
Test: Which version gets called? void Test() { vector v(100); v[3] = 6; v[7] = 9; int a = v[8]; };
48
Test: Which version gets called? void Test() { const vector v(100); v[3] = 6; v[7] = 9; int a = v[8]; };
49
Test: Which version gets called? void Test(const vector& v) { v[3] = 6; v[7] = 9; int a = v[8]; };
50
Test: Which version gets called? void Test() { vector v(100); const vector& w = v; v[3] = 6; v[7] = 9; int a = v[8]; w[3] = 6; w[7] = 9; int b = w[8]; };
51
How does this change the test code?
52
class vector { public: vector(); vector(int n); vector(const vector& v); operator=(const vector& v); int size(); void resize(int n); int GetValue(int i); void SetValue(int i, int a); private: int* m_data; int m_n; }; ClassTest code void ComputeSum(int n) { vector v(n); int i; for (i=0 ; i<n ; i++) { v.SetValue(i, i); } int sum = 0; for (i=0 ; i<n ; i++) { sum += v.GetValue(i); } (Old way: GetValue / SetValue)
53
class vector { public: vector(); vector(int n); vector(const vector& v); operator=(const vector& v); int size(); void resize(int n); int operator[](int i) const; void operator[](int i); private: int* m_data; int m_n; }; ClassTest code void ComputeSum(int n) { vector v(n); int i; for (i=0 ; i<n ; i++) { v[i] = i; } int sum = 0; for (i=0 ; i<n ; i++) { sum += v[i]; } (New way: bracket operator)
54
#12: Put in namespace Put your class in a namespace called mystd
55
(Old way: no namespace) class vector { public: vector() : m_n(0), m_data(0) {} vector(int n) : m_n(n), m_data(0) {} vector(const vector& v); operator=(const vector& v); ~vector() { free(m_data); } int size() const; void resize(int n) { realloc(m_data, n*sizeof(int)); } int operator[](int i) const { assert(); return m_data[i]; } int& operator[](int i) { assert(); return m_data[i]; } private: int* m_data; int m_n; };
56
namespace mystd { class vector { public: vector() : m_n(0), m_data(0) {} vector(int n) : m_n(n), m_data(0) {} vector(const vector& v); operator=(const vector& v); ~vector() { free(m_data); } int size() const; void resize(int n) { realloc(m_data, n*sizeof(int)); } int operator[](int i) const { assert(); return m_data[i]; } int& operator[](int i) { assert(); return m_data[i]; } private: int* m_data; int m_n; }; (New way: class wrapped in namespace)
57
How does this change the test code?
58
class vector { public: vector(); vector(int n); vector(const vector& v); operator=(const vector& v); int size(); void resize(int n); int operator[](int i) const; void operator[](int i); private: int* m_data; int m_n; }; ClassTest code void ComputeSum(int n) { vector v(n); int i; for (i=0 ; i<n ; i++) { v[i] = i; } int sum = 0; for (i=0 ; i<n ; i++) { sum += v[i]; } (Old way: no namespace)
59
namespace mystd { class vector { public: vector(); vector(int n); vector(const vector& v); operator=(const vector& v); int size(); void resize(int n); int operator[](int i) const; void operator[](int i); private: int* m_data; int m_n; }; ClassTest code void ComputeSum(int n) { mystd::vector v(n); int i; for (i=0 ; i<n ; i++) { v[i] = i; } int sum = 0; for (i=0 ; i<n ; i++) { sum += v[i]; } (New way: namespace)
60
#13: Templates Now make the class templated, so that the array will hold any type
61
Old version: array of ints class vector { public: vector() : m_n(0), m_data(0) {} vector(int n) : m_n(0), m_data(0) { resize(n); } vector::vector(const vector& v) : m_n(0), m_data(0) { *this = v; } vector::operator=(const vector& v) { resize( v.size() ); memcpy( m_data, v.m_data, m_n * sizeof(T) ); } int vector::size() { return m_n; } void vector::resize(int n) { m_n = n; m_data = (int*) realloc(m_n * sizeof(int)); } int vector::operator[](int i) const { return m_data[i]; } int& vector::operator[](int i) { return m_data[i]; } private: int* m_data; int m_n; };
62
template class vector { public: vector() : m_n(0), m_data(0) {} vector(int n) : m_n(0), m_data(0) { resize(n); } vector::vector(const vector& v) : m_n(0), m_data(0) { *this = v; } vector::operator=(const vector& v) { resize( v.size() ); memcpy( m_data, v.m_data, m_n * sizeof(T) ); } int vector::size() { return m_n; } void vector::resize(int n) { m_n = n; m_data = (T*) realloc(m_n * sizeof(T)); } T vector::operator[](int i) const { return m_data[i]; } T& vector::operator[](int i) { return m_data[i]; } private: T* m_data; int m_n; }; New version: array of anything
63
How does this change the test code?
64
class vector { public: vector(); vector(int n); vector(const vector& v); operator=(const vector& v); int size(); void resize(int n); int operator[](int i) const; void operator[](int i); private: int* m_data; int m_n; }; ClassTest code void ComputeSum(int n) { vector v(n); int i; for (i=0 ; i<n ; i++) { v[i] = i; } int sum = 0; for (i=0 ; i<n ; i++) { sum += v[i]; } (Old way: array of ints)
65
template class vector { public: vector(); vector(int n); vector(const vector& v); operator=(const vector& v); int size(); void resize(int n); T operator[](int i) const; T operator[](int i); private: T* m_data; int m_n; }; ClassTest code void ComputeSum(int n) { vector v(n); int i; for (i=0 ; i<n ; i++) { v[i] = i; } int sum = 0; for (i=0 ; i<n ; i++) { sum += v[i]; } (New way: array of anything)
66
#14: Split into.h and.cpp files Move the implementation of all methods into a separate.cpp file But note in the real world the tradeoff between speed and code size, as well as ease of use
67
namespace mystd { class vector { public: vector(); vector(int n); vector(const vector& v); operator=(const vector& v); int size(); void resize(int n); int operator[](int i) const; void operator[](int i); private: int* m_data; int m_n; }; vector.h
68
namespace mystd { vector::vector() : m_n(0), m_data(0) {} vector::vector(int n) : m_n(0), m_data(0) { resize(n); } vector::vector(const vector& v) : m_n(0), m_data(0) { *this = v; } vector::operator=(const vector& v) { resize( v.size() ); memcpy( m_data, v.m_data, m_n * sizeof(int) ); } int vector::size() { return m_n; } void vector::resize(int n) { m_n = n; m_data = (int*) realloc(m_n * sizeof(int)); } int vector::operator[](int i) const { return m_data[i]; } int& vector::operator[](int i) { return m_data[i]; } }; vector.cpp
69
Next steps How would you add the other methods that we imagined during our design session? push, pop, insert_element, remove_element,...
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.