renaming
This commit is contained in:
committed by
JamesFlare1212
parent
9096194d90
commit
21fa2c03de
BIN
lectures/26_inheritance_II/Note_multipleInheritance.png
Normal file
BIN
lectures/26_inheritance_II/Note_multipleInheritance.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 398 KiB |
339
lectures/26_inheritance_II/README.md
Normal file
339
lectures/26_inheritance_II/README.md
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
# Lecture 26 --- C++ Inheritance and Polymorphism
|
||||||
|
|
||||||
|
## Today’s Lecture
|
||||||
|
|
||||||
|
- Inheritance is a relationship among classes. Examples: bank accounts, polygons, stack & list
|
||||||
|
- Basic mechanisms of inheritance
|
||||||
|
- Types of inheritance
|
||||||
|
- Is-A, Has-A, As-A relationships among classes.
|
||||||
|
- Polymorphism
|
||||||
|
|
||||||
|
## 26.1 Motivating Example: Bank Accounts
|
||||||
|
|
||||||
|
- Consider different types of bank accounts:
|
||||||
|
– Savings accounts
|
||||||
|
– Checking accounts
|
||||||
|
– Time withdrawal accounts (like savings accounts, except that only the interest can be withdrawn)
|
||||||
|
- If you were designing C++ classes to represent each of these, what member functions might be repeated among the different classes? What member functions would be unique to a given class?
|
||||||
|
- To avoid repeating common member functions and member variables, we will create a class hierarchy, where the common members are placed in a base class and specialized members are placed in derived classes.
|
||||||
|
|
||||||
|
## 26.2 Accounts Hierarchy
|
||||||
|
|
||||||
|
- Account is the base class of the hierarchy.
|
||||||
|
- SavingsAccount is a derived class from Account. SavingsAccount has inherited member variables & functions
|
||||||
|
and ordinarily-defined member variables & functions.
|
||||||
|
- The member variable balance in base class Account is protected, which means:
|
||||||
|
– balance is NOT publicly accessible outside the class, but it is accessible in the derived classes.
|
||||||
|
– if balance was declared as private, then SavingsAccount member functions could not access it.
|
||||||
|
- When using objects of type SavingsAccount, the inherited and derived members are treated exactly the same
|
||||||
|
and are not distinguishable.
|
||||||
|
- CheckingAccount is also a derived class from base class Account.
|
||||||
|
- TimeAccount is derived from SavingsAccount. SavingsAccount is its base class and Account is its indirect base class
|
||||||
|
|
||||||
|
## 26.3 Exercise: Draw the Accounts Class Hierarchy
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
|
// Note we've inlined all the functions (even though some are > 1 line of code)
|
||||||
|
class Account {
|
||||||
|
public:
|
||||||
|
Account(double bal = 0.0) : balance(bal) {}
|
||||||
|
void deposit(double amt) { balance += amt; }
|
||||||
|
double get_balance() const { return balance; }
|
||||||
|
protected:
|
||||||
|
double balance; // account balance
|
||||||
|
};
|
||||||
|
class SavingsAccount : public Account {
|
||||||
|
public:
|
||||||
|
SavingsAccount(double bal = 0.0, double pct = 5.0) : Account(bal), rate(pct/100.0) {}
|
||||||
|
double compound() { // computes and deposits interest
|
||||||
|
double interest = balance * rate;
|
||||||
|
balance += interest;
|
||||||
|
return interest;
|
||||||
|
}
|
||||||
|
double withdraw(double amt) { // if overdraft ==> return 0, else return amount
|
||||||
|
if (amt > balance) {
|
||||||
|
return 0.0;
|
||||||
|
}else {
|
||||||
|
balance -= amt;
|
||||||
|
return amt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
double rate; // periodic interest rate
|
||||||
|
};
|
||||||
|
class CheckingAccount : public Account {
|
||||||
|
public:
|
||||||
|
CheckingAccount(double bal = 0.0, double lim = 500.0, double chg = 0.5) : Account(bal), limit(lim), charge(chg) {}
|
||||||
|
double cash_check(double amt) {
|
||||||
|
assert (amt > 0);
|
||||||
|
if (balance < limit && (amt + charge <= balance)) {
|
||||||
|
balance -= amt + charge;
|
||||||
|
return amt + charge;
|
||||||
|
} else if (balance >= limit && amt <= balance) {
|
||||||
|
balance -= amt;
|
||||||
|
return amt;
|
||||||
|
} else {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
double limit; // lower limit for free checking
|
||||||
|
double charge; // per check charge
|
||||||
|
};
|
||||||
|
class TimeAccount : public SavingsAccount {
|
||||||
|
public:
|
||||||
|
TimeAccount(double bal = 0.0, double pct = 5.0) : SavingsAccount(bal, pct), funds_avail(0.0) {}
|
||||||
|
// redefines 2 member functions from SavingsAccount
|
||||||
|
double compound() {
|
||||||
|
double interest = SavingsAccount::compound();
|
||||||
|
funds_avail += interest;
|
||||||
|
return interest;
|
||||||
|
}
|
||||||
|
double withdraw(double amt) {
|
||||||
|
if (amt <= funds_avail) {
|
||||||
|
funds_avail -= amt;
|
||||||
|
balance -= amt;
|
||||||
|
return amt;
|
||||||
|
} else {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double get_avail() const { return funds_avail; };
|
||||||
|
protected:
|
||||||
|
double funds_avail; // amount available for withdrawal
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
Account a(100); //<---one balance member, not related to c1
|
||||||
|
CheckingAccount c1(100, 366, 0.4); //c1 has it's own CheckingAccount + Account objects <---one balance member
|
||||||
|
```
|
||||||
|
|
||||||
|
## 26.4 Constructors and Destructors
|
||||||
|
|
||||||
|
- Constructors of a derived class call the base class constructor immediately, before doing ANYTHING else.
|
||||||
|
The only thing you can control is which constructor is called and what the arguments will be. Thus when
|
||||||
|
a TimeAccount is created 3 constructors are called: the Account constructor, then the SavingsAccount
|
||||||
|
constructor, and then finally the TimeAccount constructor.
|
||||||
|
- The reverse is true for destructors: derived class destructors do their jobs first and then base class destructors
|
||||||
|
are called at the end, automatically. Note: destructors for classes which have derived classes must be marked
|
||||||
|
virtual for this chain of calls to happen.
|
||||||
|
|
||||||
|
## 26.5 Overriding Member Functions in Derived Classes
|
||||||
|
|
||||||
|
- A derived class can redefine member functions in the base class. The function prototype must be identical, not even the use of const can be different.
|
||||||
|
- For example, see TimeAccount::compound and TimeAccount::withdraw.
|
||||||
|
- Once a function is redefined it is not possible to call the base class function, unless it is explicitly called. As an example, the call to SavingsAccount::compound inside of TimeAccount::compound.
|
||||||
|
|
||||||
|
## 26.6 Public, Private and Protected Inheritance
|
||||||
|
|
||||||
|
- Notice the line
|
||||||
|
```cpp
|
||||||
|
class Savings_Account : public Account {
|
||||||
|
```
|
||||||
|
This specifies that the member functions and variables from Account do not change their public, protected or private status in SavingsAccount. This is called public inheritance.
|
||||||
|
- protected and private inheritance are other options:
|
||||||
|
|
||||||
|
– With protected inheritance, public members becomes protected and other members are unchanged
|
||||||
|
|
||||||
|
– With private inheritance, all members become private.
|
||||||
|
|
||||||
|
## 26.7 Stack Inheriting from List
|
||||||
|
|
||||||
|
- For another example of inheritance, let’s re-implement the stack class as a derived class of std::list:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
template <class T>
|
||||||
|
class stack : private std::list<T> {
|
||||||
|
public:
|
||||||
|
stack() {}
|
||||||
|
stack(stack<T> const& other) : std::list<T>(other) {}
|
||||||
|
~stack() {}
|
||||||
|
void push(T const& value) { this->push_back(value); }
|
||||||
|
void pop() { this->pop_back(); }
|
||||||
|
T const& top() const { return this->back(); }
|
||||||
|
int size() { return std::list<T>::size(); }
|
||||||
|
bool empty() { return std::list<T>::empty(); }
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
- Private inheritance hides the std::list<T> member functions from the outside world. However, these member functions are still available to the member functions of the stack<T> class.
|
||||||
|
- Note: no member variables are defined — the only member variables needed are in the list class.
|
||||||
|
- When the stack member function uses the same name as the base class (list) member function, the name of the base class followed by :: must be provided to indicate that the base class member function is to be used.
|
||||||
|
- The copy constructor just uses the copy constructor of the base class, without any special designation because the stack object is a list object as well.
|
||||||
|
|
||||||
|
## 26.8 Is-A, Has-A, As-A Relationships Among Classes
|
||||||
|
|
||||||
|
- When trying to determine the relationship between (hypothetical) classes C1 and C2, try to think of a logical relationship between them that can be written:
|
||||||
|
– C1 is a C2,
|
||||||
|
– C1 has a C2, or
|
||||||
|
– C1 is implemented as a C2
|
||||||
|
- If writing “C1 is-a C2” is best, for example: “a savings account is an account”, then C1 should be a derived class (a subclass) of C2.
|
||||||
|
- If writing “C1 has-a C2” is best, for example: “a cylinder has a circle as its base”, then class C1 should have a member variable of type C2.
|
||||||
|
- In the case of “C1 is implemented as-a C2”, for example: “the stack is implemented as a list”, then C1 should be derived from C2, but with private inheritance. This is by far the least common case!
|
||||||
|
|
||||||
|
## 26.9 Exercise: 2D Geometric Primitives
|
||||||
|
|
||||||
|
Create a class hierarchy of geometric objects, such as: triangle, isosceles triangle, right triangle, quadrilateral, square,
|
||||||
|
rhombus, kite, trapezoid, circle, ellipse, etc. How should this hierarchy be arranged? What member variables and
|
||||||
|
member functions should be in each class?
|
||||||
|
|
||||||
|
## 26.10 Multiple Inheritance
|
||||||
|
|
||||||
|
- When sketching a class hierarchy for geometric objects, you may have wanted to specify relationships that were more complex... in particular some objects may wish to inherit from more than one base class.
|
||||||
|
- This is called multiple inheritance and can make many implementation details significantly more hairy. Different programming languages offer different variations of multiple inheritance.
|
||||||
|
- See [example 1](multiple_inheritance1.cpp) and [example 2](multiple_inheritance2.cpp).
|
||||||
|
|
||||||
|
- And see [example 3](multiple_level_inheritance.cpp) for a multiple level inheritance example.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 26.11 Introduction to Polymorphism
|
||||||
|
|
||||||
|
- Let’s consider a small class hierarchy version of polygonal objects:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class Polygon {
|
||||||
|
public:
|
||||||
|
Polygon() {}
|
||||||
|
virtual ~Polygon() {}
|
||||||
|
int NumVerts() { return verts.size(); }
|
||||||
|
virtual double Area() = 0;
|
||||||
|
virtual bool IsSquare() { return false; }
|
||||||
|
protected:
|
||||||
|
vector<Point> verts;
|
||||||
|
};
|
||||||
|
class Triangle : public Polygon {
|
||||||
|
public:
|
||||||
|
Triangle(Point pts[3]) {
|
||||||
|
for (int i = 0; i < 3; i++) verts.push_back(pts[i]); }
|
||||||
|
double Area();
|
||||||
|
};
|
||||||
|
class Quadrilateral : public Polygon {
|
||||||
|
public:
|
||||||
|
Quadrilateral(Point pts[4]) {
|
||||||
|
for (int i = 0; i < 4; i++) verts.push_back(pts[i]); }
|
||||||
|
double Area();
|
||||||
|
double LongerDiagonal();
|
||||||
|
bool IsSquare() { return (SidesEqual() && AnglesEqual()); }
|
||||||
|
private:
|
||||||
|
bool SidesEqual();
|
||||||
|
bool AnglesEqual();
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
- Functions that are common, at least have a common interface, are in Polygon.
|
||||||
|
- Some of these functions are marked virtual, which means that when they are redefined by a derived class, this new definition will be used, even for pointers to base class objects.
|
||||||
|
- Some of these virtual functions, those whose declarations are followed by = 0 are pure virtual, which means
|
||||||
|
they must be redefined in a derived class.
|
||||||
|
– Any class that has pure virtual functions is called “abstract”.
|
||||||
|
– Objects of abstract types may not be created — only pointers to these objects may be created.
|
||||||
|
- Functions that are specific to a particular object type are declared in the derived class prototype.
|
||||||
|
|
||||||
|
## 26.12 A Polymorphic List of Polygon Objects
|
||||||
|
|
||||||
|
- Now instead of two separate lists of polygon objects, we can create one “polymorphic” list:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::list<Polygon*> polygons;
|
||||||
|
```
|
||||||
|
|
||||||
|
- Objects are constructed using new and inserted into the list:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
Polygon *p_ptr = new Triangle( .... );
|
||||||
|
polygons.push_back(p_ptr);
|
||||||
|
p_ptr = new Quadrilateral( ... );
|
||||||
|
polygons.push_back(p_ptr);
|
||||||
|
Triangle *t_ptr = new Triangle( .... );
|
||||||
|
polygons.push_back(t_ptr);
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: We’ve used the same pointer variable (p_ptr) to point to objects of two different types.
|
||||||
|
|
||||||
|
## 26.13 Accessing Objects Through a Polymorphic List of Pointers
|
||||||
|
|
||||||
|
- Let’s sum the areas of all the polygons:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
double area = 0;
|
||||||
|
for (std::list<Polygon*>::iterator i = polygons.begin(); i!=polygons.end(); ++i){
|
||||||
|
area += (*i)->Area();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Which Area function is called? If *i points to a Triangle object then the function defined in the Triangle
|
||||||
|
class would be called. If *i points to a Quadrilateral object then Quadrilateral::Area will be called.
|
||||||
|
|
||||||
|
- Here’s code to count the number of squares in the list:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int count = 0;
|
||||||
|
for (std::list<Polygon*>::iterator i = polygons.begin(); i!=polygons.end(); ++i){
|
||||||
|
count += (*i)->IsSquare();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If Polygon::IsSquare had not been declared virtual then the function defined in Polygon would always be
|
||||||
|
called! In general, given a pointer to type T we start at T and look “up” the hierarchy for the closest function
|
||||||
|
definition (this can be done at compile time). If that function has been declared virtual, we will start this
|
||||||
|
search instead at the actual type of the object (this requires additional work at runtime) in case it has been
|
||||||
|
redefined in a derived class of type T.
|
||||||
|
|
||||||
|
- To use a function in Quadrilateral that is not declared in Polygon, you must “cast” the pointer. The pointer
|
||||||
|
*q will be NULL if *i is not a Quadrilateral object.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
for (std::list<Polygon*>::iterator i = polygons.begin(); i!=polygons.end(); ++i) {
|
||||||
|
Quadrilateral *q = dynamic_cast<Quadrilateral*> (*i);
|
||||||
|
if (q) std::cout << "diagonal: " << q->LongerDiagonal() << std::endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 26.14 Exercise
|
||||||
|
|
||||||
|
What is the output of the following [program](exercise.cpp)?
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
class Base {
|
||||||
|
public:
|
||||||
|
Base() {}
|
||||||
|
virtual void A() { std::cout << "Base A "; }
|
||||||
|
void B() { std::cout << "Base B "; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class One : public Base {
|
||||||
|
public:
|
||||||
|
One() {}
|
||||||
|
void A() { std::cout << "One A "; }
|
||||||
|
void B() { std::cout << "One B "; }
|
||||||
|
};
|
||||||
|
class Two : public Base {
|
||||||
|
public:
|
||||||
|
Two() {}
|
||||||
|
void A() { std::cout << "Two A "; }
|
||||||
|
void B() { std::cout << "Two B "; }
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
Base* a[3];
|
||||||
|
a[0] = new Base;
|
||||||
|
a[1] = new One;
|
||||||
|
a[2] = new Two;
|
||||||
|
for (unsigned int i=0; i<3; ++i) {
|
||||||
|
a[i]->A();
|
||||||
|
a[i]->B();
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 26.15 Exercise
|
||||||
|
|
||||||
|
What is the output of the following [program](virtual.cpp)?
|
||||||
34
lectures/26_inheritance_II/exercise.cpp
Normal file
34
lectures/26_inheritance_II/exercise.cpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
class Base {
|
||||||
|
public:
|
||||||
|
Base() {}
|
||||||
|
virtual void A() { std::cout << "Base A "; }
|
||||||
|
void B() { std::cout << "Base B "; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class One : public Base {
|
||||||
|
public:
|
||||||
|
One() {}
|
||||||
|
void A() { std::cout << "One A "; }
|
||||||
|
void B() { std::cout << "One B "; }
|
||||||
|
};
|
||||||
|
class Two : public Base {
|
||||||
|
public:
|
||||||
|
Two() {}
|
||||||
|
void A() { std::cout << "Two A "; }
|
||||||
|
void B() { std::cout << "Two B "; }
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
Base* a[3];
|
||||||
|
a[0] = new Base;
|
||||||
|
a[1] = new One;
|
||||||
|
a[2] = new Two;
|
||||||
|
for (unsigned int i=0; i<3; ++i) {
|
||||||
|
a[i]->A();
|
||||||
|
a[i]->B();
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
46
lectures/26_inheritance_II/inheritance.cpp
Normal file
46
lectures/26_inheritance_II/inheritance.cpp
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
class Parent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Parent(){
|
||||||
|
std::cout << "Parent default constructor" << std::endl;
|
||||||
|
}
|
||||||
|
Parent(std::string name, int age) : name(name), age(age){
|
||||||
|
std::cout << "Parent other constructor" << std::endl;
|
||||||
|
}
|
||||||
|
void print(){
|
||||||
|
std::cout << "Parent: " << name << ":" << age << std::endl;
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
std::string name;
|
||||||
|
int age;
|
||||||
|
private:
|
||||||
|
int id;
|
||||||
|
};
|
||||||
|
|
||||||
|
// public inheritance
|
||||||
|
class Child: public Parent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Child(){
|
||||||
|
std::cout << "Child default constructor" << std::endl;
|
||||||
|
}
|
||||||
|
Child(std::string name, int age): Parent(name, age) {
|
||||||
|
std::cout << "Child other constructor" << std::endl;
|
||||||
|
}
|
||||||
|
void printChild(){
|
||||||
|
std::cout << "Child: " << name << ":" << age << std::endl;
|
||||||
|
// std::cout << "id:" << id << std::endl;
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
Parent parent("bob", 30);
|
||||||
|
parent.print();
|
||||||
|
Child child;
|
||||||
|
Child child2("james", 10);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
35
lectures/26_inheritance_II/multiple_inheritance1.cpp
Normal file
35
lectures/26_inheritance_II/multiple_inheritance1.cpp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
class B
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
B(int b1):b(b1){}
|
||||||
|
protected:
|
||||||
|
int b;
|
||||||
|
};
|
||||||
|
|
||||||
|
class C
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
C(int c1):c(c1){}
|
||||||
|
protected:
|
||||||
|
int c;
|
||||||
|
};
|
||||||
|
|
||||||
|
class D: public B, public C
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
D(int b1, int c1):B(b1),C(c1),d(b1+c1){}
|
||||||
|
|
||||||
|
void print(){
|
||||||
|
std::cout << "d is " << d << std::endl;
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
int d;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
D* dOjbect = new D(2,3);
|
||||||
|
dOjbect->print();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
79
lectures/26_inheritance_II/multiple_inheritance2.cpp
Normal file
79
lectures/26_inheritance_II/multiple_inheritance2.cpp
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// diamond inheritance
|
||||||
|
// Address of A in B, in C, in D are the same, because there is only one copy.
|
||||||
|
|
||||||
|
class A
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
A(int a) : a(a) {}
|
||||||
|
protected:
|
||||||
|
int a;
|
||||||
|
};
|
||||||
|
|
||||||
|
// virtual inheritance: declare A as the virtual base class
|
||||||
|
// in virtual inheritance, B contains a pointer to the shared instance of A
|
||||||
|
class B: virtual public A
|
||||||
|
// class B: public A
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
B(int a) : A(a) {}
|
||||||
|
void print(){
|
||||||
|
std::cout << "Address of A in B: " << reinterpret_cast<void*>(static_cast<A*>(this)) << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// virtual inheritance: declare A as the virtual base class
|
||||||
|
// in virtual inheritance, C contains a pointer to the shared instance of A
|
||||||
|
class C: virtual public A
|
||||||
|
// class C: public A
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
C(int a) : A(a) {}
|
||||||
|
void print(){
|
||||||
|
std::cout << "Address of A in C: " << reinterpret_cast<void*>(static_cast<A*>(this)) << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// D has a single shared instance of the virtual base class (A), regardless of how many times it appears in the hierarchy.
|
||||||
|
// Due to virtual inheritance, there is a single shared instance of A within the D object.
|
||||||
|
// This helps in avoiding the "diamond problem" and ensures that there is only one copy of the shared base class.
|
||||||
|
class D: public B, public C
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
D(int a): B(a), C(a), A(123) {}
|
||||||
|
// D(int a): B(a), C(a), A(a) {}
|
||||||
|
|
||||||
|
void print(){
|
||||||
|
// reference to ‘a’ is ambiguous
|
||||||
|
std::cout << "a is " << a << std::endl;
|
||||||
|
// std::cout << "a is " << C::a << std::endl;
|
||||||
|
//
|
||||||
|
B::print();
|
||||||
|
C::print();
|
||||||
|
std::cout << "Address of A in D: " << reinterpret_cast<void*>(static_cast<A*>(this)) << std::endl;
|
||||||
|
std::cout << "Address of B in D: " << reinterpret_cast<void*>(static_cast<B*>(this)) << std::endl;
|
||||||
|
std::cout << "Address of C in D: " << reinterpret_cast<void*>(static_cast<C*>(this)) << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
D* dObject_p = new D(1);
|
||||||
|
dObject_p->print();
|
||||||
|
|
||||||
|
D dObject(2);
|
||||||
|
std::cout << sizeof(int) << std::endl;
|
||||||
|
// size of dObject = 4 (A) + 8 (pointer in B)+ 8 (pointer in C) + padding
|
||||||
|
std::cout << sizeof(dObject) << std::endl;
|
||||||
|
|
||||||
|
// release memory
|
||||||
|
delete dObject_p;
|
||||||
|
|
||||||
|
// Print sizes and addresses
|
||||||
|
std::cout << "Size of A: " << sizeof(A) << " bytes" << std::endl;
|
||||||
|
std::cout << "Size of B: " << sizeof(B) << " bytes" << std::endl;
|
||||||
|
std::cout << "Size of C: " << sizeof(C) << " bytes" << std::endl;
|
||||||
|
std::cout << "Size of D: " << sizeof(D) << " bytes" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
38
lectures/26_inheritance_II/multiple_level_inheritance.cpp
Normal file
38
lectures/26_inheritance_II/multiple_level_inheritance.cpp
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// multiple-level inheritance
|
||||||
|
|
||||||
|
class A
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
A(int a):a(a){}
|
||||||
|
int a;
|
||||||
|
};
|
||||||
|
class B:public A
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
B(int a, int b):b(b),A(a){}
|
||||||
|
int b;
|
||||||
|
};
|
||||||
|
class C:public B
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
C(int a, int b, int c):B(a, b),c(c){}
|
||||||
|
int c;
|
||||||
|
|
||||||
|
};
|
||||||
|
class D:public C
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
D(int a, int b, int c, int d):C(a,b,c),d(d){}
|
||||||
|
void print(){
|
||||||
|
std::cout << a << ":" << b << ":" << c << ":" << d << std::endl;
|
||||||
|
}
|
||||||
|
int d;
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
D dObject(1,2,3,4);
|
||||||
|
dObject.print();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
67
lectures/26_inheritance_II/virtual.cpp
Normal file
67
lectures/26_inheritance_II/virtual.cpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
class A
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void print(){
|
||||||
|
std::cout << "A" << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class B: public A
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// virtual void print(){
|
||||||
|
void print(){
|
||||||
|
std::cout << "B" << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class C: public B
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void print(){
|
||||||
|
std::cout << "C" << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class D: public C
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void print(){
|
||||||
|
std::cout << "D" << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
// initialize base class pointer with a derived class object.
|
||||||
|
A* pA = new B;
|
||||||
|
// which print is getting called?
|
||||||
|
// if no virtual, decide by pointer type.
|
||||||
|
pA->print();
|
||||||
|
|
||||||
|
// which print is getting called?
|
||||||
|
// if no virtual, decide by pointer type.
|
||||||
|
// if base is marked as virtual, decide by object type.
|
||||||
|
B* pB = new C;
|
||||||
|
pB->print();
|
||||||
|
// which print is getting called?
|
||||||
|
// pointer and object same type, just call its local member function.
|
||||||
|
pB = new B;
|
||||||
|
pB->print();
|
||||||
|
// which print is getting called?
|
||||||
|
// if no virtual, decide by pointer type.
|
||||||
|
// if base is marked as virtual, decide by object type.
|
||||||
|
C c;
|
||||||
|
pB = &c;
|
||||||
|
pB->print();
|
||||||
|
|
||||||
|
// which print is getting called?
|
||||||
|
// if no virtual, decide by pointer type.
|
||||||
|
// if base is marked as virtual, decide by object type.
|
||||||
|
// also, virtual can be inheritated, even if the derived one doesn't use the virtual keyword.
|
||||||
|
C* pC = new D;
|
||||||
|
pC->print();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user