purify Inheritance (review) overriding functions dynamic and static types virtual functions pure virtual memory layout ================================ purify - see handout --------------------------- Inheritance Concept: subclass is a specialization of the superclass lots of realword examples - ask for examples Possibles- Pen, Pencil, Crayon are subclasses of WritingImplement Car, Bicycle and Wagon are subclasses of Vehicle One point must always be true - ALL behaviors (functions) defined by superclass MUST be valid for every instance of every subclass Subclasses may add new behaviors however. Pen may add a new function refill whereas Pencil may add sharpen You can always assign from a pointer to a subclass to a pointer to a superclass but not the other way around. Car * c; Vehicle * v; v = c; // legal - every car IS a vehicle c = v; // illegal - compiler error - many vehicles are not cars Inheritance is good because it makes enhancing a program easier If you write a draw program using the Shape hierarchy --------- | Shape | --------- / \ / \ ---------- ------------- | Circle | | Rectangle | ---------- ------------- where all of the main code just calls functions defined on Shape, it is very easy to just add a triangle shape later on. Some forms of inheritance just add new pieces class Sale { public: Sale(char * item_sold, double sale_price); char * item() { return _item; }; double price() { return _price; }; private: char * _item; double _price; }; Sale::Sale(char * item_sold,double sale_price) { _item = strdup(item_sold); _price = sale_price; } class CommissionSale : public Sale { public: CommissionSale(char * item_sold, double sale_price, SalesPerson * soldBy); SalesPerson * agent() { return _agen; }; double commission() { return _commission; }; private: SalesPerson * _agent; double _commission; }; CommissionSale::CommissionSale(char * item_sold, double sale_price, SalesPerson * soldBy) : Sale(item_sold,sale_price) { _agent = soldBy; _commission = sale_price * soldBy->commissionPct(); } sometimes, however, you want to override a behavior if you defined a function on Sale that said how much was due to the seller such as class Sale { public: ... double toSeller() { return price(); } ... }; You would want to override this method in CommissionSale as follows class CommissionSale : public Sale { public: ... double toSeller() { return price() - commission(); } ... }; This works fine as long as you know whether each sale is a CommissionSale or not. But if you define a function void paySeller(Sale * sale,Person * seller) { transferMoney(seller,sale->toSeller()); } This code cannot know whether this Sale is actually a CommissionSale or not. It could have been called from CommissionSale * csale; csale = ... paySeller(csale,somebody); To be safe, the compiler only assumes that the sale in paySeller is a Sale, rather than a CommissionSale. Sale is the static or declared type of sale. This is the type given in the variable or parameter declaration. But in the call above, sale is actually a CommissionSale. This is called the dynamic or runtime type. The compiler can figure out what the dynamic type is. So there are to ways the compiler can use to choose which toSeller function to call - the static or the dynamic type. the static choice would be to call Sale::toSeller and the dynamic choice is to call CommissionSale::toSeller By default, in C++, the static choice is made. If you are overriding a function at all, this is almost always the wrong choice. To make the compiler choose based on the dynamic type, you must declare the function virtual. You want to do this almost always. Unless you can explain why you don't want to make a function virtual, you should make it virtual (if it is ever going to be overridden in a subclass). You make a function virtual using the keyword virtual as in class Sale { public: ... virtual double toSeller(); ... }; You only need to specify virtual in the superclass. If it is virtual in the superclass, it is virtual in the subclass. For clarity, it is usually worthwhile to put the virtual in both the superclass and the subclass. You do not use the virtual keyword in the function definition. The definition for toSeller looks like double Sale::toSeller() { return price(); } whether it is virtual or not. -------------------------------- Pure virtuals Sometimes the superclass does not actually make sense on its own. For example, there are no things that are just Shapes, there are just circles and rectangles and ... These are called abstract classes Frequently, on abstract classes it does not make sense to actually implement one or more of the functions defined by the abstract class. consider Shape class Shape { public: Shape(int x, int y); virtual void draw() = 0; virtual int area() = 0; ... } There is no sensical definition of area or draw on Shape. Instead, using C++'s arcance syntax, the functions are declared to be unimplemented on this abstract class. These functions must be implemented on each (non-abstract) subclass. Although there is a constructor for Shape, it is illegal to say new Shape. << If you need an example of a non pure virtual function on Shape, try move, which just resets x and y and calls draw >> ------------------------------------- Memory layout Given the class definitions class Shape { public: Shape(int x, int y); ... private: int x; int y; }; class Circle : public Shape { public: Circle(int x, int y, int radius); ... private: int radius; }; What does the memory look like for a Circle instance? +---------------+ / | x | Shape = +---------------+ \ | y | +===============+ Circle | radius | +---------------+ A Rectangle instance might look like +---------------+ / | x | Shape = +---------------+ \ | y | +===============+ / | length | Rect = +---------------+ \ | width | +---------------+ Notice that the beginning of both a Rect and a Circle just look like a Shape in memory. This is why any functions implemented for Shape can work for any subclass.