/* Inheritance basics */ using namespace std; #include #include // a superclass: class account { protected: double balance; public: string owner; account(double b, string s) { balance=b; owner=s; } // overridable functions virtual double inquiry() { return balance; } virtual void deposit(double x) { balance += x; } virtual void withdraw(double x) { balance -= x; cout << "calling account::withdraw\n"; } }; // subclass 1: Savings account adds interest rate class savings : public account { private: double interest; public: // constructor first calls superclass constructor: savings(double b, string s, double i) : account(b,s) { interest = i; } // override deposit function by awarding a bonus of $2: void deposit(double x) { balance += x + 2; } // add new function to add interest to balance virtual void addinterest() { balance += balance*interest; } }; // savings // subclass 2: Checking account charges fees (this is a lousy bank) class checking : public account { public: // constructor checking(double b, string s) : account(b,s) { cout << "you're charged a fee for using this type of account\n"; balance -= 10; // $10 fee for openning account } void withdraw(double x) { balance -= x + (x*0.02); // charge 2% fee for withdraws cout << "calling checking::withdraw\n"; } // add an extra function: void penalty() { if (balance<100) { cout << "your balance is too low, you'll be penalized $10\n"; balance -= 10; } } }; // class checking /////// Usage int main() { account *A; savings *S; checking *C; S = new savings(1000,"prof",0.01); C = new checking(2000,"prof"); S->withdraw(100); // calls inherited withdraw C->withdraw(200); // calls overriden withdraw /////// More interesting: A = C; // This assignment is valid because checking is a subclass of account C->penalty(); // OK //A->penalty(); // Compiler error because the static type of A is account A->withdraw(100); // which withdraw does it call? /* An object variable has two types. The "static" type is the type with which the variable is declared. The dynamic type is the actual type of the object that the varible is assigned to, which can be a subclass of the static type. In this example, the S and C objects have the same static and dynamic types. But the variable A has static type account (the superclass), but dynamic type checking (a subclass). The dynamic type cannot be determined at compile time. In fact, we can even assign A to a savings object later on in the program. This may seem strange but it is perhaps the most powerful feature of object-oriented programming. The keyword "virtual" means that the actual function to be called is to be determined by the dynamic type of the object, as opposed to the static type. Without the word "virtual" in the account class, A->withdraw(100) will invoke account::withdraw, because the static type of A is account (account* to be exact). With "virtual", it uses the dynamic type to "dispatch" to a different procedure, which is checking::withdraw in this case. */ // Why would you want variables with different static and dynamic types? // Here's a polymorphic array of different kinds of account objects: account **B; B = new account*[3]; B[0] = new savings(1000,"prof",0.01); B[1] = new checking(2000,"prof"); B[2] = new checking(3000,"student"); // withdraw from all of them: for(int i=0;i<3;i++) B[i]->withdraw(2000); //// What if I have an object of a superclass static type, but I know //// it's in fact a subclass object?: // B[1]->penalty(); // Compiler error! // Why? because B[1] has static type account, which does not declare a // a penalty function, virtual or otherwise. // But that's not fair! says you, because the dynamic type of B[1] is // clearly checking. How do I then invoke the penalty function on B[1]? // I have to type cast it to the subtype, so that the compiler knows how // to treat it: ((checking*)B[1])->penalty(); // this is OK. delete(B[0]); delete(B[1]); delete(B[2]); delete(B); return 0; } // main