C++ Object-Oriented Programming Fundamentals: Inheritance and Constructors
Experiment Objectives
- Understand the relationship between classes and objects, master constructors and destructors, and implement encapsulation
- Learn how to define base and derived classes, and understand the calling order of constructors and destructors
- Master the principles and methods of multiple inheritance and virtual inheritance
- Review function overloading (member and friend functions) and common operator overloading
Experiment Content
Exercise 1: Basic Class Inheritance
Compile and run the following program, then explain the purpose of each statement:
#include <iostream>
using namespace std;
class BaseClass { // Define a base class named BaseClass
public: // Public access specifier
BaseClass() {cout << "Base class constructor called" << endl;} // Base class constructor
~BaseClass() {cout << "Base class destructor called" << endl;} // Base class destructor
};
class DerivedClass : public BaseClass { // Define derived class DerivedClass with public inheritance
public: // Public access specifier
DerivedClass() {cout << "Derived class constructor called" << endl;} // Derived class constructor
~DerivedClass() {cout << "Derived class destructor called" << endl;} // Derived class destructor
};
int main() {
DerivedClass obj; // Create an object of DerivedClass
return 0;
}
</iostream>
Output Result
Base class constructor called
Derived class constructor called
Derived class destructor called
Base class destructor called
Exercise 2: Constructor Initialization with Member Objects
Compile and run the following program, then explain the purpose of each statement:
#include <iostream>
using namespace std;
class BaseClass { // Define base class BaseClass
private: // Private access specifier
int value; // Private member variable value
public: // Public access specifier
BaseClass(int a) { // Base class parameterized constructor
cout << "Base class constructor called" << endl;
value = a; // Assign parameter a to member variable value
cout << "value = " << value << endl;
}
~BaseClass() {cout << "Base class destructor called" << endl;} // Base class destructor
};
class DerivedClass : public BaseClass { // DerivedClass publicly inherits BaseClass
BaseClass memberObj; // Member object: object of BaseClass
int derivedValue; // Private member variable in derived class
public: // Public access specifier
DerivedClass(int a, int b, int c) : BaseClass(a), memberObj(c) { // Initialize base class and member object
cout << "Derived class constructor called" << endl;
derivedValue = b; // Assign parameter b to member variable derivedValue
cout << "derivedValue = " << derivedValue << endl;
}
~DerivedClass() {cout << "Derived class destructor called" << endl;} // Derived class destructor
};
int main() {
DerivedClass obj(1, 2, 3); // Create DerivedClass object obj with parameters 1, 2, 3
return 0;
}
</iostream>
Output Result
Base class constructor called
value = 1
Base class constructor called
value = 3
Derived class constructor called
derivedValue = 2
Derived class destructor called
Base class destructor called
Base class destructor called
Exercise 3: Multiple Inheritance
Compile and run the following program, then explain the purpose of each statement:
#include <iostream>
using namespace std;
class A { // Define base class A
public: // Public access specifier
int n; // Public member variable n
};
class B : public A {}; // Class B publicly inherits A
class C : public A {}; // Class C publicly inherits A
class D : public B, public C { // Class D inherits from both B and C
public:
int getValue() { return B::n; } // Member function to return n inherited from B
};
int main() {
D d; // Create D class object d
d.B::n = 10; // Access and set n through B path to 10
d.C::n = 20; // Access and set n through C path to 20
cout << d.B::n << "," << d.C::n << endl; // Output both n values
return 0;
}
</iostream>
Output Result
10,20
Thought Question
Run the following program and explain the differences from the previous example:
#include <iostream>
using namespace std;
class A {
public:
int n;
};
class B : virtual public A {}; // Virtual inheritance
class C : virtual public A {}; // Virtual inheritance
class D : public B, public C {
public:
int getValue() { return B::n; }
};
int main() {
D d;
d.B::n = 10;
d.C::n = 20;
cout << d.B::n << "," << d.C::n << endl;
return 0;
}
</iostream>
Difference
This example uses virtual inheritance, unlike the previous one. The output is different because in the previous example, B::n and C::n were two different variables, so they could have different values (10 and 20). Here, B::n and C::n point to the same location, so they represent the same variable, which ends up being 20 (the last value assigned).
Output Result
20,20
Exercise 4: Shape Classes with Area Calculation
Define a Point class and derive Rectangle and Circle classes from it, implementing an Area() function for each derived class:
#include <iostream>
#include <cmath>
using namespace std;
class Point {
protected:
int x, y;
public:
Point(int xx = 0, int yy = 0) : x(xx), y(yy) {}
void display() const {cout << "(" << x << "," << y << ")";}
int getX() const {return x;}
int getY() const {return y;}
};
class Rectangle : public Point {
private:
int width, height;
public:
Rectangle(int x = 0, int y = 0, int w = 0, int h = 0) : Point(x, y), width(w), height(h) {}
double Area() const {
return width * height;
}
void display() const {
Point::display();
cout << ", Rectangle width: " << width << ", Rectangle height: " << height;
cout << ", Rectangle area: " << Area() << endl;
}
};
class Circle : public Point {
private:
int radius;
public:
Circle(int x = 0, int y = 0, int r = 0) : Point(x, y), radius(r) {}
double Area() const {
return 3.14 * radius * radius;
}
void display() const {
Point::display();
cout << ", Circle radius: " << radius;
cout << ", Circle area: " << Area() << endl;
}
};
int main() {
Rectangle rect(1, 1, 4, 3);
rect.display();
Circle circ(2, 2, 2);
circ.display();
return 0;
}
</cmath></iostream>
Output Result
(1,1), Rectangle width: 4, Rectangle height: 3, Rectangle area: 12
(2,2), Circle radius: 2, Circle area: 12.56
Experiment Summary
Through multiple examples of constructors and destructors, we've mastered the construction order for derived classes containing member objects (base class → member objects → derived class). Through a comparative experiment between regular inheritance and virtual inheritance, we've understood the principles of virtual inheritance. We've also understood the differences between member functon and friend function overloading and how to choose between them, as well as why certain operators must be implemented as friend functions.