Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Unit 3

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 11

Inheritance

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
};

class Derived : public Base {


// Derived 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
};

class Derived : public Base1, public Base2 {


// Derived class code
};

3. Multilevel Inheritance
A derived class is created from another derived class, creating a chain.

class Base {
// Base class code
};

class Derived1 : public Base {


// Derived1 class code
};

class Derived2 : public Derived1 {


// Derived2 class code
};
4. Hierarchical Inheritance
Multiple classes inherit from a single base class.

class Base {
// Base class code
};

class Derived1 : public Base {


// Derived1 class code
};

class Derived2 : public Base {


// Derived2 class code
};

5. Hybrid Inheritance
A combination of two or more types of inheritance, like multiple and multilevel
inheritance.

Access Specifiers in 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).

class Derived : public Base {};

 Protected Inheritance
Public and protected members of the base class become protected in the derived class.

class Derived : protected Base {};

 Private Inheritance
Public and protected members of the base class become private in the derived class.

class Derived : private Base {};

Example Code

Here’s a simple example to illustrate inheritance:

#include <iostream>

using namespace std;

// 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.

Example of Ambiguity in Inheritance

Consider the classic "Diamond Problem" scenario:

#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.

Ways to Resolve 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;
}
};

class B : virtual public A {


// Virtual inheritance
};

class C : virtual public A {


// Virtual inheritance
};

class D : public B, public C {


// D has only one instance of A, solving the ambiguity
};

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.

3. Avoiding Ambiguity through Redesign Sometimes ambiguity can be avoided by


redesigning the class hierarchy. For example:
o Using Composition instead of Inheritance: If possible, use composition instead
of multiple inheritance. You can include objects of other classes as members,
avoiding inheritance issues.
o Separating Responsibilities: Ensure that classes represent distinct functionalities,
minimizing the need for multiple inheritance.
4. Overriding the Function in the Derived Class In some cases, you can define or
override the ambiguous function in the most derived class to resolve ambiguity.

class D : public B, public C {


public:
void display() {
A::display(); // or B::display() or C::display()
}
};

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.

Here are the primary types of relationships in OOP:

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

 Definition: A special form of association that represents a "has-a" relationship.


Aggregation implies a whole-part relationship, but the part can exist independently of the
whole.
 Example: A Library has a collection of Book objects, but Book objects can exist
independently of the Library.

class Book {
// Book details
};

class Library {
std::vector<Book*> books; // Aggregation, Library "has-a" collection of
Books
};

 Characteristics: In aggregation, the lifecycle of the part (e.g., Book) is independent of


the whole (e.g., Library).

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

 Definition: A relationship that represents an "is-a" relationship. Inheritance allows a class


(derived class) to inherit attributes and behaviors from another class (base class),
promoting code reuse.
 Example: A Dog is an Animal, so Dog can inherit from the Animal class.

class Animal {
// Animal details
};

class Dog : public Animal {


// Dog inherits from Animal
};

 Characteristics: Inheritance allows the derived class to extend or override the


functionality of the base class.

5. Dependency

 Definition: A temporary relationship where one class depends on another to function. It


is usually a "uses" relationship, where one class requires another to complete a specific
task but does not retain any long-term connection to it.
 Example: A Printer class may depend on a Document class to print a document.

class Document {
// Document details
};

class Printer {
public:
void print(Document doc) {
// Printer uses Document temporarily to print
}
};

 Characteristics: Dependency relationships are often transient and limited to a specific


function call or temporary collaboration between classes.

Summary

Relationship Description Example


Association "Uses-a" relationship Teacher and Student
Aggregation "Has-a" relationship Library and Book
Composition "Contains-a" relationship Car and Engine
Relationship Description Example
Inheritance "Is-a" relationship Dog and Animal
Dependency Temporary "uses" relationship Printer and Document

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.

Here’s a breakdown of both concepts:

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.

Key Characteristics of Abstract Classes

 Cannot Instantiate: You cannot create an instance of an abstract class.


 Contains Pure Virtual Functions: An abstract class must contain at least one pure virtual
function.
 Base Class for Inheritance: It is intended to be inherited, and derived classes must implement
the pure virtual functions.
 May Contain Implemented Methods: An abstract class can have some functions with
implementation and some without, which is useful for providing default behavior.

Defining an Abstract Class

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

void commonFunction() { // Regular function with implementation


cout << "This is a common function in Shape." << endl;
}
};

class Circle : public Shape {


public:
void draw() override { // Implementation of pure virtual function
cout << "Drawing a circle." << endl;
}
};

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:

 Shape is an abstract class because it has a pure virtual function draw.


 Circle is a concrete class because it provides an implementation of draw.

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.

Key Characteristics of Interfaces

 Purely Abstract: An interface in C++ has only pure virtual functions.


 No Implementation: Interfaces do not provide any implementation; they only specify what
functions must be implemented in derived classes.
 No Data Members: Typically, interfaces contain no data members to keep the focus solely on
behavior.

Defining an Interface

Here’s how you might define an interface in C++:

#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)
};

class Circle : public Drawable {


public:
void draw() override {
cout << "Drawing a circle." << endl;
}

void resize() override {


cout << "Resizing a circle." << endl;
}
};

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

Feature Abstract Class Interface

Instantiation Cannot be instantiated Cannot be instantiated

Contains
Can have some implemented methods Has no implemented methods
Implementation

Contains Data
Can have data members Typically does not have data members
Members

To provide a base class with common To enforce a set of functions that


Purpose
behavior and default functionality derived classes must implement

Best for classes with some common Best for defining behavior only, without
Usage
functionality and behavior any implementation

When to Use Abstract Classes vs Interfaces

 Use Abstract Classes when:


o You want to share code across related classes.
o You need a base class with default implementations for some methods.
o You need data members or constructors in the base class.

 Use Interfaces when:


o You only want to define a contract or set of behaviors without any shared
implementation.
o You need to support multiple inheritance of behavior across different class hierarchies.

You might also like