Unit 3
Unit 3
Unit 3
Inheritance in C++ allows you to create a new class (derived class) based on an existing class
(base class), allowing the derived class to inherit attributes and behaviors (data members and
member functions) from the base class. This concept promotes code reuse and establishes a
hierarchical relationship between classes. Here's a breakdown of inheritance in C++:
Types of Inheritance
1. Single Inheritance
A derived class inherits from a single base class.
class Base {
// Base class code
};
2. Multiple Inheritance
A derived class inherits from more than one base class.
class Base1 {
// Base1 class code
};
class Base2 {
// Base2 class code
};
3. Multilevel Inheritance
A derived class is created from another derived class, creating a chain.
class Base {
// Base class code
};
class Base {
// Base class code
};
5. Hybrid Inheritance
A combination of two or more types of inheritance, like multiple and multilevel
inheritance.
The access specifier used when inheriting a base class affects the accessibility of its members in
the derived class:
Public Inheritance
Members keep their original access level (public remains public, protected remains
protected).
Protected Inheritance
Public and protected members of the base class become protected in the derived class.
Private Inheritance
Public and protected members of the base class become private in the derived class.
Example Code
#include <iostream>
// Base class
class Animal {
public:
void eat() {
cout << "Eating..." << endl;
}
};
// Derived class
class Dog : public Animal {
public:
void bark() {
cout << "Barking..." << endl;
}
};
int main() {
Dog myDog;
myDog.eat(); // Inherited from Animal
myDog.bark(); // Defined in Dog
return 0;
}
Ambiguity
Ambiguity occurs when there are multiple inheritance paths to a particular base class, making it
unclear which member of the base class should be used. This issue is common in multiple
inheritance, especially with the "Diamond Problem" in multilevel inheritance.
#include <iostream>
using namespace std;
class A {
public:
void display() {
cout << "Class A display function" << endl;
}
};
class B : public A {
// Class B inherits from A
};
class C : public A {
// Class C inherits from A
};
class D : public B, public C {
// Class D inherits from both B and C
};
int main() {
D obj;
obj.display(); // This line causes ambiguity
return 0;
}
In this code, class D inherits from both B and C, which in turn both inherit from A. So, D has two
paths to access A::display(), one through B and another through C, causing ambiguity.
1. Scope Resolution Operator You can specify the path explicitly by using the scope
resolution operator (::) to indicate which path to follow.
int main() {
D obj;
obj.B::display(); // Calls display() from B's instance of A
obj.C::display(); // Calls display() from C's instance of A
return 0;
}
While this resolves ambiguity, it is often a workaround and may not be ideal for larger
programs.
2. Virtual Inheritance Virtual inheritance is a common solution for the Diamond Problem.
By declaring the inheritance from the base class as virtual, only one instance of the
base class will be created and shared among derived classes.
class A {
public:
void display() {
cout << "Class A display function" << endl;
}
};
int main() {
D obj;
obj.display(); // No ambiguity, only one A instance
return 0;
}
With virtual inheritance, D has only one instance of A, resolving the ambiguity.
int main() {
D obj;
obj.display(); // Calls D's version, avoiding ambiguity
return 0;
}
By using these techniques, you can manage and resolve inheritance ambiguity in C++,
particularly in complex hierarchies with multiple inheritance paths.
Relationships
In Object-Oriented Programming (OOP), relationships between classes define how they interact
with or depend on each other. These relationships help in organizing code in a modular,
maintainable way and are the foundation for concepts like reusability, abstraction, and
encapsulation.
1. Association
Definition: A general relationship where one class uses another without implying
ownership. Association is often a "uses-a" relationship.
Example: A Teacher and a Student might have an association where a teacher teaches
students, and students attend classes from the teacher, but neither owns the other.
class Teacher {
// Teacher details
};
class Student {
Teacher* teacher; // Association between Student and Teacher
};
Types of Association:
o Unidirectional: One class knows about the other, but not vice versa.
o Bidirectional: Both classes know about each other.
2. Aggregation
class Book {
// Book details
};
class Library {
std::vector<Book*> books; // Aggregation, Library "has-a" collection of
Books
};
3. Composition
Definition: A strong form of aggregation where the part cannot exist independently of
the whole. It represents a "contains-a" relationship, and when the whole is destroyed, its
parts are also destroyed.
Example: A Car contains an Engine, and if the Car is destroyed, the Engine is also
destroyed.
class Engine {
// Engine details
};
class Car {
Engine engine; // Composition, Car "contains-a" Engine
};
Characteristics: In composition, the part (e.g., Engine) cannot exist independently of the
whole (e.g., Car).
4. Inheritance
class Animal {
// Animal details
};
5. Dependency
class Document {
// Document details
};
class Printer {
public:
void print(Document doc) {
// Printer uses Document temporarily to print
}
};
Summary
Each of these relationships provides a different way to structure and organize classes, enabling
you to build modular and maintainable systems in OOP.
Abstraction
interfaces and abstract classes are used to define classes that provide a blueprint for other
classes, especially when you want to establish a specific behavior that must be implemented by
derived classes. While C++ does not have a keyword for "interface" as in some other languages
(e.g., Java), interfaces can still be implemented through pure virtual functions.
1. Abstract Classes
An abstract class in C++ is a class that contains at least one pure virtual function. A pure virtual
function is a function that has no implementation in the abstract class itself and is meant to be
overridden in derived classes. Abstract classes are designed to be base classes and cannot be
instantiated directly.
A pure virtual function is declared by assigning 0 to the function prototype in the base class:
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw() = 0; // Pure virtual function
int main() {
// Shape shape; // Error: Cannot instantiate abstract class
Circle circle;
circle.draw(); // Calls Circle's implementation of draw()
circle.commonFunction(); // Calls commonFunction from Shape
return 0;
}
In this example:
2. Interfaces in C++
While C++ does not have a formal keyword for interfaces, an interface is typically represented
as an abstract class where all member functions are pure virtual functions, and it contains no data
members (or sometimes only constant data members). This approach provides a way to enforce
that derived classes implement specific behaviors without any shared implementation or data.
Defining an Interface
#include <iostream>
using namespace std;
class Drawable {
public:
virtual void draw() = 0; // Pure virtual function
virtual void resize() = 0; // Pure virtual function
virtual ~Drawable() = default; // Virtual destructor (optional)
};
int main() {
Circle circle;
circle.draw();
circle.resize();
return 0;
}
In this example:
Drawable serves as an interface because it only has pure virtual functions and no data
members.
Circle implements the Drawable interface by providing implementations for draw and
resize.
Abstract Class vs Interface: Key Differences
Contains
Can have some implemented methods Has no implemented methods
Implementation
Contains Data
Can have data members Typically does not have data members
Members
Best for classes with some common Best for defining behavior only, without
Usage
functionality and behavior any implementation