Download presentation
Presentation is loading. Please wait.
1
CS3101-2 Programming Languages – C++ Lecture 4
Matthew P. Johnson Columbia University Fall 2003 1/15/2019 CS3101-2, Lecture 4
2
Agenda hw2 was due last night Today: hw3 TBA tonight Inheritance
Virtual functions Abstract classes Multiple inheritance hw3 TBA tonight 1/15/2019 CS3101-2, Lecture 4
3
A little more on I/O error-handling
Not very sophisticated error-handling was required on hw error message and quit was okay But how do we continue? After input error cin is in the failed state (ios::failbit or ios::badbit is on) bad data remains in the stream We need to fix both 1/15/2019 CS3101-2, Lecture 4
4
A little more on I/O error-handling
But: as long as cin is in a failed/bad state, we can’t do any input not even through ignore()! Recovery consists of two steps: cin.clear() puts cin back in the good state 2. cin.ignore(1000,’\n’); empties much data up till new-line 1/15/2019 CS3101-2, Lecture 4
5
A little more on I/O error-handling
Doing these in reverse does not work ignore has no effect on stream Calling cin.setstate(0) does not work turns on flags in addition to current flags we need ios:failbit turned off and ios:goodbit turned on See for a good I/O FAQ 1/15/2019 CS3101-2, Lecture 4
6
Objects Last time: talked about objects and encapsulation
Each relevant idea class Sometimes, concepts are all independent – set of unconnected nodes But often one concept is a special case of another Like another but more specialized Inst of one concept IS-An instance of another concept Inheritance 1/15/2019 CS3101-2, Lecture 4
7
Inheritance Basic intuition:
Suppose we want to fully describe concepts of: Lion, tiger, bear Oh my! – lots of work Save much work if we fully describe mammal fully describe lion – mammal, tiger – mammal, bear – mammal 1/15/2019 CS3101-2, Lecture 4
8
Inheritance: life Many levels: animal v. plants, genus, species
Many levels: animal v. plants, genus, species A human IS-A mammal, a mammal IS-An animal, etc. 1/15/2019 CS3101-2, Lecture 4
9
Inheritance: music formats
Analog Format Digital Format Magnetic Tape Vinyl DAT Disk MP3 12” Record 7” Record MiniDISC CD 1/15/2019 CS3101-2, Lecture 4
10
Inheritance Nomenclature
We say Queue extends Stack extends == inherits from subclass == child class = derived class superclass == parent class == base class Consider inher if always case that: inst2 IS-A inst1 Composition means: inst2 HAS-An inst1 The Queue class will be defined as the Stack class plus additions Some stack instances will be mere stacks; some will be queues the set of queues is a subset of the set of stacks 1/15/2019 CS3101-2, Lecture 4
11
Queues Goal: queue class: FIFO Operations: Also:
“queuing” in British Operations: enquene (put in front) dequeue (take from end) Also: initialization static v. dynamic storage Lots to do; But: almost all of this is already done in last week’s stack class 1/15/2019 CS3101-2, Lecture 4
12
Queue class We can use existing stack class for most of queue class
By copying and modifying stack.cpp? No! Future improvements to stack will likely need to be copied to queue parallel code maintenance Instead: Queue would extend Stack Only change: addition of enqueue function 1/15/2019 CS3101-2, Lecture 4
13
Simple inheritance: inher.cpp
Given one class: class Base { public: double square(double x) { return x*x; } string getClassName() { return “Base”; } } We can create an extension: class Extended: public Base { double cube(double x) { return x*x*x; } return “Extended”; } } 1/15/2019 CS3101-2, Lecture 4
14
Inher code e.g. Base b; Extended e; b.sq(3) 9 e.sq(3) 9
e.cu(3) 27 b.cu(3) DNC! b.getClassName() Base e.getClassName() Extended 1/15/2019 CS3101-2, Lecture 4
15
Inheritance code: NB We can call base-class ftns on derived-class inst: e.square(3) We can call new derived-class ftns on derived-class inst e.cube(3) We can override base-class ftn in derived class: b.getClassName() v. e.getClassName() 1/15/2019 CS3101-2, Lecture 4
16
Inheritance code: inher2.cpp
We can call base-class ftn from inside derived class: class Extended: public Base { public: double cube(double x) { return x*square(x); } string getClassName() { return "Extended"; } }; Output is unchanged: e.cube(3) == 27 1/15/2019 CS3101-2, Lecture 4
17
Inheritance code: inher2b.cpp
We defined methods in class def for convenience If method is overridden outside of class def, method must be re-declared in class: class Extended: public Base { public: double cube(double x) { return x*square(x); } string getClassName(); }; string Extended::getClassName() { return "Extended"; } 1/15/2019 CS3101-2, Lecture 4
18
Inheritance code: inher3.cpp
We can access a base-class var in the derived class: class Base { public: double square(double x) { return x*x; } string getClassName() { return s; } Base(): s("Base") {} protected: string s; }; The string is protected: accessible by descendents 1/15/2019 CS3101-2, Lecture 4
19
Inheritance code: inher3.cpp
We set s differently in extension: class Extended: public Base { public: double cube(double x) { return x*square(x); } Extended() { s = "Extended"; } }; e.getCN executes orig. ftn, but with right val: b.getClassName() == Base e.getClassName() == Extended 1/15/2019 CS3101-2, Lecture 4
20
Inheritance code: inher4.cpp
Finally, a derived-class ftn can call the corr. (overridden) base-class ftn base.getClassName() again returns a hard-coded string: class Base { public: double square(double x) { return x*x; } string getClassName() { return "Base"; } }; 1/15/2019 CS3101-2, Lecture 4
21
Inheritance code: inher4.cpp
But now extended.getClassName() creates a string based on a local hard-coding, and it’s parent’s name: class Extended: public Base { public: double cube(double x) { return x*square(x); } string getClassName() { string s = “Extended”; s += “, which derives from “; s += Base::getCN(); return s; } }; b.getClassName() == Base e.getClassName() == Extended, which derives from Base 1/15/2019 CS3101-2, Lecture 4
22
Inheritance and ptrs: inherpt.cpp
Suppose we use these classes with pointers: Base* bp = new Base; Base* ep = new Extended; cout << "bp->getCN() == " << bp->getCN() << endl; cout << "ep->getCN() == " << ep->getCN() << endl; What is the output? Q: Same as before? A: No: bp->getClassName() == Base ep->getClassName() == Base 1/15/2019 CS3101-2, Lecture 4
23
Inheritance and ptrs: inherpt.cpp
What happened? Before, with non-dynamic object vars, we knew which getClassName() to call the one defined in our var type: Base b; Extended e; “static binding” Now, our var type is just Base* by default, we call Base’s method 1/15/2019 CS3101-2, Lecture 4
24
Inheritance and ptrs: inherpt2.cpp
To ensure that the right version of getCN() is called, we make the method virtual: class Base { public: double square(double x) { return x*x; } virtual string getClassName() { return "Base"; } }; Now, by default, we get the lowest-level getCN: “dynamic binding” bp->getClassName() == Base ep->getClassName() == Extended, which derives from Base 1/15/2019 CS3101-2, Lecture 4
25
Virtual functions Why do you have to make functions virtual?
Finding the right version takes longer at runtime faster to automatically use var type version C/C++ wants to be fast Then why isn’t this necessary in Java? In Java, all methods are virtual Isn’t this slower Yes – Java isn’t going to be fast anyway 1/15/2019 CS3101-2, Lecture 4
26
Inheritance and ptrs: inherpt3.cpp
Similarly, what if we want to call epcube(3)? Error E2316 inherpt3.cpp 28: 'cube' is not a member of 'Base' in function main() For the same reason, we assign ep to a declared Extended*: Error E2034 inherpt3.cpp 28: Cannot convert 'Base *' to 'Extended *' in function main() Really, we wouldn’t want this allowed If an Extended* pointed to a Base, e.cube() would be undefined 1/15/2019 CS3101-2, Lecture 4
27
Inheritance and ptrs: inherpt3.cpp
In both cases, ep points to an Extended, buy the compiler doesn’t know it we might say ep an Extended “contingently” We must inform the compiler with a dynamic cast A “down cast” Extended* realEp = dynamic_cast<Extended*>(ep); cout << "ep->cube(3) == " << realEp->cube(3) << endl; Now, we can compile and get the correct result: realEp->cube(3) == 27 1/15/2019 CS3101-2, Lecture 4
28
Inheritance and ptrs: inherpt4.cpp
If the compiler can tell that the cast isn’t allowed because the cast couldn’t be valid we get a compile error Error E2031 inherpt4.cpp 35: Cannot cast from 'string' to 'Extended *' in functi on main() C’s cast () operator would simply do the cast and see what happens Result would be undefined, likely crash 1/15/2019 CS3101-2, Lecture 4
29
Inheritance and ptrs: inherpt4.cpp
If C++ can’t tell at compile-time, we don’t crash the result of cast is just null Base* bp = new Base; Extended* badCast = dynamic_cast<Extended*>(bp); cout << "The cast was " << (badCast == NULL ? "INvalid." : "VALID.") << endl; The cast was INvalid. Far superior to casting in C! 1/15/2019 CS3101-2, Lecture 4
30
Inheritance and ptrs: inherpt6.cpp
void printValid(string name, void * ptr) { cout << "This cast of " << name << " was " << (ptr == NULL ? "INvalid." : "VALID.") << endl; } … Base* bp = new Extended; Extended* ep = dynamic_cast<Extended*>(bp); printValid("bp", ep); Q: What is output? A: This cast of bp was VALID. 1/15/2019 CS3101-2, Lecture 4
31
Inheritance and ptrs: inherpt6.cpp
Extended e; Base b = e; // (*) ep = dynamic_cast<Extended*>(&b); printValid("&b", ep); Q: What is output? A: This cast of &b was INvalid.* * copy-o was corrected Q: Why? A: In (*), we construct Base b according to Base field values in e – but b is just a Base! 1/15/2019 CS3101-2, Lecture 4
32
dynamic_cast and references
We just used dynamic_cast for pointers It can’t be used for objects per se: Extended e = dynamic_cast<Extender>(b); But it turns out it can be used for references: Extended e; Base &br = e; Extended &er = dynamic_cast<Extended&>(br); Also: Base* bp = new Extended; Base &br2 = *bp; Extended &er2 = dynamic_cast<Extended&>(br2); 1/15/2019 CS3101-2, Lecture 4
33
dynamic_cast and references
What about invalid reference casts? How do we find out? Null references aren’t allowed Base* bp = new Base; Base &br2 = *bp; Extended &er2 = dynamic_cast<Extended&>(br2); Produces: Abnormal program termination An exception was thrown but not caught We’ll discuss these next time 1/15/2019 CS3101-2, Lecture 4
34
RTTI – in <typeinfo> - rtti.cpp
Dynamic casting uses run-time type info (RTTI) Apply typeid op returns type_info instance, describing type of object ref: Base* bp1 = new Base; Base* bp2 = new Extended; typeid(bp1).name() Base * typeid(*bp2).name() Extended typeid(12).name() int delete bp2; delete bp1; 1/15/2019 CS3101-2, Lecture 4
35
RTTI – rtti2.cpp RTTI can also be used to compare classes
typeid(12) == typeid(13)) 1 typeid(*bp1) == typeid(*bp2) 0 typeid(bp1) == typeid(bp2) 1 typeid(bp2) == typeid(*bp2) 0 Also: we can never save a type_info instance: type_info info = typeid(…); DNC! Q: Why/how? type_info’s copy constr and assign op are both private 1/15/2019 CS3101-2, Lecture 4
36
Polymorphism = many form-ism Polymorphism in:
overloading by params default function values overridden/virtual functions With overridden/virtual ftns, caller needn’t know real instance type need only know the abstract class it extends ~= the interface the class implements ~= how to use blackbox 1/15/2019 CS3101-2, Lecture 4
37
Canonical polymorphism e.g.: shapes
Suppose we have several shape classes: Circle, Square, Rectangle extending a shape class contains shape-type var to recognize Can write area function for all types: double Shape::area() { switch (s.getType()) { case CIRCLE: return rad()*rad()*PI; case SQUARE: return side()*side(); case RECT: return length()*width(); default: return -1; } 1/15/2019 CS3101-2, Lecture 4
38
Shapes Using a switch works, but in often:
should be replaced with polymorphism don’t group unshared intelligence together What if we create new shape class? would have to modify parent Shape class could affect other subclasses – bad! Better: for each concept, put its intel in its class Here: area Shape::area becomes pure virtual: virtual double area() = 0; And is implemented in each child class, e.g.: Circle::area() {return PI*rad()*rad(); } 1/15/2019 CS3101-2, Lecture 4
39
Pure virtual functions
Like overloaded virtual functions But: parent class defines but doesn’t implement ftn virtual ftn() = 0; defined with “null value” until overridden 1/15/2019 CS3101-2, Lecture 4
40
Abstract classes Has virtual functions cannot be instantiated
All classes are abstract the form is constant but the data changes With abstract classes, the class is incomplete part of the class is abstracted away 1/15/2019 CS3101-2, Lecture 4
41
Squares and rectangles
Pretty similar: square is like rect except both dims equal Maybe one should extend another Which one? Maybe rect extends square Then square looks like: class Square: public Shape { public: Square(double d): side(d) {} double area() { retrn val*val; } double side() { return side; } private: double side; } 1/15/2019 CS3101-2, Lecture 4
42
Squares and rectangles’
And Rectangle looks like: class Rect: private Square { public: Rect(double d1, double d2): len(d1), wid(d2) {} double area() { return len*wid; } double length() { return len; } double width() { return wid; } private: double len, wid; } This works, but do we have rect is-a square? NO! other way around! 1/15/2019 CS3101-2, Lecture 4
43
Rectangle and squares’
Inheritance could go the other way: class Square: public Shape { public: Square(double d): side(d1) {} double area() { return side*side; } double side() { return side; } protected: double side; } 1/15/2019 CS3101-2, Lecture 4
44
Squares and rectangles’
class Rect: public Square { public: Square(double d1, double d2): side(d1),wid(d2) {} double area() { return side*wid; } double length() { return Shape::side(); } double width() { return width; } private: double wid; } 1/15/2019 CS3101-2, Lecture 4
45
Rectangles and squares
2nd way works too, and enjoys proper is-a property Involves some reinterpretation of data member: side leng Requires making square.side protected Which way is better? In general, right way to “carve world at its joints” is a judgment Btw: we can’t have rect sq rect, since would be circular: structure is a tree 1/15/2019 CS3101-2, Lecture 4
46
Polymorphism e.g.: poly.cpp
Can be particularly dramatic with large numbers of instances multiple derived classes Suppose we abstract Animal class: class Animal { public: virtual void talk() = 0; }; And several extensions: class Cat: public Animal { public: void talk() { cout << "Meow!\n"; } };, etc. 1/15/2019 CS3101-2, Lecture 4
47
Polymorphism e.g.: poly.cpp
Next, write ftn to return inst of some derived type Animal* getInst(int which) { switch (which) { case 0: return new Cat; case 1: return new Dog; case 2: return new Frog; case 3: return new Human; default: cout << "bad i!\n"; return null; } 1/15/2019 CS3101-2, Lecture 4
48
Polymorphism e.g.: poly.cpp
srand(time(NULL)); // seed the RNG Animal** ans = new Animal*[10]; // constr ar for (int i = 0; i < 10; i++) // constr rand ans[i] = getInst(rand() % 4); // insts for (int i = 0; i < 10; i++) ans[i]->talk(); // each inst talks delete ans[i]; // each insts deleted delete[] ans; // ar deleted-ed 1/15/2019 CS3101-2, Lecture 4
49
Multiple inheritance Single inheritance: each class builds on previous (first-order MCs) Multiple inheritance: each class builds on multiple previous (higher-order MCs) Example: voic features of and features of a phone Q: What if same member inher-ed from 2 parents? Parent constructor calls should be explicit 1/15/2019 CS3101-2, Lecture 4
50
Multiple inheritance e.g.: multi.cpp
Re-design Point class to extend two classes 1 for x coord, 1 for y coord class XCoord { private: double x; public: XCoord(double val): x(val) { cout << "In XCoord CONStructor.\n"; } virtual ~XCoord() { cout << "In XCoord DEStructor.\n"; } }; YCoord is similar… 1/15/2019 CS3101-2, Lecture 4
51
Multiple inheritance e.g.: multi.cpp
class Point: public XCoord, public YCoord { public: Point(double v1, double v2): XCoord(v1), YCoord(v2) { cout << "In Point CONStructor.\n"; } ~Point() { cout << "In Point DEStructor.\n"; } }; Point pt(5,10); Result: In XCoord CONStructor. In YCoord CONStructor. In Point CONStructor. In Point DEStructor. In YCoord DEStructor. In XCoord DEStructor. 1/15/2019 CS3101-2, Lecture 4
52
Multiple inher & dyn casting
Dyn cast of ptr returns a ptr to another part of same object Usually, base class info stored at beginning of derived-class isnt ptr vals of orig. and cast are equal but system-dependent Can’t happen with multiple inher, since we have distinct sets of base class info 1/15/2019 CS3101-2, Lecture 4
53
Mult inher & dyn casting: multi2.cpp
Point* pp = new Point(5,10); XCoord* xp = pp; YCoord* yp = pp; cout << "pp address == " << reinterpret_cast<void*>(pp) << endl; cout << "xp address == " << reinterpret_cast<void*>(xp) cout << "yp address == " << reinterpret_cast<void*>(yp) delete pp; pp address == xp address == yp address == Result: 1/15/2019 CS3101-2, Lecture 4
54
Varieties of Casting Old-fashioned all-purpose casting: x = (int)2.5;
no longer used static casting: x = static_cast<int>(2.5) replaces old-fashioned casting produces anolog of param in new type new value usually “close” but different” usually limited to primitives 1/15/2019 CS3101-2, Lecture 4
55
Varieties of casting reinterpret casting:
p = reinterpret_cast<void*>(s); new value is the “same” as old value but of different type, i.e., void* instead of char* dynamic casting: ep = dynamic_cast Extended*>(bp); new value is usually the “same” as old value but ref interped as diff related class in hierarchy 1/15/2019 CS3101-2, Lecture 4
56
Varieties of casting – constcast.cpp
const double PI = 3.14; const double* cp = &PI; double* p = const_cast<double *>(cp); *p = 4; cout << "PI == " << PI << endl; Result: PI == 4 By casting, we can modify a const But that doesn’t make it right! 1/15/2019 CS3101-2, Lecture 4
57
Overloading and inheritance
In general, for declared derived-class insts, we call the “lowest” version of the ftn called So if ftn is defined in Base and Derived, d.ftn() hides the Base version and calls the derived version Unfortunately, Derived::ftn() hides any funtion in Base with the name ftn not based on param-lists overloading is not automatically inherited though can re-define overloaded versions in Derived 1/15/2019 CS3101-2, Lecture 4
58
Access-levels & inheritance
So far, have done public inheritance: class Derived: public Base { } public mems of Base public mems of Derived protected mems of Base protected mems of Derived private mems of Base are not inherited 1/15/2019 CS3101-2, Lecture 4
59
Access-levels & inheritance
Private inheritance: class Derived: public Base { } public and protected mems of Base private mems of Derived Private mems of Base are still not inherited Inherited methods avail internally to this class, but not visible outside or in further extensions 1/15/2019 CS3101-2, Lecture 4
60
Access-levels & inheritance
Protected inheritance: class Derived: public Base { } public and protected mems of Base protected mems of Derived As usual, private mems of Base are not inherited Inherited methods avail internally to this class and descendents, but not visible outside 1/15/2019 CS3101-2, Lecture 4
61
Access-levels & inheritance
Public inher Protected inher Private inher Public member Public in derived Protected in derived Private in derived Protected member Private member Hidden in derived 1/15/2019 CS3101-2, Lecture 4
62
Inher and consts & destrs: inhercd.cpp
If Base and Derived have constrs and destrs, when are they called? First, consider no-param constrs class Base { public: Base() { cout << "In BASE CONStructor.\n"; } ~Base() { cout << "In BASE DEStructor.\n"; } … }; Q: void main() {Base b; } produces what? A: In BASE CONStructor. In BASE DEStructor. 1/15/2019 CS3101-2, Lecture 4
63
Inher and consts & destrs: inhercd.cpp
class Extended: public Base { public: Extended() { cout << "In EXTENDED CONStructor.\n"; } ~Extended() { cout << "In EXTENDED DEStructor.\n"; } … }; Q: void main() { Extended e; } produces? A: In BASE CONStructor. In EXTENDED CONStructor. In EXTENDED DEStructor. In BASE DEStructor. 1/15/2019 CS3101-2, Lecture 4
64
Inher and consts & destrs
This should seem reasonable In creating inst first lay the foundation then build up What’s in Derived likely depends on what’s in Base In destruction, we destroy top and then bottom Dynamic mem rule of thumb: always delete in reverse new-order 1/15/2019 CS3101-2, Lecture 4
65
Inher and consts & destrs
Same for dynamic memory: Extended* e = new Extended; delete e; Produces: In BASE CONStructor. In EXTENDED CONStructor. In EXTENDED DEStructor. In BASE DEStructor. 1/15/2019 CS3101-2, Lecture 4
66
Inher and consts & destrs: inhercd2.cpp
Base* be = new Extended; delete be; ? Produces: In BASE CONStructor. In EXTENDED CONStructor. In BASE DEStructor. Trouble! - Any memory we wanted to free in ~Extended is still allocated! 1/15/2019 CS3101-2, Lecture 4
67
Inher and consts & destrs: inhercd2.cpp
We need to make ~Base virtual C++ assumes the base destr should always be called making it virtual means any subclass destructor is called before the base destr Only change: virtual ~Base(){cout << "In BASE DEStructor.\n";} Now: In BASE CONStructor. In EXTENDED CONStructor. In EXTENDED DEStructor. In BASE DEStructor. 1/15/2019 CS3101-2, Lecture 4
68
Inher and consts & destrs: inhercd2b.cpp
What if we add a param to the (only) Base constr? Base(int i) { cout << "In BASE CONStructor.\n"; } With Extended constr unchanged: Extended() { cout << "In EXTENDED CONStructor.\n"; } ? inhercd2b.cpp: In method `Extended::Extended()': inhercd2b.cpp:24: no matching function for call to `Base::Base ()' Why: In derived constr, we always call a base constr Call no-param base constr by default Create a param base constr lose automatic no-param constr 1/15/2019 CS3101-2, Lecture 4
69
Make files Defines script for compiling project Main ideas: Convenient
Only compiles what has changed/must be compiled Main ideas: comments: 1-line, begins with # macros: CC = g++ explicit rules: run arbitrary cmds Won’t cover implicit rules for dependencies (most important) target target-name : depend1, depend2, ... 1/15/2019 CS3101-2, Lecture 4
70
Make file example # comp CC = g++ # comp flags CFLAGS = -Wall –g
# var for exec list EXES = rational 1/15/2019 CS3101-2, Lecture 4
71
Make file example (continued)
#define target for all – default all: ${EXES} # lib recipes rational.o: rational.h rational.cpp driver.o: rational.h driver.cpp # exec recipes # clean target clean: rm -f core *.o *~ $(EXES) 1/15/2019 CS3101-2, Lecture 4
72
Make file dependencies
Idea: list all the immediate dependencies of the target dependency means: if it’s updated then target must be updated: e.g. object’s source file, an included header file, etc. Update recognized by difference of timestamps Dependency tree traversed recursively E.g: Makefile 1/15/2019 CS3101-2, Lecture 4
73
Make files- NB: Everything assumed to be one line Case-sensitive
Use \ to continue (as with CPP) Case-sensitive Many built-in vars: CC: do $ make -p | more to see Use ${VAR} to refer to get val of VAR A target is like a ftn call: $ make clean - Default target is all Default make filename is Makefile (no extension) Specify other filename with $ make –f filename Be careful: make-file writing can be harder than C++ programming Little feedback - Errors from missing/extra spaces, tabs For must purposes, can modify existing makefiles 1/15/2019 CS3101-2, Lecture 4
74
Next time Topic: Exceptions, Templates & the STL For next time:
Do reading assigned on web Hw3 will be up tonight Done individually! Sign in before you leave! 1/15/2019 CS3101-2, Lecture 4
Similar presentations
© 2025 SlidePlayer.com Inc.
All rights reserved.