5 Object Programming Essentials
5 Object Programming Essentials
essentials
OBJECIFS
• 5.1 Basic concepts of object programming
• 5.2 A stack: a view from two different perspectives
• 5.3 Anatomy of the class
• 5.4 Static components
• 5.5 Objects vs. pointers and objects inside the objects
5.1 Basic concepts of object programming
class OurClass{ } ;
OurClass ourobject;
void push(int value) { the function that places a value onto the stack.
stack[SP++] = value;
}
int pop(void) { the function to take a value off the stack.
return stack[--SP];
}
#include <iostream>
using namespace std;
int stack[100]; 5.2.6 The stack in action
int SP = 0; 1. imagine that you’ve accidentally written something
void push(int value) {
little_stack.push(1);
another_stack.push(little_stack.pop() + 1);
funny_stack.push(another_stack.pop() + 2);
cout << funny_stack.pop() << endl;
return 0;
}
Try to modify the code and add a line like this to the main functions:
little_stack.SP++;
5.2.14 Stack from scratch (7)
We want a new class to handle stacks. We want the new class to be able
to evaluate a sum of all the elements currently stored on the stack.
We don’t want to modify the previously defined stack. We want a new
stack with new capabilities. In other words, we want to construct
a subclass of the Stack class.
class AddingStack : Stack{ };
The class doesn’t define any new component yet, but that doesn’t mean
that it’s empty. It derives all the components defined by its superclass –
the name of the superclass is written after the colon directly after the new
class name.
Any object of the AddingStack class can do everything that
each Stack class’ object does.
class AddingStack : Stack {
private: 5.2.15 Stack from scratch (8)
int sum; 1. We want the push function not only
public: to push the value onto the stack, but
also to add the value to
void push(int value); the sum variable
int pop(void); };
void AddingStack::push(int value) { 2. We want the pop function not only
sum += value; to pop the value off the stack, but
also to subtract the value from
Stack::push(value);} the sum variable
int AddingStack::pop(void) {
int val = Stack::pop(): There’s another thing that should be taken into consideration,
it’s about the initial value of the sum variable.
sum -= val;
return val; } AddingStack::AddingStack(void) : Stack() {
int AddingStack::getSum(void) { sum = 0;
return sum; } } Note the phrase “: Stack()”. It’s a request to invoke the superclass
constructor before the current constructor starts its work.
#include <iostream>
using namespace std;
5.2.21 Stack from scratch (14)
int main(void) {
AddingStack super_stack;
for(int i = 1; i < 10; i++)
super_stack.push(i);
cout << super_stack.getSum() << endl;
for(int i = 1; i < 10; i++)
super_stack.pop();
cout << super_stack.getSum() << endl;
return 0;
}
5.3 Anatomy of the class
class A {
5.3.1 Class components Type Var;
};
A class is an aggregate consisting of variables (also
called fields or properties) and functions
(sometimes called methods). Both variables and should be read as:
functions are class components.
class A {
class Class { private:
int value; Type Var;
all three components
void setVal(int value); are private. };
int getVal(void);
};
5.3.2 Access specifiers
The setVal and getVal components class Class {
are public – they’re accessible to
all users of the class.
public:
The value component is private – void setVal(int value);
it’s accessible only within the class. int getVal(void);
private:
int value;
};
5.3.3 Creating an object
Class the_object;
Any object of the class is equipped with all the components defined in
the class. This means that the_object object has the same three
components as its base class.
the_object.setVal(0);
the_object.value = 0;
5.3.5 "this" pointer
each object is equipped with a special component, its name is this: it’s a pointer to the
current object – each object has its own copy of the this pointer
class Class {
public:
Class(int val) { this -> value = val; }
void setVal(int value) { this -> value = value; }
int getVal(void) { return value; }
private:
int value;
};
#include <iostream>
using namespace std;
5.3.11 Copying constructors
class Class1 { Note that the keyword const used in the parameter
declaration is a promise that the function won’t attempt
public: to modify the values stored in the referenced object.
Class1(int val) { this -> value = val; }
Class1(Class1 const &source) { value = source.value + 100; }
int value; };
class Class2 { There is a special kind of
public: constructor intended to copy
Class2(int val) { this -> value = val; } one object into another.
int value; }; Constructors of this kind have
int main(void) {
one parameter referenced to
Class1 object11(100), object12 = object11;
an object of the same class
Class2 object21(200), object22 = object21;
cout << object12.value << endl;
and are used to copy all
cout << object22.value << endl; important data from the
return 0; source object to the newly
200
} 200
created object
What is a memory leak in C++? a leaky faucet
• A memory leak occurs when a piece (or pieces) of memory that was
previously allocated by a programmer is not properly deallocated by
the programmer. Even though that memory is no longer in use by the
program, it is still “reserved”, and that piece of memory can not be
used by the program until it is properly deallocated by the
programmer. That’s why it’s called a memory leak – because it’s like a
leaky faucet in which water is being wasted, only in this case it’s
computer memory. void memLeak( )
• Here is an example of a memory leak in C++: {
int *data = new int;
*data = 15;
}
#include <iostream>
using namespace std; 5.3.12 Memory leaks
class Class {
The object variable is an example of an “automatic variable”.
public: This means that the variable automatically finishes its life when the
Class(int val) { execution of the function containing the variable’s declaration ends.
value = new int[val];
cout << "Allocation (" << val << ") done." << endl;
} We can imagine that object creation consists
int *value; of two phases:
};
1. the object itself is created and a part of
void MakeALeak(void) { the memory is implicitly allocated to the
Class object(1000); object.
}
int main(void) { 2. the constructor explicitly allocates
MakeALeak();
another part of the memory
return 0;
}
5.3.13 Destructors
We can safeguard ourselves against this danger by defining a special function
called destructor. Destructors have the following restrictions:
• if a class is named X, its destructor is named ~X
• a class can have no more than one destructor
• a destructor must be a parameter-less function (note that the two last
restrictions are the same – can you explain why?)
• a destructor shouldn’t be invoked explicitly
#include <iostream>
using namespace std; 5.3.13 Destructors
class Class {
public:
Class(int val) {
value = new int[val];
cout << "Allocation (" << val << ") done." << endl; }
~Class(void) {
delete [] value;
cout << "Deletion done." << endl; }
int *value;
};
void MakeALeak(void) {
Class object(1000); }
int main(void) {
MakeALeak(); Allocation (1000) done.
Deletion done.
return 0; }
5.4 Static components
#include <iostream>
using namespace std;
5.4.1 The “auto” keyword (1)
void fun(void) {
The “auto” keyword came from the
auto int var = 99; ancestor of “C++”, the “C” programming
cout << "var = " << ++var << endl; language. The word has preserved its
original meaning.
} All the variables in your code belong to
int main(void) { one of two categories. They are:
1. automatic variables, created and
for(int i = 0; i < 5; i++) destroyed, sometimes repeatedly,
and automatically (hence their name)
fun(); during program execution.
return 0; 2. static variables, existing
continuously during the whole program
} var = 100 execution.
var = 100
var = 100
var = 100
var = 100
#include <iostream>
5.4.2 The “static” keyword (2)
using namespace std;
void fun(void) { • The code behaviour has been changed
static int var = 99; radically. The “var” variable is created
cout << "var = " << ++var << endl; and initiated once during the so-called
“program prologue” and is destroyed
} after program completion during the
int main(void) { operation of the so-called “program
epilogue”.
for(int i = 0; i < 5; i++)
fun(); • This means that the var variable exists
even when the fun function isn’t
return 0; var = 100
var = 101
working, so the variable’s value is
} var = 102
preserved between
var = 103 subsequent fun invocations.
var = 104
5.4.3 Instances of the class
Every object created from a particular class is
named a class’s instance. From this point of view,
#include <iostream> each instance of the class is a separate
using namespace std; universe and has nothing to do with any of the
class Class { remaining instances.
public:
int val;
void print(void) { cout << val << endl; }
};
int main(void) {
Class::val = 0;
is wrong and will cause a compilation error.
Class::print();
return 0; None of these components really exist until the
} val and print are non-static first instance is created. In consequence, we
mustn’t use any of the class components until
components of the class we’ve created an object of that class.
#include <iostream>
using namespace std; 5.4.4 Static components of the class
class Class {
static variables are not actually part of any object.
public:
static int Static;
int NonStatic; the variable NonStatic has to have both an
void print(void) { explicitly expressed separate definition and
cout << "Static = " << ++Static << a possible initialization, and both must be
placed outside the class definition.
", NonStatic = " << NonStatic << endl; ---------------------------------------------
} the definition has to be separate from the class body
}; because static variables are not actually part of any
int Class::Static = 0; Removing this line generates an error. object.
int main(void) {
Class instance1, instance2;
instance1.NonStatic = 10; • A static component exists throughout
the whole life of the program.
instance2.NonStatic = 20; Moreover, there is always only one
instance1.print(); component regardless of the number of
instances of the class.
instance2.print();
Static = 1, NonStatic = 10
• We can say that all the instances share
return 0; the same static components.
} Static = 2, NonStatic = 20
The unique traits of the static class variables predestine them to be used as counters of
instances of a particular class.
#include <iostream>
using namespace std;
5.4.5 Static class variables (1)
We’ll increment the Counter inside the Class constructor
class Class { and decrement it inside the Class destructor.
public:
static int Counter; int main(void) {
Class(void) { ++Counter; }; Class a;
~Class(void) { Class b;
--Counter; cout << Class::Counter << " instances so far"
if(Counter == 0) cout << "Bye, bye!" << endl;
<< endl; Class c;
}; Class d;
d.HowMany(); 2 instances so far
void HowMany(void) { cout << Counter << " 4 instances
instances" << endl; } return 0; Bye, bye!
}; }
int Class::Counter = 0; the Counter field is accessed directly when it’s being used inside the
class and with the “::” operator when it’s being used outside the class.
#include <iostream> 5.4.6 Static class variables (2)
using namespace std;
class Class { there are no obstacles to making a particular variable private and
static at the same time. This will obviously prevent direct access to
static int Counter; the variable
public:
Class(void) {
++Counter; int main(void) {
}; Class a;
~Class(void) { --Counter; if(Counter == 0) Class b;
cout << b.HowMany();
"Bye, bye!" << endl; Class c;
}; Class d;
void HowMany(void) { cout << Counter d.HowMany();
<< " instances" << endl; } return 0;
}; } 2 instances
Class::Counter = 1;
int Class::Counter = 0; are strictly prohibited.
4 instances
Bye, bye!
we want to protect the value against any unauthorized modification.
#include <iostream>
using namespace std; 5.4.7 Static class variables (3)
class Class {
It’s not only class variables that can be declared as
static int Counter; static – functions can be declared like this, too.
public:
Class(void) {
++Counter; int main(void) {
}; Class::HowMany();
~Class(void) { Class a; 0 instances
--Counter; Class b; 2 instances
4 instances
if(Counter == 0) b.HowMany(); Bye, bye!
cout << "Bye, bye!" << endl; Class c;
}; Class d;
static void HowMany(void) { d.HowMany();
cout << Counter << " instances" << endl; } return 0;
}; }
int Class::Counter = 0; that the static function may be invoked from inside the class, like this: HowMany();
or by using any of the existing instances, like this: b.HowMany();
5.4.8 Static vs. non-static components
accessed component
static non-static
accessing static OK FAIL
component non-static OK OK
5.5 Objects vs. pointers and objects inside the
objects
5.5.1 Pointers to objects
• So far we’ve treated objects like ordinary variables and assumed that an object is created in the
place where it is declared and destroyed when its declaration scope is exited. This is only one of
the many possible object incarnations.
• Objects may also exist as dynamically created and destroyed entities. In other words, objects may
appear on demand – when they’re needed – and vanish in the same way.
#include <iostream>
using namespace std; int main(void) {
class Class { Class *ptr;
public:
Class(void) { ptr = new Class();
cout << "Object constructed!" << endl;} delete ptr;
~Class(void) { return 0;
cout << "Object destructed!" << endl;} }
};
Object constructed!
Object destructed!
5.5.3. Pointers to functions
Member functions invoked for an object accessed through the
#include <iostream> pointer have to be accessed using the arrow operator, too.
using namespace std;
class Class {
public:
Class(void) { int main(void) {
cout << "Object constructed!" << endl; Class *ptr;
}
~Class(void) { ptr = new Class;
cout << "Object destructed!" << endl; ptr -> value = 1;
} ptr -> IncAndPrint();
void IncAndPrint(void) { delete ptr;
cout << "value = " << ++value << endl; return 0; Object constructed!
} } 2
int value; Object destructed!
};
#include <iostream>
using namespace std; 5.5.4 Selecting the constructor
class Class {
public:
Class(void) { cout << "Object constructed (#1)" << endl; }
Class(int v) { value = v; cout << "Object constructed (#2)" << endl; }
~Class(void) { cout << "Object destructed! val = " << value << endl; }
void IncAndPrint(void) { int main(void) {
cout << "value = " << ++value << endl; Class *ptr1, *ptr2;
} Object constructed (#1) ptr1 = new Class;
Object constructed (#2) ptr2 = new Class(2);
int value; value = 2 ptr1 -> value = 1;
value = 3
}; Object destructed! val = 3
ptr1 -> IncAndPrint();
ptr2 -> IncAndPrint();
Object destructed! val = 2
delete ptr2;
If a class has more than one constructor, one of them may be chosen during object delete ptr1;
creation. This is done by specifying the form of the parameter list associated with return 0;
the class name. }
#include <iostream> 5.5.5 Arrays of pointers to objects (1)
using namespace std;
The class offers us two methods for accessing the array.
class Array {
int *values; Array of 2 ints constructed.
int size; #1:100
#2:101
public: Array of 2 ints destructed. int main(void) {
Array(int siz) {
Array *arr = new Array(2);
size = siz; values = new int[size];
cout << "Array of " << size << " ints constructed." << endl;
for(int i = 0; i < 2; i++)
}
arr->Put(i, i + 100);
~Array(void) {
for(int i = 0; i < 2; i++)
delete [] values;
cout << "#" << i + 1
cout << "Array of " << size << " ints destructed." << endl;
<< ":" << arr->Get(i) << endl;
delete arr;
}
return 0;
int Get(int ix) { return values[ix]; }
}
void Put(int ix, int val) { values[ix] = val; }
};
#include <iostream> 5.5.6 Arrays of pointers to objects (2)
using namespace std;
int main(void) {
class Array { Array of 2 ints constructed.
Array of 2 ints constructed.
Array *arr[2] =
int *values;
#1:10; #1:11; { new Array(2), new Array(2) };
int size; #2:11; #2:12;
public: Array of 2 ints destructed.
Array of 2 ints destructed.
for(int i = 0; i < 2; i++)
Array(int siz) {
for(int j = 0; j < 2; j++)
size = siz; values = new int[size];
arr[i]->Put(j, j + 10 + i);
cout << "Array of " << size << " ints constructed." << endl;
for(int i = 0; i < 2; i++) {
}
for(int j = 0; j < 2; j++)
~Array(void) {
cout << "#" << i + 1
delete [] values;
<< ":" << arr[i]->Get(j) << "; ";
cout << "Array of " << size << " ints destructed." << endl;
cout << endl;
}
}
delete arr[0];
int Get(int ix) { return values[ix]; }
delete arr[1];
void Put(int ix, int val) { values[ix] = val; }
return 0;
};
There are no obstacles to gathering pointers to objects inside an array. }
#include <iostream> 5.5.7 Objects inside objects (1)
using namespace std; An object of any class may be the field of an object of any other class.
class Element { int main(void) {
int value; Collection coll; coll (visible at the main function level)
public:
int Get(void) { return value; } for(int i = 1; i <= 2; i++)
void Put(int val) { value = val; } coll.Put(i, i + 1);
for(int i = 1; i <= 2; i++)
};
cout << "Element #" << i << " = " << coll.Get(i) << endl;
class Collection { return 0;
Element el1, el2; }
public:
int Get(int elno) { return elno == 1 ? el1.Get() : el2.Get(); }
int Put(int elno, int val) { if(elno == 1) el1.Put(val); else el2.Put(val); }
}; Element #2 = 2
el1 and el2 (visible at the coll object level)
Element #3 = 3
#include <iostream> 5.5.8 Objects inside objects (2)
using namespace std;
The conclusion is: constructors from inner objects (objects stored inside other
class Element { objects) are invoked before the outer object’s constructors start their work.
int value;
public:
Element(void) { cout << "Element constructed!" << endl; }
int Get(void) { return value; }
void Put(int val) { value = val; } int main(void) {
}; Collection coll;
class Collection { return 0;
Element el1, el2; }
public: Element constructed!
Collection(void) { cout << "Collection constructed!" << endl; } Element constructed!
Collection constructed!
int Get(int elno) { return elno == 1 ? el1.Get() : el2.Get(); }
int Put(int elno, int val) { if(elno == 1) el1.Put(val); else el2.Put(val); }
};
#include <iostream> 5.5.9 Objects inside objects (3)
using namespace std; we’ve changed the form of the Element class’ constructor – before it
was a parameter-less function, now it needs one parameter of type int.
class Element {
int value;
public:
Element(int val) { value = val; cout << "Element(" << val << ") constructed!" << endl;
}
int Get(void) { return value; } In constructor 'Collection::Collection()':
void Put(int val) { value = val; } error: no matching function for call to 'Element::Element()'
}; The constructor invoked implicitly (sometimes called the default
class Collection { constructor) is the one which has no parameters.
int main(void) {
A a;
a. a. a = 0; a.b();
cout << a.b();
cout << a.a.b << endl;
return 0;
}
//What is the output of the following
program?
#include <iostream> Q6
using namespace std;
class A {
public :
int a;
A() { a = 1; }
A(int aa) { a = 2; }
A (A &aa) { a = 3; }
};
int main(void) {
A a(1),b(a);
cout << a.a + b.a << endl;
return 0;
}
//What is the output of the following program?
#include <iostream>
using namespace std;
int z = 1;
Q7
class A {
public :
int a ;
A() { a = 1; z++; }
A (A &aa) { a = 3; z++; }
~A ( ) { z--; }
};
void fun(void) {
A a, b (a) , c (b) ;
}
int main (void) {
cout << z << endl;
return 0;
}
//What is the output of the following
program?
#include <iostream> Q8
using namespace std;
class A {
public:
static int a;
A() { a = 1; a++; }
A (A &aa) { a++; }
};
int main (void) {
A a, b (a) , c (b) ;
cout << A.a << endl;
return 0;
}
//What is the output of the following
program?
#include <iostream> Q9
using namespace std;
class A {
public:
static int a;
A() { a = 0; a++; }
A (A &Saa) { a++; }
};
int A::a = 1;
int main(void) {
A a,b(a),c (b);
cout << a.a << endl;
return 0;
}
//What is the output of the follovving program?
#include <iostream>
using namespace std;
class A {
Q10
public :
int a;
A() { a = 0; }
A(int b) { a = b + 1; }
};
class B {
public :
A a;
B () : a (0) { }
};
int main(void) {
B *b = new B() ;
cout << b->a.a << endl;
return 0;
}
ASSESSMENT
Assessment
What happens when you attempt to compile and run the following code?
#include <iostream>
using namespace std; Q1
class A {
int data[3];
public:
int cnt;
void put(int v) {data[cnt++] = v; }
}; Select correct answer (single
int main() { choice)
A a; ○ Compilation fails
a.cnt = 0; ○ It prints 0
a.put(1);
a.put(1);
○ It prints 1
cout << a.cnt; ○ It prints 2
return 0;
}
2
What happens when you attempt to compile and run the following code?
#include <iostream>
using namespace std;
class A {
Q2
int cnt;
void put(int v) { cout << cnt++;}
}; Select correct answer (single choice)
int main() { ○ Compilation fails
A a; ○ It prints 0
a.cnt =0; ○ It prints 1
a.put(1); ○ It prints 2
a.put(1);
return 0;
}
err
What happens when you attempt to compile and run the following code?
#include <iostream> Q3
using namespace std;
class A {
public:
int cnt;
void put(int v);
Select correct answer (single choice)
};
○ Compilation fails
void A::put(int v) {cout << ++cnt; }
○ It prints 0
int main() {
○ It prints 1
A a[2];
○ It prints 2
a[0].cnt = 0; a[1].cnt = 1;
a[a[0].cnt].put(a[1].cnt);
return 0;
}
1
What happens when you attempt to compile and run the following code
#include <iostream>
Q4
using namespace std;
class A {
public:
float v;
float set(float v) { A::v += 1.0; return v; }
float set(void) { A::v = v + 1.0; return 0.0; }
float get(float v) { v+=A::v; return v;}
}; Select correct answer (single choice)
int main() { ○ Compilation fails
A a; ○ It prints 1
cout << a.get(a.set(a.set(a.set()))); ○ It prints 2
return 0; ○ It prints 3
3
}
What happens when you attempt to compile and run the following code?
#include <iostream>
using namespace std; Q5
class A {
public:
A() {v = 2.5;}
float v;
float set(float v) { A::v += 1.0; return v;}
float get(float v) { v+= A::v; return v;}
};
int main() { Select correct answer (single choice)
A a; ○ Compilation fails
a.A(); ○ It prints 1
cout << a.get(a.set(1. 5)); ○ It prints 5
return 0; ○ It prints 3
} err
What happens when you attempt to compile and run the following code?
#include <iostream> Q6
using namespace std;
class A {
public:
A() {v = 2.5;}
float v;
float set (float v) { A::v += 1.0; return v;}
float get(float v) { v += A::v; return v;}
};
Select correct answer (single choice)
int main() {
○ Compilation fails
A a;
○ It prints 1
cout << a.get(a.set(1.5));
○ It prints 5
return 0;
○ It prints 3
}
5
What happens when you attempt to compile and run the following code?
#include <iostream>
using namespace std; Q7
class A {
public:
A() {v = 2.5;}
A(float v) {A::v = v + 1.0; }
float v;
float set(float v) {A::v+= 1.0; return v;}
float get(float v) { v += A::v; return v;}
}; Select correct answer (single choice)
int main() { ○ Compilation fails
A a,b(1.0); ○ It prints 1
cout << a.get(b.set(1.5)); ○ It prints 2
return 0; ○ It prints 4
} 4
What happens when you attempt to compile and run the following code?
#include <iostream>
using namespace std; Q8
class A {
public:
A(A &a) {v = a.get(0.0); }
A(float v) {A::v= v;}
float v;
float set(float v) {A::v += v; return v;}
float get(float v) { return A::v + v;}
}; Select correct answer (single choice)
int main() { ○ Compilation fails
A *a = new A(1.0), *b = new A(*a); ○ It prints 1
cout << a->get(b->set(a->v)); ○ It prints 2
return 0; ○ It prints 4
} 2
What happens when you attempt to compile and run the following code?
#include <iostream>
using namespace std;
Q9
class A {
public:
A(A *v) {A::v = v; }
A(){A::v=1.0;}
float v;
float set(float v) { A::v = v; return v;}
float get(float v) { return A::v;}
}; Select correct answer (single choice)
int main(){ ○ Compilation fails
A a,*b = new A(a); ○ It prints 1
cout << a->get(b->set(a->v)); ○ It prints 2
return 0; ○ It prints 4
}
What happens when you attempt to compile and run the following code?
#include <iostream> Q10
using namespace std; int main() {
class A { B b(2.0);
public: cout << b.b;
float v; return 0;
A(float x) : v(x) {} }
};
class B {
public: Select correct answer (single choice)
A a; ○ Compilation fails
float b; ○ It prints 1
B(float x) : a(x + 1) { b = a.v;} ○ It prints 2
}; ○ It prints 3
3