Classes with dynamic members
int x; void f() { x=10; } main() { x=0; f(); } ‘Class’ matters! int x; void f() { int x; x=10; } main() { x=0; f(); } class A { public int x; void f(); } main() { A a; a.x = 0; a.f(); } localglobalpublic Unstructured with everything ‘global’ Structured with ‘local’ variables, ‘global’ functions Object-oriented with ‘global’ objects
class A { public: void f(); int x; private: int y; } void A::f() { x=10; y=100; } int main() { A a; a.f(); cout << a.x << endl; cout << a.y << endl; // no!!! a.x = 1000; a.y = 10000; // no!!! } Public or private? Global objects member functions (global? or public to objects) member variables (local or private to member functions)
class A { public: A(); int f(); private: int x; } A::A() { x=0; } int A::f() { return x; } int main() { A a; cout << a.f(); // not a.x!!! A b(a); A c; c = a; // member-wise copy } Objects are calling member funtions (public interface) Member functions are using member variables (private data) Objects are not directly dependent on implementation (private data) Abstract Data Type: public function, private data
Static variables (objects)Dynamic variables (objects) A (direct) named memory locationA static part (pointer) + (indirect) nameless memory location (dynamic part) int a; a = 20; int* pa; pa = new int; *pa = 20; 20 a pa static dynamic Static and dynamic
Only static member variables At least one pointer member (I.e. dynamic variable) class A { public: A(); private: int x; } class B { public: B(); B(const B& b); ~B(); private: int* px; } (Must) a defaut value constructor A() for ‘copy constructor’ automatically privided No destructor ‘assignment’ automatically provided (Must) a defaut value constructor for (Must) redefine ‘copy constructor’ (Must) the destructor (Must) redefine ‘assignment’ (do later) initialisation Creation by new + initialiation classes
class B { public: B(); B(const B& b); ~B(); private: int* px; } B::B() { px = new int; *px = 0; } B::B(const B& b) { px = new int; (*px) = *(b.px); } B::~B() { delete px; }
All constructors/destructor have automatic behavior, they are called ‘implicitly’!!! (that’s why they have standard names!) Constructors are called when creating variables and other occasions: Destructors are called when the variables go out of scope A a; // implicitly call the default value constructor A::A(); A b(a); // implicitly call the copy constructor A::A(const A& a); Automatic behavior of constructors/destructor void somefunction() { B b; };
Make an Abstract Data Type with ‘dynamic’ class l One more example of ADT: l integer linked list using class l A class with dynamic objects: l Copy constructor l Destructor
bool listEmpty(NodePtr head) { } NodePtr addHead(NodePtr head, int newdata) { } int getHead(NodePtr head) { } NodePtr getRest(NodePtr head) { } // void delHead(NodePtr& Head){ // } linked lists: definition struct Node{ int data; Node* next; }; typedef Node* NodePtr; NodePtr head;
void main(){ NodePtr Head1=NULL, Head2 = NULL, Head; addHead(Head1, 50); addHead(Head1, 40); addHead(Head1, 30); addHead(Head1, 20); cout << "List 1: " << endl; DisplayList(Head1); cout << "Length of Head1 list: " << length(Head1) << endl; cout << "Recursive length of Head1 list: " << lengthRec(Head1) << endl; if(isPalindrome(Head1)) cout << "Head1 list is palindrome" << endl; else cout << "Head1 list is not palindrome" << endl; addHead(Head2, 25); addHead(Head2, 35); addHead(Head2, 45); addHead(Head2, 35); addHead(Head2, 25); cout << "List 2: " << endl; DisplayList(Head2); cout << "Length of Head2 list: " << length(Head2) << endl; cout << "Recursive length of Head2 list: " << lengthRec(Head2) << endl; if(isPalindrome(Head2)) cout << "Head2 list is palindrome" << endl; else cout << "Head2 list is not palindrome" << endl; Head = mergeLists(Head1, Head2); cout << "Merged List: " << endl; DisplayList(Head); cout << "Length of Merged list: " << length(Head) << endl; cout << "Recursive length of Merged list: " << lengthRec(Head) << endl; if(isPalindromeRec(Head)) cout << "Merged list is palindrome" << endl; else cout << "Merged list is not palindrome" << endl; cout << "check the list again:" << endl; DisplayList(Head); } Usage in ‘procedural way’:
struct Node{ public: int data; Node* next; }; typedef Node* Nodeptr; class listClass { public: listClass(); // constructor listClass(const listClass& list1); // copy constructor ~listClass(); // destructor bool empty() const; // boolean function void addHead(int newdata); // add to the head void delHead(); // delete the head int headElement() const; // access functions int length() const; // utility function void print() const; // output private: Nodeptr head; }; ‘old’ operations ‘new’ member functions
void main(){ listClass L; // constructor called automatically here for L L.print(); { } L.addHead(30); L.print(); { 30 } L.addHead(13); L.print(); { } L.addHead(40); L.print(); { } L.addHead(50); L.print(); { } listClass N(L); N.print(); { } listClass R; R.print(); { } if(R.empty()) cout << "List R empty" << endl; L.delHead(); L.print(); { } L.delHead(); L.print(); { } if(L.empty()) cout << "List L empty" << endl; else{ cout << "List L contains " << L.length() << " nodes" << endl; cout << "Head element of list L is: " << L.headElement() << endl; } } // destructor called automatically here for L Usage in ‘object way’:
listClass::listClass() { head = NULL; } bool listClass::empty() const { if(head==NULL) return true; else return false; } int listClass::headElement() const { if(head != NULL) return head->data; else{ cout << "error: trying to find head of empty list" << endl; exit(1); } Some simple member functions: Implementation
listClass::listClass(const listClass& list1) { head = NULL; Nodeptr cur = list1.head; while(cur != NULL) { // addEnd(cur->data); addHead(cur->data); // inverse list order cur = cur->next; } (explicitly defined) copy constructor: Call other member function!, so within ‘class’, still ‘procedural’.
Destructor: deallocation function listClass::~listClass() { Nodeptr cur; while(head!=NULL){ cur = head; head = head->next; delete cur; }
void listClass::addHead(int newdata) { Nodeptr newPtr = new Node; newPtr->data = newdata; newPtr->next = head; head = newPtr; } Adding an element to the head:
void listClass::delHead() { if(head != NULL){ Nodeptr cur = head; head = head->next; delete cur; } Deleting the head:
void listClass::print() const { cout << "{"; Nodeptr cur = head; while(cur != NULL){ cout data << " "; cur = cur->next; } cout << "}" << endl; } Print the list:
int listClass::length() const { int n=0; Nodeptr cur = head; while(cur != NULL){ n++; cur = cur->next; } return n; } Computing the number of elements of a given list: