Unit 33
Unit 33
Answer:
Constructors and destructors are fundamental to object-oriented
programming, as they manage the lifecycle of objects.
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.
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.
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.
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.
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
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
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.
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.
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++.