Classes with dynamic members Class matters global local
Classes with dynamic members
‘Class’ matters! global local public class A { int x; void f() { x=10; int x; x=10; void f(); } public } } x=0; main() { f(); x=0; A a; f(); a. x = 0; main() { } a. f(); } Unstructured with everything ‘global’ Structured with ‘local’ variables, ‘global’ functions } Object-oriented with ‘global’ objects
Public or private? class A { public: int main() { void f(); A a; int x; a. f(); private: cout << a. x << endl; int y; } cout << a. y << endl; // no!!! void A: : f() { a. x = 1000; a. y = 10000; // no!!! x=10; y=100; } } Global objects member functions (global? or public to objects) member variables (local or private to member functions)
Abstract Data Type: public function, private data class A { int main() public: { A(); A a; int f(); cout << a. f(); // not a. x!!! private: A b(a); int x; A c; } c = a; // member-wise copy } A: : A() { x=0; } int A: : f() { return x; } Objects are calling member funtions (public interface) Member functions are using member variables (private data) Objects are not directly dependent on implementation (private data)
Static and dynamic Static variables (objects) Dynamic variables (objects) A (direct) named memory location A static part (pointer) + (indirect) nameless memory location (dynamic part) int a; int* pa; a = 20; pa = new int; *pa = 20; a 20 static 20 pa static dynamic
classes Only static member variables At least one pointer member (I. e. dynamic variable) class A { class B { public: B(); A(); B(const B& b); private: ~B(); int x; private: } int* px; } (Must) a defaut value constructor A() for initialisation (Must) a defaut value constructor for Creation by new + initialiation ‘copy constructor’ automatically privided (Must) redefine ‘copy constructor’ No destructor (Must) the destructor ‘assignment’ automatically provided (Must) redefine ‘assignment’ (do later)
class B { B: : B() { px = new int; public: *px = 0; B(); B(const B& b); } ~B(); private: B: : B(const B& b) { px = new int; int* px; (*px) = *(b. px); } } B: : ~B() { delete px; }
Automatic behavior of constructors/destructor 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: A a; // implicitly call the default value constructor A: : A(); A b(a); // implicitly call the copy constructor A: : A(const A& a); Destructors are called when the variables go out of scope void somefunction() { B b; };
A constructor is called when: l declare/define objects l pass by value l return by value X f(X x) { X a; return a; }
A function returning an object X f() { X x; return x; } ‘return x’ returns a temporay object ‘temp’ of class X by constructor (because x is a local object, and to be ‘destructed’ !)
Make an Abstract Data Type with ‘dynamic’ class l l One more example of ADT: l integer linked list using class A class with dynamic objects: l Copy constructor l Destructor
linked lists: definition struct Node{ int data; Node* next; }; bool list. Empty(Node* head) { } Node* add. Head(Node* head, int newdata) { Node* head; } int get. Head(Node* head) { } Node* get. Rest(Node* head) { } // void del. Head(Node*& Head){ // }
Usage in ‘procedural way’: void main(){ Node. Ptr Head 1=NULL, Head 2 = NULL, Head; add. Head(Head 1, 50); add. Head(Head 1, 40); add. Head(Head 1, 30); add. Head(Head 1, 20); cout << "List 1: " << endl; Display. List(Head 1); cout << "Length of Head 1 list: " << length(Head 1) << endl; cout << "Recursive length of Head 1 list: " << length. Rec(Head 1) << endl; if(is. Palindrome(Head 1)) cout << "Head 1 list is palindrome" << endl; else cout << "Head 1 list is not palindrome" << endl; add. Head(Head 2, 25); add. Head(Head 2, 35); add. Head(Head 2, 45); add. Head(Head 2, 35); add. Head(Head 2, 25); cout << "List 2: " << endl; Display. List(Head 2); cout << "Length of Head 2 list: " << length(Head 2) << endl; cout << "Recursive length of Head 2 list: " << length. Rec(Head 2) << endl; if(is. Palindrome(Head 2)) cout << "Head 2 list is palindrome" << endl; else cout << "Head 2 list is not palindrome" << endl; Head = merge. Lists(Head 1, Head 2); cout << "Merged List: " << endl; Display. List(Head); cout << "Length of Merged list: " << length(Head) << endl; cout << "Recursive length of Merged list: " << length. Rec(Head) << endl; if(is. Palindrome. Rec(Head)) cout << "Merged list is palindrome" << endl; else cout << "Merged list is not palindrome" << endl; cout << "check the list again: " << endl; Display. List(Head); }
struct Node{ public: int data; Node* next; }; class list. Class { public: list. Class(); list. Class(const list. Class& list 1); ~list. Class(); ‘new’ member functions // constructor // copy constructor // destructor bool empty() const; void add. Head(int newdata); void del. Head(); int head. Element() const; // // boolean function add to the head delete the head access functions int length() const; void print() const; // utility function // output private: Node* head; }; ‘old’ operations
Usage in ‘object way’: void main(){ list. Class L; L. print(); L. add. Head(30); L. print(); L. add. Head(13); L. print(); L. add. Head(40); L. print(); L. add. Head(50); L. print(); list. Class N(L); N. print(); } // constructor called automatically here for L { } { 30 } { 13 30 } { 40 13 30 } { 50 40 13 30 } { 30 13 40 50 } list. Class R; R. print(); { } if(R. empty()) cout << "List R empty" << endl; L. del. Head(); L. print(); { 40 13 30 } L. del. Head(); L. print(); { 13 30 } 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. head. Element() << endl; } // destructor called automatically here for L
Implementation Some simple member functions: list. Class: : list. Class() { head = NULL; } bool list. Class: : empty() const { if(head==NULL) return true; else return false; } int list. Class: : head. Element() const { if(head != NULL) return head->data; else{ cout << "error: trying to find head of empty list" << endl; exit(1); } }
(explicitly defined) copy constructor: list. Class: : list. Class(const list. Class& list 1) { head = NULL; Node* cur = list 1. head; while(cur != NULL) { // add. End(cur->data); add. Head(cur->data); // inverse list order cur = cur->next; } } Call other member function!, so within ‘class’, still ‘procedural’.
Destructor: deallocation function list. Class: : ~list. Class() { Node* cur; while(head!=NULL){ cur = head; head = head->next; delete cur; } }
Adding an element to the head: void list. Class: : add. Head(int newdata) { Node* new. Ptr = new Node; new. Ptr->data = newdata; new. Ptr->next = head; head = new. Ptr; }
Deleting the head: void list. Class: : del. Head() { if(head != NULL){ Node* cur = head; head = head->next; delete cur; } }
Print the list: void list. Class: : print() const { cout << "{"; Node* cur = head; while(cur != NULL){ cout << cur->data << " "; cur = cur->next; } cout << "}" << endl; }
Computing the number of elements of a given list: int list. Class: : length() const { int n=0; Node* cur = head; while(cur != NULL){ n++; cur = cur->next; } return n; }
- Slides: 22