In C++: Where do we stand on inheritance? Classes can be derived from other classes Basic Info about inheritance: To declare a derived class: class <derived-class-name>:public <base class-name> <derived class member functions> <derived class member data> Derived classes inherit: public and protected methods and data members of the base class Derived classes do not inherit: private data members and methods of the base class friend functions of the base class constructor(s) and the destructor of the base class base class assignment operator
Types of Inheritance We ve said that to declare a derived class: class <derived-class-name>:public <base class-name> <derived class member functions> <derived class member data> In fact, the full syntax is class <derived-class-name>:<access specifier> <base class-name> <derived class member functions> <derived class member data> The access specifier can be public (in this case, public and protected members of the base class become public and protected members of the derived class and private members are only accessible through public/protected members of the base class) protected (in this case, public and protected members of the base class become protected members of the derived class) private (in this case public and protected members of the base class become private members of the derived class) We almost never use protected and private inheritance!!
We d defined and implemented a standard C++ class hierarchy class BankAccount public: void Deposit (double dep); int AcctNum(); double Balance(); int getcust(); int AcctNumber; int custid; double Bal; class SuperPlus: public Checking public: SuperPlus(int AcctNo = 0000, int cust = 000, double Bal = 0, double Min = 5000, double Chg =.5, double Rate = 10); void Addinterest(); double InterestRate; class Checking:public BankAccount public: Checking (int AcctNum = 0000, int custid = 000, double Bal = 0, double Min = 1000, double Chg =.5); void CashCheck(float Amt); double MinBal; double Charge; class Savings:public BankAccount public: Savings( int AcctNumber = 0, int custid = 000, double Bal = 0, double Rate = 10); void AddInterest(); void Withdraw(double Amt); double InterestRate; L17 Feb. 24, 2010 friends and software reuse
Differences: Managed vs. Unmanaged classes If we are working in the CLR, there are some issues that are significant: an unmanaged type cannot derive from a managed type a ref class can only inherit from a ref class or interface class More about interface classes later the bottom line is that you need to keep a hierarchy compatible: all standard C++ or all CLR Working with managed types: no default arguments are allowed for member functions of managed types or generic functions this means that you have to make a default constructor that explicitly assigns the default values for data members instead of using the default argument syntax of standard C++ it also may mean that you need some extra constructors for situations in which you may want to do some default, some supplied data member values (e.g. accounts and values in the banking example)
A ref version of the banking class we discussed last time ref class BankAccount public: BankAccount (); BankAccount (int AcctNum, int cust); BankAccount (int AcctNum, int custid, double Bal); void Deposit (double dep);// deposit method int AcctNum(); // get account number double Balance(); // get balance int getcust(); // get customer SSN ref class Savings:public BankAccount public: Savings(); Savings( int AcctNumber, int custid); Savings( int AcctNumber, int custid, double Bal, double Rate); void AddInterest(); // add interest to balance void Withdraw(double Amt); // subtract withdrawal BankAccount (BankAccount % a); BankAccount operator = (BankAccount a); double InterestRate; int AcctNumber; int custid; double Bal;
The ref version of the banking class (cont.) ref class Checking:public BankAccount public: Checking(); Checking (int AcctNum, int custid); Checking (int AcctNum, int custid, double Bal, double Min, double Chg); void CashCheck(float Amt); Checking (Checking % a); Checking operator = (Checking a); double MinBal; // min bal to avoid charges double Charge; // per check charge ref class SuperPlus: public Checking public: // Constructors SuperPlus(); SuperPlus(int AcctNo, int cust); SuperPlus(int AcctNo, int cust, double Bal, double Min, double Chg, double Rate); // add interest to the balance void Addinterest(); double InterestRate;
ref Banking class hierarchy -- Notice that we have assignment and copy constructor, and other constructors defined for the base class BankAccount and for the derived class Checking, are we OK? Not really an assignment involving derived classes Savings and SuperPlus will generate an error saying that the operator = is not available Moral of the story: when dealing with ref classes in.net: if you have any intention of doing assignment, you must define an overloading of the assignment operator for the class this means that you must also define a copy constructor for the class it s good practice to define a destructor as well Assignment operators are not inherited by derived classes
Base Classes, and Derived Classes Even though a derived class object (like a SuperPlus account, for example) is-a base class object (i.e. a Checking account), the derived class type and the base class type are different types With public inheritance, derived class objects can be treated as base class objects the derived class has the full set of base class data members, after all It is not true, though, that base class objects can be treated as derived class objects the non-corresponding derived class data members would be undefined (unless care were taken to handle the situation) Look at the structure of Checking type objects (in a minute)! This means that assigning a derived class object to an object of a corresponding base class then trying to reference derived class (only) members in the new base class object will generate errors
BankAccount int AcctNumber; int custid; double Bal; Remember the Banking hierarchy: BankAccount AcctNumber custid Bal Checking:public BankAccount double MinBal; double Charge; SuperPlus: public Checking double InterestRate; Savings:public BankAccount double InterestRate; L17 Feb. 24, 2010 friends and software reuse Checking AcctNumber custid Bal MinBal Charge Savings AcctNumber custid Bal InterestRate SuperPlus AcctNumber custid Bal MinBal Charge InterestRate
Backpatching a hole Casting to a type I ve noticed that some of you are casting to types Converting an expression of one type to another type is called type casting There are two ways to do type casting: Implicitly: C++ does implicit type conversion when a value is copied to a compatible type For example, int x = 3.5 / 1.5 the rhs expression is a floating point expression and when the assignment is done it is automatically converted to an int. Explicitly: Many conversions, especially those that imply a different interpretation of the value, need to be explicitly converted. C++ has four specific casting operators: dynamic_cast reinterpret_cast static_cast const_cast
dynamic_cast Syntax: dynamic_cast <new_type> (expression) Usage: can be used only with pointers and references to objects purpose is to ensure that the result of the type conversion is a valid complete object of the right class is always successful when we cast a class to one of its base classes Example: class Base class Der: public Base Base b; Base* pb; Der d; Der* pd; pb = dynamic_cast<base*>(&d); pd = dynamic_cast<der*>(&b); // ok: derived-to-base // wrong: base-to-derived
static_cast Syntax: static_cast <new_type> (expression) Usage: can perform conversions between pointers to related classes from the derived class to its base from a base class to its derived. ensures that at least the classes are compatible if the proper object is converted no safety check is performed during runtime to check if the object being converted is in fact a full object of the destination type (unlike dynamic_cast) up to the programmer to ensure that the conversion is safe plus side: the overhead of the type-safety checks of dynamic_cast is avoided. can also be used to perform any other non-pointer conversion that could also be performed implicitly Example: class Base class Der: public Base Base * a = new Base; Der * b = static_cast<der*>(a); double pi=3.14159265; int i = static_cast<int>(pi);
reinterpret_cast Syntax: reinterpret_cast <new_type> (expression) Usage: converts any pointer type to any other pointer type, even of unrelated classes result is a simple binary copy of the value from one pointer to the other. all pointer conversions are allowed: neither the content pointed nor the pointer type itself is checked. can also cast pointers to or from integer types format in which this integer value represents a pointer is platform-specific. The only guarantee is that a pointer cast to an integer type large enough to fully contain it, is guaranteed to be able to be cast back to a valid pointer. conversions that can be performed by reinterpret_cast but not by static_cast have no specific uses in C++ and are low-level operations whose interpretation results in code which is generally system-specific, and thus non-portable. that s why you don t see this one much! Example: class A class B A * a = new A; B * b = reinterpret_cast<b*>(a); This is legal, but it does not make a lot of sense -- we have a pointer that points to an object of an incompatible class -- dereferencing it is unsafe
Syntax const_cast const_cast <new_type> (expression) Usage manipulates the constness of an object, either to be set or to be removed. Example: void foo(char *); const char *x = "abcd"; foo(const_cast<char *>(x)); This strips the const-ness from the variable x useful in those rare circumstances when you have a const thing that you want to use as an argument to a non const function or method
Pointers, Base Classes, and Derived Classes With public inheritance, a pointer (or tracking handle) to a derived class object can be implicitly converted to a pointer (or tracking handle) to a base-class object why? because a derived class object is a base class object, too There are four ways of mixing and matching base class pointers (tracking handles) and derived class pointers (tracking handles) with base class objects and derived class objects: base-class object base class pointer derived-class object derived class pointer derived-class object base class pointer base-class object derived class pointer
Homework 5 The beginnings of a class structure is posted to Moodle The base class and some derived classes The assignment involves you defining and implementing some more derived classes and doing a small application based on them. The idea is to give you some practice with an inheritance hierarchy and using it. There will be a Moodle quiz on inheritance that will be part of this assignment. All is due on Friday of next week (BEFORE BREAK so you don t have an assignment hanging over your head going into break)
Pointers, Base Classes, and Derived Classes (cont.) Referring to a base class object with a base class pointer or tracking handle is fine Referring to a derived class object with a derived class pointer or tracking handle is fine Referring to a derived class object with a base class pointer or tracking handle is safe the derived class object is-a base class thing, too if you try to refer to derived-class-only members through the base class pointer, a syntax error occurs Referring to a base class object with a derived class pointer or tracking handle is an error the derived class pointer has to be cast to a base class pointer or tracking handle first
Some Examples (class definitions) class point public: point(); point (double x, double y); point (point &p); point operator = (point p); void setpoint(double, double); double get_x(); double get_y(); double x, y; class circle : public point public: circle (double r = 0.0, int x = 0, int y = 0); void setradius(double); double getradius(); double area(); double radius;
#include "stdafx.h" #include "PointCircle.h" point::point() x = -1.0; y = -1.0;} Examples (point class implementation) Code placed in PointCircle.cpp point::point (double xval, double yval) setpoint (xval, yval);} // overloaded constructor void point::setpoint(double xval, double yval) x = xval; y = yval; } double point::get_x()return x;} double point::get_y() return y;} point::point (point &p) double xval = p.get_x(); double yval = p.get_y(); setpoint (xval, yval); } // copy constructor point point::operator = (point p) // overloading of = double x = p.get_x(); double y = p.get_y(); setpoint(x, y); return *this; }
// within PointCircle.cpp Examples (circle class implementation) circle::circle(double r, int a, int b) : point(a, b) setradius(r);} void circle::setradius (double r) radius = (r >= 0? r : 0); } double circle::getradius() return radius; } double circle::area() return 3.14159 * radius * radius; }
Derived class and Base class Pointers #include "stdafx.h" #include <iostream> using namespace System; using namespace std; #include "PointCircle.h" int main() } point *pointptr, p(10,10); // p is a point at (10,10) circle *circleptr, c(5, 20, 20); // c is a circle of radius // 5 centered at (20, 20) // treating a Circle as a Point: pointptr = &c; // assign address of the Circle to pointptr circleptr = &c; cout << pointptr -> get_x() << ", " << pointptr -> get_y(); return 0; BUT An attempt to access the radius of the circle through pointptr will generate a syntax error! double cr = circleptr->getradius(); double r = pointptr->getradius(); // is just fine // generates a syntax error
Derived class and Base class Pointers (cont.) int main() point *pointptr, p(10,10); // p is a point at (10,10) circle *circleptr, c(5, 20, 20); // c is a circle of radius // 5 centered at (20, 20) // treating a Circle as a Point: pointptr = &c; // assign address of the Circle to pointptr // cast the base class pointer to a derived class pointer circleptr = static_cast <circle *> (pointptr); cout << "\n circle c via circleptr: " << circleptr->getradius() << "\n area of circle via circleptr: " << circleptr-> area() << endl; // treating a point as a circle?? pointptr = &p; // cast base class pointer to a derived class pointer circleptr = static_cast <circle *> (pointptr); cout << "\n point p (xval, yval) via circleptr: (" << circleptr->get_x()<<", " << circleptr->get_y()<< ")" << endl << "radius of object circleptr points to: " << circleptr-> getradius() << "\n area of the object circleptr points to: " << circleptr-> area() << endl; return 0; } Reference classes behave in pretty much the same way
What s going on here and why is it important? When a class hierarchy is defined, the base class provides a set of data elements and methods that are inherited by all classes derived from it some features of the base class are not inherited by the derived classes, including: constructor(s) and destructor friend functions Each class (whether derived or otherwise) has to have at least a default constructor and a destructor. When we instantiate a derived class object: a chain of constructor calls occurs: before it does its own tasks, the constructor of a derived class first either explicitly or implicitly calls its base class constructor then it dos its own tasks if the base class was itself derived, it follows the same discipline: call its base class constructor, etc. On garbage collection, destructors are called in reverse order
What s going on here and why is it important? (cont.) We can access derived class objects through base class-type pointers as long as we don t try to access those elements that are derived-class only (i.e. not base-class) elements If a base-class pointer refers to a derived-class object, we can cast the base-class pointer to the object s actual type and then manipulate the derived-class-only elements (but we do have to do the cast)