0% found this document useful (0 votes)
59 views14 pages

Pic Unit 2 and 3

The document provides an overview of inheritance in Object-Oriented Programming (OOP), explaining derived classes, forms of inheritance, and issues like ambiguity in multiple inheritance. It also covers concepts such as constructors, destructors, operator overloading, type conversion, and virtual functions, detailing their roles and implementations in C++. Overall, it emphasizes the importance of these features in promoting code reusability, polymorphism, and efficient resource management.

Uploaded by

n3223655
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
59 views14 pages

Pic Unit 2 and 3

The document provides an overview of inheritance in Object-Oriented Programming (OOP), explaining derived classes, forms of inheritance, and issues like ambiguity in multiple inheritance. It also covers concepts such as constructors, destructors, operator overloading, type conversion, and virtual functions, detailing their roles and implementations in C++. Overall, it emphasizes the importance of these features in promoting code reusability, polymorphism, and efficient resource management.

Uploaded by

n3223655
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 14

Inheritance is a key feature of Object-Oriented

Programming (OOP) that allows one class (derived class)


to inherit the properties (data members) and behaviors
(member functions) of another class (base class). It
promotes code reusability and establishes a hierarchical
relationship between classes.
Defining Derived Classes

A derived class is created using the syntax:


class DerivedClass : accessSpecifier
BaseClass {
// Body of the derived class
};
• Access Specifier: Determines the accessibility of
the inherited members. It can be:
o public: Public and protected members of

the base class remain public and protected in


the derived class.
o protected: Public and protected members

of the base class become protected in the


derived class.
o private: All inherited members become

private in the derived class.


Forms of Inheritance

1. Single Inheritance: A single derived class inherits


from one base class.
class A { /* Base Class */ };
class B : public A { /* Derived
Class */ };
2. Multiple Inheritance: A derived class inherits
from more than one base class.
class A { /* Base Class */ };
class B { /* Base Class */ };
class C : public A, public B { /*
Derived Class */ };
3. Multilevel Inheritance: A class is derived from
another derived class.
class A { /* Base Class */ };
class B : public A { /*
Intermediate Class */ };
class C : public B { /* Derived
Class */ };
4. Hierarchical Inheritance: Multiple classes are
derived from a single base class.
class A { /* Base Class */ };
class B : public A { /* Derived
Class */ };
class C : public A { /* Derived
Class */ };
5. Hybrid Inheritance: A combination of two or
more types of inheritance.
Ambiguity in Multiple and Multipath Inheritance

When a derived class inherits from multiple base classes


or when a class is derived from multiple paths in the
inheritance hierarchy, ambiguity can occur.
Example (Multiple Inheritance Ambiguity):
class A {
public:
void show() { cout << "Class A"; }
};
class B {
public:
void show() { cout << "Class B"; }
};
class C : public A, public B {
// Ambiguity: show() is present in
both A and B
};
To resolve:
c.B::show(); // Explicitly specify
which base class's method to call.
Example (Multipath Inheritance Ambiguity):

When a derived class inherits from the same base class


via multiple paths:
class A { /* Base Class */ };
class B : public A { /* Intermediate
Class */ };
class C : public A { /* Intermediate
Class */ };
class D : public B, public C { /*
Derived Class */ };
// A's members are inherited twice.
Solution: Use a virtual base class.
Virtual Base Class

A virtual base class ensures that only one copy of the base
class's members is inherited, regardless of the inheritance
paths.
class A { /* Base Class */ };
class B : virtual public A { /*
Intermediate Class */ };
class C : virtual public A { /*
Intermediate Class */ };
class D : public B, public C { /*
Derived Class */ };
Object Slicing

Object slicing occurs when a derived class object is


assigned to a base class object, causing the derived class's
specific attributes to be "sliced off."
class Base {
int a;
};
class Derived : public Base {
int b;
};
Base obj1;
Derived obj2;
obj1 = obj2; // b is "sliced off."
Overriding Member Functions

When a derived class provides a new definition for a base


class's function, it overrides the base class's version. Use
the virtual keyword to achieve polymorphism.
class Base {
public:
virtual void display() { cout <<
"Base class"; }
};
class Derived : public Base {
public:
void display() override { cout <<
"Derived class"; }
};
Object Composition and Delegation

• Composition: A class contains objects of other


classes as members, promoting a "has-a"
relationship.
• Delegation: A class delegates a task to another
class, using composition or aggregation.
Order of Execution of Constructors and Destructors

• Constructors: Base class constructors are


executed first, followed by derived class
constructors.
• Destructors: Derived class destructors are
executed first, followed by base class destructors.

