|
| 1 | +Class 10 |
| 2 | +======== |
| 3 | + |
| 4 | +## Order of execution |
| 5 | + |
| 6 | +Base class constructors run even without a constructor for the derived class |
| 7 | + |
| 8 | +Constructors run in the order the members appear in the class |
| 9 | + |
| 10 | +### Base class constructors always run |
| 11 | + |
| 12 | +``` cpp |
| 13 | +#include <iostream> |
| 14 | +using namespace std; |
| 15 | + |
| 16 | +class Base |
| 17 | + { |
| 18 | + public: |
| 19 | + int j; |
| 20 | + |
| 21 | + Base( ) : j( 3 ) |
| 22 | + { |
| 23 | + } |
| 24 | + }; |
| 25 | + |
| 26 | +class Derived : public Base |
| 27 | + { |
| 28 | + public: |
| 29 | + int k; |
| 30 | + }; |
| 31 | + |
| 32 | +int main( ) |
| 33 | + { |
| 34 | + Derived d; |
| 35 | + cout << d.j << endl; // j = 3 |
| 36 | + cout << d.k << endl; // k = junk |
| 37 | + } |
| 38 | +``` |
| 39 | +
|
| 40 | +``` sh |
| 41 | +% derived.exe |
| 42 | +3 |
| 43 | +-858993460 |
| 44 | +``` |
| 45 | + |
| 46 | +``` cpp |
| 47 | +#include <iostream> |
| 48 | +using namespace std; |
| 49 | +class Base2 |
| 50 | + { |
| 51 | + public: |
| 52 | + int i; |
| 53 | + Base2( int i ) : i( i ) |
| 54 | + { |
| 55 | + cout << “i = " << i << endl; |
| 56 | + } |
| 57 | + }; |
| 58 | + |
| 59 | +class Base3 |
| 60 | + { |
| 61 | + public: |
| 62 | + int j; |
| 63 | + Base3( int j ) : j( j ) |
| 64 | + { |
| 65 | + cout << "j = " << j << endl; |
| 66 | + } |
| 67 | + }; |
| 68 | + |
| 69 | +class Derived4 : public Base2, Base3 |
| 70 | + { |
| 71 | + public: |
| 72 | + Derived4( int j, int i ) : |
| 73 | + Base3( j ), Base2( i ) |
| 74 | + { |
| 75 | + cout << "Derived4" << endl; |
| 76 | + } |
| 77 | + }; |
| 78 | + |
| 79 | +int main( ) |
| 80 | + { |
| 81 | + Derived4 d4( 18, 22 ); |
| 82 | + } |
| 83 | +``` |
| 84 | +
|
| 85 | +``` sh |
| 86 | +% order.exe |
| 87 | +i = 22 |
| 88 | +j = 18 |
| 89 | +Derived4 |
| 90 | +``` |
| 91 | + |
| 92 | +## Derived types |
| 93 | + |
| 94 | +A derived type is used to represent an ***is a*** relationship |
| 95 | + |
| 96 | +A derived type inherits the member functions and member variables of its parent |
| 97 | + |
| 98 | +### Upcast |
| 99 | + |
| 100 | +Permits an object of a subclass type to be treated as an object of any supercalss type |
| 101 | + |
| 102 | +### Downcast |
| 103 | + |
| 104 | +When a variable of the base calss has a value of the derived class, downcasting is possible, which casts a reference of a base class to one of its derived classes |
| 105 | + |
| 106 | +### Benefits of derived classes |
| 107 | + |
| 108 | +#### Interface inheritence |
| 109 | + |
| 110 | +*Save implementation effort by sharing functions provided by the base class* |
| 111 | + |
| 112 | +We can replace any `Triangle` object in a program with an `Isosceles` object and the program will still work |
| 113 | + |
| 114 | +##### Liskov Substitution Principle |
| 115 | + |
| 116 | +***If S is a subtype of T, then objects of |
| 117 | +type T may be replaced with objects |
| 118 | +of type S*** |
| 119 | + |
| 120 | +***Functions that use pointers to base |
| 121 | +classes can use objects of derived |
| 122 | +classes without knowing it*** |
| 123 | + |
| 124 | +This is upcasting |
| 125 | + |
| 126 | +#### Interface inheritence |
| 127 | + |
| 128 | +*Allow different derived classes to be used interchangeably thorugh the interface provided by a common base class* |
| 129 | + |
| 130 | +### Static type |
| 131 | + |
| 132 | +Often, the compiler can tell which derived type is needed |
| 133 | + |
| 134 | +This is called the *static type* |
| 135 | + |
| 136 | +``` cpp |
| 137 | +Triangle t( 3, 4, 5 ); |
| 138 | +t.print( ); |
| 139 | + |
| 140 | +Triangle *t_ptr = &t; |
| 141 | +t_ptr->print( ); |
| 142 | + |
| 143 | +Isosceles i( 1, 12 ); |
| 144 | +i.print( ); |
| 145 | + |
| 146 | +Isosceles *i_ptr = &i; |
| 147 | +i_ptr->print( ); |
| 148 | +``` |
| 149 | +
|
| 150 | +``` sh |
| 151 | +./a.out |
| 152 | +Triangle: a=3, b=4, c=5 |
| 153 | +Triangle: a=3, b=4, c=5 |
| 154 | +Isosceles: base=1, leg=12 |
| 155 | +Isosceles: base=1, leg=12 |
| 156 | +``` |
| 157 | + |
| 158 | +### Dynamic type |
| 159 | + |
| 160 | +Other times, the type is not known until run time |
| 161 | + |
| 162 | +This is called the *dynamic type* |
| 163 | + |
| 164 | +#### Example |
| 165 | + |
| 166 | +``` cpp |
| 167 | +Triangle * ask_user( ); |
| 168 | + |
| 169 | +int main( ) |
| 170 | + { |
| 171 | + Triangle *t = ask_user( ); //enters "Isosceles" |
| 172 | + t->print( ); |
| 173 | + } |
| 174 | +``` |
| 175 | +
|
| 176 | +The static type of `t` is `Triangle *` |
| 177 | +
|
| 178 | +The dynamic type of `t` is `Isosceles *` |
| 179 | +
|
| 180 | +#### `new` |
| 181 | +
|
| 182 | +To create an object that won't go out of scope when the function returns, we use `new` |
| 183 | +
|
| 184 | +``` cpp |
| 185 | +Triangle *ask_user( ) |
| 186 | + { |
| 187 | + cout << "Triangle, Isosceles " |
| 188 | + "or Equilateral?" << endl; |
| 189 | + string s; |
| 190 | + cin >> s; |
| 191 | + |
| 192 | + if ( s == "Triangle" ) |
| 193 | + return new Triangle( 3,4,5 ); |
| 194 | + if ( s == "Isosceles" ) |
| 195 | + return new Isosceles( 1,12 ); |
| 196 | + if ( s == "Equilateral" ) |
| 197 | + return new Equilateral( 5 ); |
| 198 | + |
| 199 | + cout << "Unrecognized shape '" |
| 200 | + << s << "'\n"; |
| 201 | + exit( 1 ); //crash |
| 202 | + } |
| 203 | +``` |
| 204 | + |
| 205 | +Always safe to upcast, so returning `Isosceles` or `Equilateral` is safe |
| 206 | + |
| 207 | +#### `delete` |
| 208 | + |
| 209 | +Always goes together with `new` |
| 210 | + |
| 211 | +``` cpp |
| 212 | +int main( ) |
| 213 | + { |
| 214 | + Triangle *t = ask_user( ); |
| 215 | + //enters "Isosceles" |
| 216 | + t->print( ); |
| 217 | + delete t; |
| 218 | + } |
| 219 | +``` |
| 220 | +
|
| 221 | +``` sh |
| 222 | +./a.out |
| 223 | +Triangle, Isosceles or Equilateral? |
| 224 | +Isosceles |
| 225 | +Triangle: a=1 b=12 c=12 |
| 226 | +``` |
| 227 | + |
| 228 | +* `t` can change types at runtime, in other words it is ***polymorphic*** |
| 229 | +* We can use the ***virtual function*** mechanism in C++ to check the *dynamic type* of `t` at runtime and call the correction version of `print()` |
| 230 | + |
| 231 | +### Polymorphism |
| 232 | + |
| 233 | +*The ability to associate many behaviors with one function name* |
| 234 | + |
| 235 | +A polymorphic type is any type with a virtual function |
| 236 | + |
| 237 | +Virtual functions are the C++ mechanism used to implement polymorphism |
| 238 | + |
| 239 | +#### Example |
| 240 | + |
| 241 | +`virtual` means "check the dynamic type at runtime, then select the correct `print( )` member funciton |
| 242 | + |
| 243 | +`virtual` is inherited so the overridden `print( )` in `Isosceles` and `Equilateral` will automatically become `virtual` |
| 244 | + |
| 245 | +``` cpp |
| 246 | +class Triangle |
| 247 | + { |
| 248 | + virtual void print( ) const |
| 249 | + { |
| 250 | + : |
| 251 | + } |
| 252 | + : |
| 253 | + }; |
| 254 | +``` |
| 255 | + |
| 256 | +Now the orignal program works correctly |
| 257 | + |
| 258 | +``` sh |
| 259 | +./a.out |
| 260 | +Triangle, Isosceles or Equilateral? Isosceles |
| 261 | +Isosceles: base=1 leg=12 |
| 262 | +``` |
| 263 | + |
| 264 | +#### Exercise |
| 265 | + |
| 266 | +`Rectangle` *is a* `Shape` |
| 267 | + |
| 268 | +``` cpp |
| 269 | +class Shape |
| 270 | + { |
| 271 | + public: |
| 272 | + virtual double area( ) const {/*...*/} |
| 273 | + virtual void print( ) const {/*...*/} |
| 274 | + }; |
| 275 | +class Triangle : public Shape {/*...*/}; |
| 276 | +class Rectangle : public Shape {/*...*/}; |
| 277 | +``` |
| 278 | +
|
| 279 | +Although our code is perfectly legal C++, `Shape` has no member variables, and something like |
| 280 | +
|
| 281 | +``` cpp |
| 282 | +Shape s; |
| 283 | +s.print( ); |
| 284 | +``` |
| 285 | + |
| 286 | +makes no sense and will not compile! |
| 287 | + |
| 288 | +A shape is an abstract idea, and our shape should only be an interface to ensure that all shapes behave the same way |
| 289 | + |
| 290 | +### Abstract class |
| 291 | + |
| 292 | +You cannot create an instance of an abstract class, which is what we want for a `Shape` |
| 293 | + |
| 294 | +An abstract class will force derived types to all behave the same way |
| 295 | + |
| 296 | +### Pure virtual functions |
| 297 | + |
| 298 | +*Each method is declared a `virtual` function and "Assigns" a 0 to each of these `virtual` functions* |
| 299 | + |
| 300 | +#### Example |
| 301 | + |
| 302 | +``` cpp |
| 303 | +class Shape |
| 304 | + { |
| 305 | + public: |
| 306 | + virtual double area( ) const = 0; |
| 307 | + void print( ) const = 0; |
| 308 | + }; |
| 309 | +``` |
| 310 | +
|
| 311 | +* You *can* create a pointer to an abstract class, and then assign the pointer to a *concrete class* derived from the base class |
| 312 | +
|
| 313 | +``` cpp |
| 314 | +Rectangle r(2,4); //concrete derived type |
| 315 | +Shape *s = &r; //OK, Rectangle is a Shape |
| 316 | +s->print(); //virtual, so correct version |
| 317 | + //of print() is called |
| 318 | +``` |
| 319 | + |
| 320 | +``` sh |
| 321 | +./a.out |
| 322 | +Rectangle: a=2 b=4 |
| 323 | +``` |
| 324 | + |
| 325 | +### Factory functions |
| 326 | + |
| 327 | +*Creates objects for another programmer who doesn't need to know their actual types* |
| 328 | + |
| 329 | +``` cpp |
| 330 | +//EFFECTS: asks user to select a shape |
| 331 | +// returns a pointer to correct object |
| 332 | +Shape * ask_user( ); |
| 333 | +``` |
| 334 | +
|
| 335 | +`ask_user( )` is an example of a *factory function* |
| 336 | +
|
| 337 | +### Upcast |
| 338 | +
|
| 339 | +Conversion from `Isosceles` to `Shape` is called an *upcast* |
| 340 | +
|
| 341 | +In `ask_user( )`, the type conversion is automatic, an *implicit cast* |
| 342 | +
|
| 343 | +### Downcast |
| 344 | +
|
| 345 | +*A cast from one type in the class hierarchy to a lower one* |
| 346 | +
|
| 347 | +Since `Shape` might not be `Isosceles`, this cas cannot happen automatically |
| 348 | +
|
| 349 | +#### `dynamic_cast` |
| 350 | +
|
| 351 | +`dynamic_cast<T*>(ptr)` downcasts `ptr` to type `T*`, if possible. Otherwise, it returns 0. |
| 352 | +
|
| 353 | +This is usually a symptom of a poor design |
0 commit comments