FAC1003 Tutorial 14 — OOP Solutions
Complete solutions for Tutorial 14 covering Object-Oriented Programming concepts, code tracing, and C++ applications.
Part 1: Concepts
Question 1: Define OOP and Contrast with Procedural Programming
Object-Oriented Programming (OOP) is a programming paradigm that organizes software design around objects rather than functions and logic. Objects are instances of classes that bundle data (attributes) and behavior (methods) together.
| Aspect | Procedural Programming | Object-Oriented Programming |
|---|---|---|
| Organization | Functions/procedures operating on data | Objects containing data + methods |
| Data Access | Global/shared data | Encapsulated (controlled access) |
| Reusability | Limited (function libraries) | High (inheritance, polymorphism) |
| Modularity | Function-based | Class-based |
| Real-world modeling | Process-oriented | Entity-oriented |
Key difference:
- Procedural:
dataandfunctionsare separate - OOP:
objectsencapsulate both data and the functions that operate on it
Question 2: Encapsulation with C++ Example
Encapsulation is the bundling of data and methods that operate on that data within a single unit (class), while restricting direct access to some components.
#include <iostream>
#include <string>
using namespace std;
class BankAccount {
private:
string accountNumber; // Hidden data
double balance; // Cannot be accessed directly
public:
// Constructor
BankAccount(string acc, double bal) {
accountNumber = acc;
balance = bal;
}
// Getter (controlled read access)
double getBalance() {
return balance;
}
// Setter with validation (controlled write access)
void deposit(double amount) {
if (amount > 0) {
balance += amount;
cout << "Deposited: " << amount << endl;
} else {
cout << "Invalid amount!" << endl;
}
}
void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
cout << "Withdrawn: " << amount << endl;
} else {
cout << "Insufficient funds or invalid amount!" << endl;
}
}
};
int main() {
BankAccount myAccount("ACC123", 1000.0);
// myAccount.balance = 5000; // ERROR: private member
myAccount.deposit(500); // OK: public method
myAccount.withdraw(200); // OK: public method
cout << "Current balance: " << myAccount.getBalance() << endl;
return 0;
}
Output:
Deposited: 500
Withdrawn: 200
Current balance: 1300
Question 3: Abstraction and Its Usefulness
Abstraction is the concept of hiding complex implementation details and showing only the essential features of an object.
Why it's useful:
- Simplifies complexity — Users interact with simple interfaces
- Reduces cognitive load — Focus on what, not how
- Improves maintainability — Implementation can change without affecting users
- Enables modularity — Clear separation of concerns
Example: A car driver uses the steering wheel and pedals (abstraction) without knowing how the engine works internally.
// Abstract class (interface)
class Vehicle {
public:
virtual void start() = 0; // Pure virtual — "what"
virtual void stop() = 0; // Implementation hidden — "how"
};
class Car : public Vehicle {
public:
void start() { cout << "Car: Ignition on, engine running" << endl; }
void stop() { cout << "Car: Braking, engine off" << endl; }
};
Question 4: Difference Between Inheritance and Polymorphism
| Inheritance | Polymorphism |
|---|---|
| "is-a" relationship | "many forms" capability |
| Child class inherits from parent | Same interface, different implementations |
| Creates class hierarchies | Enables runtime behavior variation |
| Code reuse mechanism | Flexibility mechanism |
Inheritance Example:
class Animal { // Base class
public:
void eat() { cout << "Animal eats" << endl; }
};
class Dog : public Animal { // Dog "is-a" Animal
public:
void bark() { cout << "Dog barks" << endl; }
};
Polymorphism Example:
class Shape {
public:
virtual double area() = 0; // Same interface
};
class Circle : public Shape {
double radius;
public:
double area() { return 3.14159 * radius * radius; } // Different implementation
};
class Rectangle : public Shape {
double width, height;
public:
double area() { return width * height; } // Different implementation
};
Question 5: The Four Pillars of OOP
| Pillar | Description | Purpose |
|---|---|---|
| 1. Encapsulation | Bundling data and methods; hiding internal state | Data protection, controlled access |
| 2. Abstraction | Hiding implementation complexity | Simplified interfaces |
| 3. Inheritance | Creating new classes from existing ones | Code reuse, hierarchy |
| 4. Polymorphism | Same interface, different implementations | Flexibility, extensibility |
Question 6: Class and Object with Analogies
| Class | Object | |
|---|---|---|
| Definition | Blueprint/template | Instance of a class |
| Analogy | Car blueprint | Your specific car |
| Analogy | Cookie cutter | The actual cookie |
| Analogy | House architectural plan | The built house |
| Memory | No memory allocated | Memory allocated when created |
class Student { // Class (blueprint)
string name;
int id;
public:
void study() { }
};
Student ali; // Object (instance)
Student sarah; // Another object
Real-life analogies:
- Class: Recipe for a cake
- Object: The actual cake you baked using that recipe
Question 7: Constructor and Destructor Roles
Constructor:
- Automatically called when object is created
- Initializes object state
- Same name as class, no return type
Destructor:
- Automatically called when object is destroyed
- Cleans up resources (memory, file handles)
- Same name as class with
~prefix
class MyClass {
int* data;
public:
// Constructor
MyClass(int size) {
data = new int[size]; // Allocate memory
cout << "Constructor: Memory allocated" << endl;
}
// Destructor
~MyClass() {
delete[] data; // Free memory
cout << "Destructor: Memory freed" << endl;
}
};
Question 8: Three Types of Constructors
| Type | Syntax | Purpose |
|---|---|---|
| 1. Default Constructor | ClassName() |
No arguments; creates object with default values |
| 2. Parameterized Constructor | ClassName(int x, int y) |
Accepts arguments; initializes with specific values |
| 3. Copy Constructor | ClassName(const ClassName& obj) |
Creates object as copy of existing object |
class Box {
int length, width;
public:
// 1. Default Constructor
Box() {
length = 0;
width = 0;
cout << "Default constructor called" << endl;
}
// 2. Parameterized Constructor
Box(int l, int w) {
length = l;
width = w;
cout << "Parameterized constructor called" << endl;
}
// 3. Copy Constructor
Box(const Box& b) {
length = b.length;
width = b.width;
cout << "Copy constructor called" << endl;
}
};
int main() {
Box b1; // Default constructor
Box b2(10, 20); // Parameterized constructor
Box b3 = b2; // Copy constructor
return 0;
}
Part 2: Code Tracing
Question 9: Inheritance Code Tracing
Code:
class Animal {
public:
void speak() { cout << "Animal speaks" << endl; }
};
class Dog : public Animal {
};
int main() {
Dog d;
d.speak();
return 0;
}
Trace:
Dog d;— Creates aDogobject. SinceDoginherits fromAnimal, it has access toAnimal's public members.d.speak();— Callsspeak()method.Dogdoesn't overridespeak(), so it usesAnimal's version.
Output:
Animal speaks
Question 10: Constructor/Destructor Tracing
Code:
class A {
public:
A() { cout << "Constructor"; }
~A() { cout << "Destructor"; }
};
int main() {
A obj;
return 0;
}
Trace:
A obj;— Objectobjis created → Constructor called immediately → Output:"Constructor"return 0;— Program ends,objgoes out of scope → Destructor called automatically → Output:"Destructor"
Output:
ConstructorDestructor
Note: Output appears on one line because no endl or \n is used.
Part 3: Applications
Question 11: Rectangle Class with Constructors and Destructor
Problem Requirements:
- Private members:
length,width - Public methods:
area(),perimeter() - Constructor to initialize
- Destructor to clean up
- Main program creates two rectangles
#include <iostream>
using namespace std;
class Rectangle {
private:
double length;
double width;
public:
// Constructor
Rectangle(double l, double w) {
length = l;
width = w;
cout << "Rectangle created (" << length << " x " << width << ")" << endl;
}
// Destructor
~Rectangle() {
cout << "Rectangle destroyed (" << length << " x " << width << ")" << endl;
}
// Calculate area
double area() {
return length * width;
}
// Calculate perimeter
double perimeter() {
return 2 * (length + width);
}
};
int main() {
// Create two rectangle objects
Rectangle rect1(5.0, 3.0);
Rectangle rect2(10.0, 4.0);
// Display area and perimeter for rect1
cout << "\nRectangle 1:" << endl;
cout << " Area: " << rect1.area() << endl;
cout << " Perimeter: " << rect1.perimeter() << endl;
// Display area and perimeter for rect2
cout << "\nRectangle 2:" << endl;
cout << " Area: " << rect2.area() << endl;
cout << " Perimeter: " << rect2.perimeter() << endl;
return 0;
}
Expected Output:
Rectangle created (5 x 3)
Rectangle created (10 x 4)
Rectangle 1:
Area: 15
Perimeter: 16
Rectangle 2:
Area: 40
Perimeter: 28
Rectangle destroyed (10 x 4)
Rectangle destroyed (5 x 3)
Question 12: Student Class with Getters/Setters and Average Calculation
Problem Requirements:
- Private members:
name,rollNumber,marks - Getter and setter methods
- Static/class method to calculate average marks
- Main program with array of students
#include <iostream>
#include <string>
using namespace std;
class Student {
private:
string name;
int rollNumber;
double marks;
public:
// Default constructor
Student() {
name = "";
rollNumber = 0;
marks = 0.0;
}
// Setters
void setName(string n) { name = n; }
void setRollNumber(int r) { rollNumber = r; }
void setMarks(double m) { marks = m; }
// Getters
string getName() { return name; }
int getRollNumber() { return rollNumber; }
double getMarks() { return marks; }
// Static method to calculate average
static double calculateAverage(Student students[], int size) {
double sum = 0;
for (int i = 0; i < size; i++) {
sum += students[i].getMarks();
}
return sum / size;
}
};
int main() {
const int NUM_STUDENTS = 3;
Student students[NUM_STUDENTS];
// Set values for each student
students[0].setName("Ali");
students[0].setRollNumber(101);
students[0].setMarks(85.5);
students[1].setName("Sarah");
students[1].setRollNumber(102);
students[1].setMarks(92.0);
students[2].setName("John");
students[2].setRollNumber(103);
students[2].setMarks(78.5);
// Display all students
cout << "Student Records:" << endl;
cout << "----------------" << endl;
for (int i = 0; i < NUM_STUDENTS; i++) {
cout << "Name: " << students[i].getName()
<< ", Roll: " << students[i].getRollNumber()
<< ", Marks: " << students[i].getMarks() << endl;
}
// Calculate and display average
double average = Student::calculateAverage(students, NUM_STUDENTS);
cout << "\nAverage Marks: " << average << endl;
return 0;
}
Expected Output:
Student Records:
----------------
Name: Ali, Roll: 101, Marks: 85.5
Name: Sarah, Roll: 102, Marks: 92
Name: John, Roll: 103, Marks: 78.5
Average Marks: 85.3333
Question 13: Abstract Shape Class with Polymorphism
Problem Requirements:
- Abstract
Shapeclass with pure virtual functions RectangleandCirclederived classes- Array of Shape pointers
- Calculate area and perimeter using virtual functions
#include <iostream>
#include <cmath>
using namespace std;
// Abstract base class
class Shape {
public:
virtual double area() = 0; // Pure virtual
virtual double perimeter() = 0; // Pure virtual
virtual ~Shape() {} // Virtual destructor
};
// Rectangle class
class Rectangle : public Shape {
private:
double length, width;
public:
Rectangle(double l, double w) : length(l), width(w) {}
double area() override {
return length * width;
}
double perimeter() override {
return 2 * (length + width);
}
};
// Circle class
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double area() override {
return M_PI * radius * radius;
}
double perimeter() override {
return 2 * M_PI * radius;
}
};
int main() {
const int NUM_SHAPES = 4;
Shape* shapes[NUM_SHAPES];
// Create objects and assign to shape pointers
shapes[0] = new Rectangle(5.0, 3.0);
shapes[1] = new Circle(4.0);
shapes[2] = new Rectangle(10.0, 2.0);
shapes[3] = new Circle(2.5);
// Display area and perimeter for each shape
cout << "Shape Calculations:" << endl;
cout << "===================" << endl;
for (int i = 0; i < NUM_SHAPES; i++) {
cout << "Shape " << (i + 1) << ":" << endl;
cout << " Area: " << shapes[i]->area() << endl;
cout << " Perimeter: " << shapes[i]->perimeter() << endl;
cout << endl;
}
// Clean up memory
for (int i = 0; i < NUM_SHAPES; i++) {
delete shapes[i];
}
return 0;
}
Expected Output:
Shape Calculations:
===================
Shape 1:
Area: 15
Perimeter: 16
Shape 2:
Area: 50.2655
Perimeter: 25.1327
Shape 3:
Area: 20
Perimeter: 24
Shape 4:
Area: 19.635
Perimeter: 15.708
Summary: Key OOP Concepts Tested
| Concept | Questions Covered |
|---|---|
| Encapsulation | Q2, Q11, Q12 |
| Inheritance | Q4, Q9, Q13 |
| Polymorphism | Q4, Q13 |
| Abstraction | Q3, Q13 |
| Constructors | Q7, Q8, Q11 |
| Destructors | Q7, Q10, Q11 |
| Getters/Setters | Q12 |
| Virtual Functions | Q13 |
Related
- FAC1003 - Programming II
- Object-Oriented Programming (concept page if exists)
- C++ Classes and Objects (concept page if exists)