Pointers and Dynamic Memory Management


Declaring and Initializing Pointers

A pointer is a variable that stores the memory address of


another variable.
int a = 10;
int *ptr = &a; // Pointer to an
integer
Accessing Data through Pointers
cout << *ptr; // Dereferencing the
pointer to access the value of 'a'
Pointer Arithmetic

Pointer arithmetic allows navigation through memory:


int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;
ptr++; // Moves to the next integer (4
bytes for int)
Memory Allocation

1. Static Memory Allocation: Memory is allocated


at compile-time.
2. Dynamic Memory Allocation: Memory is
allocated at runtime using new and deallocated
using delete.
Dynamic Memory Management
int *ptr = new int; // Allocates
memory for an integer
*ptr = 20;
delete ptr; // Deallocates
memory
For Arrays:
int *arr = new int[5]; // Allocates
memory for an array
delete[] arr; // Deallocates
array memory
Pointer to an Object

You can create a pointer to an object and access its


members using ->.
class A {
int x;
public:
void display() { cout << x; }
};
A *obj = new A();
obj->display();
this Pointer

The this pointer is an implicit pointer available in all


non-static member functions of a class. It points to the
current object.
Pointer-Related Problems

1. Dangling/Wild Pointers: A pointer that points to


deallocated memory or an uninitialized address.
int *ptr = new int;
delete ptr;
cout << *ptr; // Dangling pointer
2. Null Pointer Assignment: Assign nullptr to
pointers to avoid issues with invalid memory.
int *ptr = nullptr;
3. Memory Leak: Occurs when dynamically
allocated memory is not deallocated.
int *ptr = new int;
// Memory leak: no delete statement
4. Allocation Failure: Handle failures using
exception handling or by checking the returned
pointer.
int *ptr = new (nothrow) int;
if (!ptr) { cout << "Memory
allocation failed"; }
Question 1: Explain the need for constructors and destructors, and
differentiate between dynamic constructors, explicit constructors, and
destructors. (5 Marks)

Answer:
Constructors and destructors are fundamental to object-oriented programming, as they manage
the lifecycle of objects.

Need for Constructors:


A constructor is a special member function automatically invoked when an object is created. It
initializes object members and ensures they are in a valid state. Constructors save effort by
automating the initialization process, reducing the need for manual setup. For example,
constructors can set default values, allocate resources, or establish object-specific configurations.
Without constructors, programmers would need to write initialization functions, increasing code
redundancy and complexity.

Need for Destructors:


Destructors are automatically invoked when an object goes out of scope or is explicitly deleted.
They clean up resources such as memory, file handles, or network connections. Without
destructors, memory leaks or resource mismanagement can occur, causing system instability.
Destructors provide a mechanism to release resources predictably and automatically.

Dynamic Constructors:
Dynamic constructors involve the allocation of memory at runtime using new. These constructors
are beneficial when the size or nature of the object depends on runtime inputs. For instance, a
class managing a dynamic array can use a dynamic constructor to allocate memory based on user
input.

Explicit Constructors:
Explicit constructors prevent implicit type conversions, ensuring stricter type safety. For
example, without marking a constructor as explicit, an integer value might implicitly convert
to an object, leading to unexpected behavior. Explicit constructors require an explicit call, which
improves code clarity and prevents unintended conversions.

Destructors:
Destructors are defined using a tilde (~) followed by the class name. They release resources and
perform cleanup when the object is destroyed. For instance, if a class allocates memory
dynamically, the destructor should free this memory to prevent leaks. Unlike constructors,
destructors cannot have parameters or be overloaded, ensuring predictable behavior.

In summary, constructors initialize objects, and destructors clean them up. Dynamic constructors
allow flexible resource allocation, while explicit constructors enforce stricter type control.
Together, they ensure efficient resource management and object lifecycle handling in object-
oriented programming.
Question 2: Explain operator overloading and the rules for overloading
operators. Discuss with examples how operators like + and [] can be
overloaded. (5 Marks)

Answer:
Operator Overloading:
Operator overloading allows customizing the behavior of operators (+, -, [], etc.) for user-
defined types (e.g., classes). It makes code intuitive by enabling operators to work seamlessly
with objects as they do with basic types. For example, the + operator can be overloaded to add
two complex numbers.

Rules for Overloading Operators:

1. Only Certain Operators Can Be Overloaded: Some operators (e.g., ::, sizeof, .*)
cannot be overloaded.
2. Maintain Logical Consistency: Overloaded operators should mimic the expected
behavior. For example, if + is overloaded, it should perform addition-like
functionality.
3. At Least One Operand Must Be a User-Defined Type: This ensures meaningful
customization of the operator for specific data types.
4. Cannot Change Operator Precedence or Associativity: Overloading does not alter
how operators are evaluated in expressions.
5. Return Type and Parameters: Overloaded operators are implemented as member
or friend functions. They can take arguments and return values based on the desired
functionality.

Overloading +:
The + operator can be overloaded for a class Complex to add two complex
numbers.

class Complex {
int real, imag;
public:
Complex(int r = 0, int i = 0) : real(r), imag(i) {}
Complex operator+(const Complex& c) {
return Complex(real + c.real, imag + c.imag);
}
};

Overloading []:
The [] operator can be overloaded to access elements in a class managing an
array.
class Array {
int* arr;
int size;
public:
Array(int s) : size(s) { arr = new int[s]; }
int& operator[](int index) { return arr[index]; }
~Array() { delete[] arr; }
};

Summary:
Operator overloading enhances code readability and usability by extending
operators to user-defined types. By following rules and implementing them
logically, operators like + and [] can perform custom tasks on objects,
making code more intuitive.

Question 3: Explain type conversion in C++ and describe how to convert basic
types to class types, class types to basic types, and one class type to another.
Provide examples. (5 Marks)

Answer:
Type Conversion in C++:
Type conversion allows converting data between different types. In object-oriented
programming, this includes conversions between basic types (e.g., int, float) and user-defined
class types, as well as between different class types.

1. Basic Type to Class Type:


This conversion is achieved using constructors. A constructor with a single
parameter of the basic type can convert the basic type to an object.

class Distance {
int meters;
public:
Distance(int m) : meters(m) {} // Constructor for conversion
void display() { std::cout << meters << " meters"; }
};
Distance d = 10; // Converts integer to Distance object

2. Class Type to Basic Type:


This conversion is implemented using a member function, often marked explicit
to avoid implicit conversions.

class Distance {
int meters;
public:
Distance(int m) : meters(m) {}
explicit operator int() { return meters; } // Conversion to int
};
Distance d(15);
int meters = (int)d; // Converts Distance to integer

3. Class Type to Another Class Type:


This conversion can be achieved using a conversion constructor in the target class
or a member function in the source class.

class Distance {
int meters;
public:
Distance(int m) : meters(m) {}
int getMeters() { return meters; }
};
class Length {
int centimeters;
public:
Length(Distance d) : centimeters(d.getMeters() * 100) {} // Conversion
constructor
};
Distance d(5);
Length l = d; // Converts Distance to Length

Summary:
Type conversion enhances interoperability between types. Converting basic types to class types
simplifies object creation, converting class types to basic types provides data in primitive forms,
and converting between class types allows seamless transitions between related objects.

Question 4: Define virtual functions and explain the concept of early binding,
late binding, pure virtual functions, abstract classes, and virtual destructors
with examples. (5 Marks)

Answer:
Virtual Functions:
Virtual functions enable polymorphism by allowing derived classes to override base class
methods dynamically. They are declared using the virtual keyword in the base class. When a
derived class object is accessed through a base class pointer, the overridden function is called
dynamically, based on the actual object type.

class Base {
public:
virtual void display() { std::cout << "Base class"; }
};
class Derived : public Base {
public:
void display() override { std::cout << "Derived class"; }
};
Base* obj = new Derived();
obj->display(); // Calls Derived class function

Early Binding:
In early binding (compile-time binding), the function to be called is determined at compile time,
based on the pointer or reference type.

Late Binding:
In late binding (runtime binding), the function call is resolved at runtime, based on the actual
type of the object. Virtual functions enable late binding.

Pure Virtual Functions and Abstract Classes:


A pure virtual function is declared using = 0. It makes the class abstract, meaning the class
cannot be instantiated. Derived classes must override all pure virtual functions.

class Abstract {
public:
virtual void func() = 0; // Pure virtual function
};
class Concrete : public Abstract {
public:
void func() override { std::cout << "Implemented in derived"; }
};

Virtual Destructors:
Virtual destructors ensure proper cleanup of derived objects when deleted through a base class
pointer. Without them, only the base class destructor is called, leading to resource leaks.

class Base {
public:
virtual ~Base() { std::cout << "Base destructor"; }
};
class Derived : public Base {
public:
~Derived() { std::cout << "Derived destructor"; }
};
Base* obj = new Derived();
delete obj; // Calls both destructors

Summary:
Virtual functions, along with late binding, enable polymorphism. Abstract classes provide a
blueprint for derived classes, and virtual destructors ensure complete cleanup. Together, they
form the foundation of runtime polymorphism in C++.

You might also like