CS60 Lecture 17 --------------------- Announce ---------------------- - hw12 due monday - project 4 posted, due nov 14th Today --------------------- - book conventions - more operator overloading - friends - basic inheritance book conventiosn -------------------- - returning by reference - returning by reference essentially 'turns' the function call into a variable: double &foo(double &in) { return(in); } int a; foo(a) = 10; - careful not to return references to local variables.. Initilization block ----------------------- - can initialize basic types using constructor notation int a(10); - in member function implementation, we can do some special initialization of member variables stack::stack() : top(-1), size(0) { bzero(data, sizeof(int)*32); } more operator overloading -------------------------- - to review, we have looked at overloading operators on class types - allows us to define behavior when programmer uses a + b type statements - must be careful to really examine what is going on with operators and stick to the expected bahavior ex: complex + complex - we have been defining overloaded operators as member functions - fine but one operand MUST be an instance of a class - we can also overload operators outside class defs const complex operator +(const complex &a, const complex &b) { ... } - this is nice but now we cannot access private data from within the function - class must implement accessors - lets look at << operator cout << 10; - << is a binary op, just like + or = - int on one side, what is that on the other side? - turns out cout is an instance of an object of type ostream. - << operator returns an ostream& cout << 10 << 20 << 30; (((cout << 10) << 20) << 30) (((ostream << int) << int) << int) ostream& ostream& ostream& - what does this prototype look like? ostream& operator <<(ostream &ostr, const complex &in) { ostr << in.getx() << " + " << in.gety() << "i"; return(ostr); } - analysis: what is happening? complex foo; cout << foo; - above function is called with a ref to 'cout' as param 1, and a ref to 'foo' as param 2 - we can use 'ostr' as cout since it's a ref - we return a reference to ostr Freinds -------------- - Sometimes it's tedious to do everything with accessors/mutator functions - C++ allows us to grant freindship to classes and functions class foo { private: int a, b; public: friend void somefunc(foo &); }; - back in main.c void somefunc(foo &in) { in.a = 10; in.b = 10; } - one can also make classes friends of eachother class foo { private: int a; public: friend class stack; } - key to realize about friendship is that friendship is given, not taken. - So in order for class B to have access to class A privates, A defines B as it's friend. - often times programmers define operators as friends to avoid overhead of function calls. ---------------------- Basic Inheritance ------------------------ - classes obviously have many special features - we can encapsulate data and functions - we can specify levels of safety of encapsulated data - we can overload operators on our own types - next special thing we can do is define heirarchies of classes - concept known as inheritance - again, idea is to reduce code duplication as much as possible - in C, if we had many complex data types, all had to be in their own structs - many times, different data types share a great deal in common and only differ in a few ways - ex: vehicles - motorcycle, skateboard, boat, kayak, airplane, helicopter common characteristics - all have a driver - all have a 3d position - all move in at least 2 dimensions wheeled vehicles - have some set # of wheels water vehicles - have some # of lifejackets air vehicles - can move in one more dimension motorcycle - has an engine - has a passenger skateboard boat - has an engine airplane - has a copilot helicopter vehicle water air road boat kayak airplane helicopter motorcycle skateboard - C++ lets us 'share commonalities' among types by using inheritance class vehicle { private: char *driver; int posx, posy, posz; int velx, vely; public: char *getdriver() {return driver;}; ... }; class roadvehicle : public vehicle { private: int numwheels; public: ... } class watervehicle : public vehicle { private: int numlifejackets; public: ... } class motorcycle : public roadvehicle { private: int enginecc; public: ... } - now in main motorcycle mybike; cout << mybike.getdriver() << endl; - but we have a problem, all our data values are declared as private which expressly means ONLY OBJECTS OF THE SAME TYPE HAVE ACCESS - new qualifier 'protected' - simply means that other same type classes or any class derived from class have access to protected data - now mybike can run 'getdriver()'. Multiple Types ----------------- - when a class is a child of another class, it has more than one type. any function that accepts a parent type can also accept a child type Redefining functions ------------------------- - child classes inherit all data/functions from parent classes. - if we want some child to do somthing different for some function, we can 'redefine' the function - just put a new definition with the same name and args in the class definition, and the new function code will run when invoked on a child class instance class motorcycle : public roadvehicle { public: char *getdriver() {cout << "no driver!" << endl; return(NULL);}; }; vehicle a; motorcycle b; a.getdriver() // calls getdriver code implemented in 'vehicle' b.getdriver() // calls getdriver code implemented in 'motorcycle' Functions Not Inherited ------------------------- - constructors, overloaded = operator, destructor - ALL classes must define these if they want to use them - why? well they initialize variables that may only exist in the child class - initialization section syntax makes it easier class roadvehicle { ... public: roadvehicle() : vehicle() { numwheels=1; }; };