Presentation is loading. Please wait.

Presentation is loading. Please wait.

Practice with C++: Writing a concrete class ECE 417/617: Elements of Software Engineering Stan Birchfield Clemson University.

Similar presentations


Presentation on theme: "Practice with C++: Writing a concrete class ECE 417/617: Elements of Software Engineering Stan Birchfield Clemson University."— Presentation transcript:

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

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

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

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

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

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

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

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

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

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

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

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

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

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,...


Download ppt "Practice with C++: Writing a concrete class ECE 417/617: Elements of Software Engineering Stan Birchfield Clemson University."

Similar presentations


Ads by Google