Lecture 30 - Nov 21, 2023
Last lecture
Inheritance continuation and polymorphism
Today
Dynamic memory allocation and polymorphism
Pointers to dynamic memory in derived class
When a class has members that point to dynamically allocated memory , we should have our own operator=
and copy constructor.
class Person {
private:
int age;
char* name;
public:
// asume Person() and Person(int, char*) are implemented
(const Person& original) {
Person= new char[strlen(original.name) + 1];
name (name, original.name);
strcpy= original.age;
age }
& operator=(const Person& rhs) {
Personif (&rhs == this) {
return *this;
}
delete[] name;
= new charr[strlen(rhs.name) + 1];
name (name, rhs.name);
strcpy= rhs.age;
age return *this;
}
~Person() {
delete[] name;
}
}
class Student : public Person {
private:
int ID;
public:
// assume Student() and Student(int, char*, int) are implemented
// You have to explicitly call the copy constructor
// Otherwise, the default copy constructor will be called
(const Student& original) : Person(original) {
Student= original.ID;
ID }
& operator=(const Student& rhs) {
Student// rhs is also Person we can pass it to operator= of Person
::operator=(rhs);
Person= rhs.ID;
ID return *this;
}
}
Dynamic versus static binding
class Polygon {
protected:
int width, height;
public:
void set(int w, int h) {
= w;
width = h;
height }
};
class Rectangle : public Polygon {
public:
int area() {
return width * height;
}
};
class Triangle : public Polygon {
public:
int area() {
return width * height / 2;
}
};
int main() {
;
Polygon p;
Rectangle r
= r; // p.operator = (s)
p // p = polygon
// s = rectangle
// ok
// Polygon& operator=(Polygon& rhs) { ... }
= p; // r.operator = (p)
r // r = rectangle
// p = polygon - not rectangle
// error
// important
// rectangle: polygon and rectangle too
// polygon: polygon only
* r1;
Rectangle* p1;
Polygon= &r;
pl
// p1 pointer can be used to access Polygon members which also exist in rectangle
<- set(3,4); // calls set of Polygon - ok
p1
<< p1 -> area(); // error as area() is a member of Rectangle, not Polygon
cout
= &p; // error
r1
// r1 pointer cannot access all Rectangle members as not all of them exist in
// Polygon p (base class)
}
Problem: we cannot access members of a Derived
object if the pointer pointing to it is of Base*
type.
Solution: virtual functions.
Virtual functions
If a function is declared as a virtual function in the base class, then redefined or overriden in a derived class, a class to that function through a Base*
pointer will invoke the function depending on the type of object (not pointer).
class Polygon {
protected:
int width, height;
public:
void set(int w, int h) {
= w;
width = h;
height }
virtual int area() {
return 0;
}
};
-> area(); // will invoke the area function of what p1 is pointing to
p1 // i.e., Rectangle = 12
// if we remove virtual
// p1 -> area() and p2 -> area() will both return 0
Non-virtual functions are invoked depending on the type of pointer. This is known at compile-time.
Virtual functions are invoked depending on the type the pointer points to. This is known at run-time.
A class that inherits a virtual function is called polymorphic class (e.g., Rectangle
and Triangle
).
Why is this helpful? We can have an array of Polygon*
and have different shapes on it.
for (int i = 0; i < 5; i++) {
<< a[i] -> area(); // will print out area according to shape
cout }