Upcoming SlideShare
×

Lecture 2 constructors:composition

294 views
175 views

Published on

none

Published in: Technology, Sports
0 Likes
Statistics
Notes
• Full Name
Comment goes here.

Are you sure you want to Yes No
• Be the first to comment

• Be the first to like this

Views
Total views
294
On SlideShare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
5
0
Likes
0
Embeds 0
No embeds

No notes for slide
• Lecture 2 constructors:composition

1. 1. Wednesday, Jan 11 Collect Academic Integrity Forms Constructors Destructors Class Composition Composition with Initializer Lists A few final topics required for Project #1 • Learning how to use the VC debugger (time permitting) • • • • • • th
2. 2. Constructors: Class Initialization Every class should have an initialization function that can be used to reset new variables before they’re used. class CSNerd { public: void Init(int PCs, bool usesMac) { 2 false m_numPCs = PCs; // # of PCs owned void Init(int PCs, bool usesMac) Ack! How can david { m_macUser = usesMac; m_numPCs = PCs; m_macUser = usesMac; } someone have -32 } computers? 10*2 = 20 int int getNerdScore(void) { if(m_macUser == true) return(0); return(10 * m_numPCs); } m_numPCs -32 m_macUser getNerdScore(void) { false if(m_macUser == true) return(0); //not nerdy; “artistic” return(10 * m_numPCs); } main() { private: CSNerd david; int m_numPCs; Right! Our programmer might But all simple variables Well, remember, there’s one problem david.Init(2,false); // geeky So if you forget to call the Init bool m_macUser; forget with such anInit to call the Init (e.g., ints, bools, CSNerd’s member cout << david.getNerdScore(); function, your etc.) in C++ start out }; function before values. Not variables will have randomusing is it? } with random function… What the values unless they’re what you’d want. variable…Here’s ahappen? explicitly What’ll hint! initialized!
3. 3. class CSNerd { public: void Init(int PCs, bool usesMac) { m_numPCs = PCs; m_macUser = usesMac; } int getNerdScore(void) { if(m_macUser == true) return(0); return(10 * m_numPCs); } private: int m_numPCs; bool m_macUser; }; Constructors Wouldn’t it be great if C+ + would guarantee that every time we create a new class variable, it’ll be auto-initialized? Well, as it turns out, that’s exactly what the C++ constructor does! main() { CSNerd david; cout << david.getNerdScore(); }
4. 4. class CSNerd Since the constructor is { called automatically any time public: you void Init(int define bool usesMac) CSNerd (int PCs, a new variable… { m_numPCsthere’s no chance of a new = PCs; variable being m_macUser = usesMac; uninitialized accidentally. } int getNerdScore(void) { if(m_macUser == true) return(0); return(10 * m_numPCs); } private: int m_numPCs; bool m_macUser; }; Constructors main() { (3,true); CSNerd chen;3 true chen.Init(3,true); cout << chen.getNerdScore(); } true 3 Instead constructor is The of being called CSNerd(int chen called PCs, bool usesMac) { Init, the=automatically constructor m_numPCs PCs; m_macUser = usesMac; functiontime the same } every has you create int getNerdScore(void) name as the class! { a new instance of if(m_macUser == (Confusing, true) huh?) return(0); your class. return(10 * m_numPCs); } m_numPCs m_macUser A constructor is a special member function that automatically initializes every new variable you create of that class.
5. 5. And remember to also include just the function header, followed by a class CSNerd semicolon, in the class { declaration itself. Constructors class CSNerd public: { CSNerd(int PCs, bool usesMac); public: { CSNerd(int PCs, bool usesMac) m_numPCs Boy, isn’t that syntax ugly? = PCs; { m_macUser = usesMac; m_numPCs = PCs; Just remember, to define } m_macUser = usesMac; ... a constructor outside the } return(true); //illegal! class declaration: } private: ... private: int m_numPCs; bool m_macUser; }; int m_numPCs;use the class name, bool m_macUser; by ::, followed by followed }; the class name. CSNerd:: You can define the LikeconstructorA theno return is a special constructor To summarize…inhas class constructor type! It’s not The all other member functions, the declaration (see not void, int, have new constructor’sit’s above)…be defined inside function in your logic can initializes athe The constructor function allowed! Notice class thatMUST or bool. Or, outside the class variable whennot first created. SAME NAME AS class to return a value. or outside the alloweddeclaration. declaration, like this… And thus it’s its THE CLASS!
6. 6. Constructors If a constructor requires parameters: class CSNerd { public: CSNerd(int PCs, bool usesMac ) { m_numPCs = PCs; 0; m_macUser = usesMac; false; } int getNerdScore(void) { if(m_macUser == true) return(0); return(10 * m_numPCs); } private: int m_numPCs; bool m_macUser; }; You must to provide values for those parameters when you create a new variable: main() { CSNerd ed(1,true); // OK ed(1,true);//invalid! CSNerd alan; // invalid! OK! }
7. 7. Constructors class CSNerd { public: CSNerd(int PCs, bool usesMac = true ) { m_numPCs = PCs; m_macUser = usesMac; } int getNerdScore(void) { if(m_macUser == true) return(0); return(10 * m_numPCs); } private: int m_numPCs; bool m_macUser; }; Just like any C++ function, a constructor can have one or more default parameters… main() { 1 false CSNerd lyn(1,false); 5 CSNerd ned(5); // OK! CSNerd dave; //invalid! cout << lyn.getNerdScore(); }
8. 8. C++: “I’m confused! Should I call this class CSNerd constructor with default parameters…” { public: CSNerd(int PCs, bool usesMac = true) CSNerd() CSNerd(int PCs=1, bool usesMac=true) Your class can have many { different constructors. C++: m_numPCs = PCs; m_numPCs = 1; “…or should I call this (this is called overloading m_macUser = false; = usesMac; constructor with no parameters?” m_macUser constructors) } main() int getNerdScore(void) { { Constructors if(m_macUser == true) return(0); return(10 * m_numPCs); } CSNerd lyn(1,false); CSNerd ned(5); // OK! CSNerd dave; // OK!!! //invalid! cout << lyn.getNerdScore(); } private: int m_numPCs; bool m_macUser; }; One more thing: If you have 2 or more constructors, they cannot have the exact same parameters/types.
9. 9. Constructors If you don’t define any constructors at all… class CSNerd { public: then C++ will provide an implicit, default constructor for you that basically does nothing! int getNerdScore(void) by compiler CSNerd() // generated { if(m_macUser == true) // I do nothing at all. //return(0); I’m not worthy!!!!! } return(10 * m_numPCs); } private: int m_numPCs; bool m_macUser; }; main() { CSNerd carey; // OK } cout << carey.getNerdScore(); //?? In this case, your member variables are never initialized.
10. 10. Constructors & Arrays class CSNerd { public: CSNerd(int PCs, bool usesMac = true) { m_numPCs = PCs; m_macUser = usesMac; } 1 false 1 false 1 false 1 false CSNerd() { m_numPCs = 1; m_macUser = false; } ... private: int m_numPCs; bool m_macUser; When you define an array, the your class, If you want to have an array of }; constructor is run on every your class must have a constuctor that element in the no arguments! requires array! main() { CSNerd lec1[4]; cout << lec1[0].getNerdScore(); }
11. 11. Constructors There are several different ways to initialize your member variables in a constructor. Here’s the easiest way: class CSNerd { public: CSNerd(int PCs, bool usesMac) { m_numPCs = PCs; Class CSNerd m_macUser = usesMac; { } public: ... Or, if you’re really masochistic, you can do a little of both. Here’s a more complex way that does the same thing… This is called an “initializer list” CSNerd(int PCs, bool usesMac) { : m_numPCs (PCs) , m_macUser (usesMac) } ... // I don’t need to do anything! m_macUser = usesMac;
12. 12. Constructor Challenge class CSProf { public: // version #4 #3 #2 #1 39) CSProf(const string &name, int age = 39); { : m_name(name), m_age(age) { m_name = name; // do nothing else m_age = age; } Define a constructor that initializes a CSProf. The user can specify the name of the prof and his/her age. Notice youIf the user omits the don’t put theage, then the prof’s default age is 39. “= 39” here too! ... private: string m_name; int m_age; }; // version #2 #4 CSProf::CSProf(const string &name, int age) { : m_name(name), m_age(age) { m_name = name; // do nothing else m_age = age; } int main(void) { CSProf a(“David”,52); CSProf b(“Carey”); }
13. 13. When Constructors are Called A constructor is called any time you create a new variable for a class. A constructor is called N times when you create an array of size N. A constructor is called when you use new to dynamically allocate a new variable or an array. (we’ll learn new later) The constructor is not called when you just define a pointer variable. main() { CSNerd carey(3,false), bill; // called once for each var CSNerd arr[52]; // constructor’s called 52 times CSNerd *ptr = new CSNerd(1,3); // c’tor called once CSNerd *dyn = new CSNerd[6]; // c’tor called 6 times CSNerd *justAPtr; // c’tor is NOT called
14. 14. Class Challenge Name all the times the CSNerd constructor is called in this example… class CSNerd { public: CSNerd() { m_numPCs = 1; m_macUser = true; } ... private: int m_numPCs; bool m_macUser; }; void foo() { CSNerd *herbert = new CSNerd; } void main(void) { int j; CSNerd *ptrToNerd; CSNerd xavier; for (j=0;j<3;j++) { CSNerd a[5]; foo(); } }
15. 15. Destructors Just as every class has a constructor, every class also has a destructor function (one and only one of these). The job of the destructor is to de-initialize or destruct each variable of a class when the variable goes away. Destructors return nothing and take NO parameters. class SomeClass { public: SomeClass(); // ~SomeClass() ~SomeClass(); // { private: // ... destructor code // }; } private: SomeClass::~SomeClass() ... // { }; // destructor code } constructor destructor! whatever whatever To define a d’tor function, you use the tilde ~ character in front of the class’s name: Or define the destructor function outside your class… and just add a function header inside the class declaration.
16. 16. Ben’s Balance: \$990 \$1290 \$1270 \$1280 \$1260 \$1000 PayPerUseBank.com (we charge by the minute!) Ben we need a Destructor? Why doDover CSRules Here we have a class that allows ib disappears banking. Hmmm… us to do internetwhen \$300 CSRules Ben Dover Let’s see but the user main exits,what happens when class InternetBanking Wow – we just logged we use this class without a forgot to call stopBanking, { destructor. public:on and they’re so our PC is still connected voidalready charging startBanking(string nm,string pw) Of course, had we added to (and being charged by) a { fees! m_ic.connect(“payperusebank.com”); destructor… None of this the bank! m_ic.send(nm); // send name m_ic.send(pw); // send password } would have happened… int main(void) void stopBanking(){ m_ic.disconnect(); } { Now, when ourib ib void deposit(float amount) { … } InternetBanking ib; variable goes away, ~InternetBanking() C++ will automatically { string name, pw; m_ic.disconnect(); cin >> name >> pw; call it’s destructor!!! } ib.startBanking(name,pw); private: ib.deposit(300); InternetConnection m_ic; } };
17. 17. When must you have a destructor? Any time a class… Allocates data using the new command Opens a disk file Opens a network connection Your class must have a destructor that… Frees the allocated memory Closes the disk file Disconnects the network connection Don’t forget or you’ll FAIL!
18. 18. Destructors So when is a destructor called anyway? Local function variables are destructed any time a function exits. Local variables defined in a block are destructed when the block finishes. Dynamically allocated variables are destructed when delete is called before the memory is actually freed by the operating system. void Dingleberry(void) { SomeClass c; Oh, and what about arrays? When an array goes away, the destructor is called for every item in the array (just like construction). for (int j=0;j<10;j++) { SomeClass v; // do something } v’s destructor is called each time we hit the end of the block InternetBanking *ib = new InternetBanking(“Carey”,”MyPassword”); // do something delete ib; The d’tor is called for ib by C++ here c’s destructor is called. }
19. 19. Class Challenge #2 Name all the times the CSNerd destructor is called in this example… class CSNerd { public: CSNerd() { m_numPCs = 1; m_macUser = true; } ~CSNerd() { void foo() { CSNerd a, *b; } Question: How could we make our program more efficient? void main(void) where this is (Hint: Look { pointing) int j; CSNerd *ptr, x; ptr = new CSNerd; for (j=0;j<3;j++) { CSNerd a[5]; foo(); } delete ptr; cout <<“Argh! I’m dying. Where’s my iphone?”; } ... private: int m_numPCs; bool m_macUser; }
20. 20. Can you guess? Programming Language Inventor Or Serial Killer See if you can guess who uses a keyboard and who uses a chainsaw!
21. 21. Class Composition Class composition: If a class contains one or more classes as member variables, then you are using class composition: class Valve class GasTank { … }; class Car { ... private: GasTank myTank; }; { … }; Class Composition Class Composition Class Composition class Heart { … }; private: Valve m_valves[4]; class TinMan { ... private: Heart m_myHeart; };
22. 22. class Stomach { public: Stomach() { gas = 0; } ~Stomach() { cout << “boom!”; } void eat() { cout << “yummy”; gas ++; } void fart() { gas--; cout << “pffft!”; } private: }; int gas; class Nerd { public: Nerd() { thought=“Waaa”; belly.eat(); } void meetCuteGirl(string &herName) { } thought = “H.O.T. HOT!”; belly.fart(); ~Nerd() { thought=“Argh”; belly.eat( ); } private: string thought; Stomach belly; }; Composition In this example, our Nerd class holds a Stomach variable (every nerd has a stomach) When using composition, the outer class (Nerd) can use all of the public functions of the contained variable (belly) in: • The outer class’s constructor(s) • The outer class’s functions • The outer class’s destructor Ok, that was easy… But now let’s look into how construction and destruction work with composition…
23. 23. class Stomach { public: Stomach() { gas = 0; } ~Stomach() { cout << “boom!”; } void eat() { cout << “yummy”; gas ++; } void fart() { gas--; cout << “pffft!”; } private: }; int gas; Composition and Construction When we create a Stomach variable, its constructor is called. And when the variable goes away, its destructor is called… s gas int main(void) { Stomach s; ... // use s } boom! 0
24. 24. class Stomach { public: Stomach() { gas = 0; } ~Stomach() { cout << “boom!”; } void eat() { cout << “yummy”; gas ++; } Hint: void fart() { gas--; cout << “pffft!”; } private: }; int gas; class Nerd { When we How can Nerd’screate a Nerd variable, belly constructor use the Nerd’s constructor variable if belly must be called. hasn’t been constructed? But our Nerd contains a Stomach, so its constructor must be called too! public: Nerd() { thought=“Waaa”; belly.eat( ); } void meetCuteGirl(string &herName) { } So which constructor is called first? Nerd’s or Stomach’s? thought = “H.O.T. HOT!”; belly.fart(); ~Nerd() { thought=“Argh”; belly.eat(); } private: string thought; Stomach belly; }; Composition and Construction int main(void) { Nerd david; ... }
25. 25. class Stomach { public: Composition and Since theConstruction Stomach was Stomach() { gas = 0; } ~Stomach() { cout << “boom!”; } C++ constructs the void eat() { cout << “yummy”; gasalready constructed, our ++; } contained class void fart() { gas--; cout << “pffft!”; } class can use it in its first, Nerd private: }; int gas; class Nerd { public: Nerd() { thought=“Waaa”; belly.eat( ); } void meetCuteGirl(string &herName) { } thought = “H.O.T. HOT!”; belly.fart(); ~Nerd() { thought=“Argh”; belly.eat(); } private: string thought; Stomach belly; }; and the outer class second. constructor! So our Stomach is constructed first and then Nerd is constructed after. david thought “Waaa” belly gas int main(void) { Nerd david; ... } 1 0 yummy!
26. 26. class Stomach { public: Stomach() { gas = 0; } ~Stomach() { cout << “boom!”; } void eat() { cout << “yummy”; gas ++; } void fart() { gas--; cout << “pffft!”; } private: }; int gas; class Nerd { public: Nerd() } thought = “H.O.T. HOT!”; belly.fart(); ~Nerd() { thought=“Argh”; belly.eat(); } private: string thought; Stomach belly; }; Now what happens when our Nerd is destructed? Destruction happens in the reverse order. The outside destructor runs first, then the contained destructor runs second. the Stomach has not Since been destructed yet, it { thought=“Waaa”; belly.eat( ); still bedavid by Nerd! can } used thought “Waaa” “Argh” void meetCuteGirl(string &herName) { Composition and Destruction belly int main(void) { Nerd david; ... } 1 gas 0 yummy boom!
27. 27. Class Composition & Construction/Destruction What does it print? class Stomach { public: Stomach(void) { cout<<“Mmm foodn”; } ~Stomach() { cout << “Poof!n”; } }; class Nerd { public: Nerd(void) { cout<<“I like integralsn”; } ~Nerd() { cout << “Oh derivatives!n”; } private: Stomach belly; }; int main(void) { Nerd herbert; ... } Mmm food I like integrals Oh derivatives Poof!
28. 28. Class Composition Let’s examine a slightly different Stomach class. Here’s the Stomach’s specification: • When a Stomach is constructed, you must specify how many farts it starts with. It prints out the starting number of farts • You can add farts to a Stomach with an eat method • When a Stomach is destructed, it farts N times
29. 29. Class Composition class Stomach { public: Stomach(int startFarts) { farts = startFarts; cout << “Start farts: “ << farts; } void eat() { farts ++; } ~Stomach() { while (farts-- > 0) cout << “pffft!”; } private: int farts; }; int main(void) { Stomach a(5); // 5 farts Stomach b; // ??? }   a.eat(); // 6 farts ... Now, what happens if we want to use our new Stomach class in a Nerd?
30. 30. Class Composition class Stomach { public: Stomach(int startFarts) { farts = startFarts; cout << “Start farts: “ << farts; } void eat() { farts ++; } ~Stomach() { while (farts-- > 0) cout << “pffft!”; } private: int farts; }; class Nerd { public: Nerd(void) { thought = "CS"; } ~Nerd() { cout << “Argh “ << thought; } private: Stomach belly; string thought; }; Will this work? NO!
31. 31. But doesn’t Class Composition specify the required starting Nerd class class Stomach # of farts!!! { { public: public: Nerd(void) Stomach(int startFarts) { { thought = "CS"; Your Nerd has a farts = startFarts; } cout << “Start farts: “ << farts; Stomach } ~Nerd() variable… { cout << “Argh “ << thought; } void eat() { private: farts ++; Stomach belly; } string thought; }; ~Stomach() { while (farts-- > 0) This won’t work because you cout << “pffft!”; } private: int farts; }; must pass in a # of farts any time you construct a Stomach. But our Nerd doesn’t do that!
32. 32. Class Composition class Stomach { public: Stomach(int startFarts) { farts = startFarts; cout << “Start farts: “ << farts; } void eat() { farts ++; } ~Stomach() { while (farts-- > 0) cout << “pffft!”; } private: int farts; }; class Nerd { public: Nerd(void) { thought = "CS"; } ~Nerd() { cout << “Argh “ << thought; } private: Stomach belly;(10); // Good? string thought; }; Q: How can we fix our Nerd? A: By properly specifying the # of farts when we construct a Stomach??
33. 33. Class Composition This means: “Before you construct the Nerd… class Nerd class Stomach first construct its Stomach by { passing in a value of 10.” { public: public: Nerd(void) : belly(10) Stomach(int startFarts) { { thought = "CS"; farts = startFarts; } cout << “Start farts: “ << farts; } ~Nerd() { cout << “Argh “ << thought; } void eat() { private: farts ++; Stomach belly; } string thought; }; ~Stomach() { while (farts-- > 0) A: You MUST explicitly cout << “pffft!”; } private: int farts; }; construct a contained class variable using an initializer list in your constructor.
34. 34. Class Composition belly class Nerd farts 10 { "CS" thought public: Nerd(void) : belly(10) { thought = "CS"; } class Stomach { 10 public: Stomach(int startFarts) { farts = startFarts; cout << “Start farts: “ << farts; } ~Nerd() { cout << “Argh “ << thought; } void eat() { farts ++; } ~Stomach() { while (farts-- > 0) cout << “pffft!”; } private: int farts; }; carey private: Stomach belly; string thought; }; int main(void) { Nerd carey; } ... Start farts: 10
35. 35. Class Composition In our current Nerd, the Stomach always starts out with 10 farts of gas… Challenge: Modify the Nerd class so you can pass in the number of farts of gas that the Nerd starts with. class Nerd { public: Nerd(void) : belly(10) { thought = “CS”; } ~Nerd() { cout << “Argh “<< thought; } private: Stomach belly; string thought; }; class Nerd { public: Nerd( intvoid ) : belly( farts ) 10 farts { thought = “CS”; } ~Nerd() { cout << “Argh “<< thought; } private: Stomach belly; string thought; };
36. 36. Class Composition class Stomach { public: 3 Stomach(int startFarts) { farts = startFarts; cout << “Start farts: “ << farts; } void eat() { farts ++; } ~Stomach() { while (farts-- > 0) cout << “pffft!”; } private: int farts; }; Carey belly farts class Nerd thought { public: 3 3 Nerd(int farts) : belly(farts) { thought = "CS"; } 3 “CS” ~Nerd() { cout << “Argh “ << thought; } private: Stomach belly; string thought; }; int main(void) { Nerd Carey(3); } ... Start farts: 3
37. 37. Class Composition Challenge: Our current Nerd only has one Stomach. Create a NerdyCow class that has two Stomachs and a thought. When you construct the NerdyCow, you should be able to specify how many farts start out in each stomach and its thought. class Nerd { public: Nerd(int farts) : belly(farts) { thought = "CS"; } ~Nerd() { cout << “Argh “ << thought; } private: Stomach belly; string thought; }; class NerdyCow { public: NerdyCow(int f1, int f2, string &idea) : belly1(f1), belly2(f2) { } thought = idea; ~NerdyCow() { cout << “Moooo!”; } private: Stomach belly1; Stomach belly2; string thought; };
38. 38. earl Class Composition class Stomach { public: 7 8 Stomach(int startFarts) { farts = startFarts; cout << “Start farts: “ << farts; } void eat() { farts ++; } ~Stomach() { while (farts-- > 0) cout << “pffft!”; } private: int farts; }; belly1 farts class NerdyCow { public: 7 7 belly2 farts 8 8 thought “milk” “milk” NerdyCow(int f1, int f2, string &idea) : belly1(f1), belly2(f2) { } thought = idea; ~NerdyCow() { cout << “Moooo!”; } private: Stomach belly1; Stomach belly2; string thought; }; Start farts: 7 Start farts: 8 int main(void) { NerdyCow earl(7,8, “milk”); } ...
39. 39. A Few Final Topics Let’s talk about three final topics that you’ll need in order to solve your Project #1…
40. 40. Topic #1: Include Etiquette A. Never include a CPP file in another .CPP or .H file. file1.cpp void someFunc(void) { cout << “I’m cool!” } file2.cpp #include “file1.cpp” void otherFunc(void) { cout << “So am I!” someFunc(); } You’ll get linker errors. Only include .H files within a .CPP file.
41. 41. Topic #1: Include Etiquette B. Never put a “using namespace” command in a header file. someHeader.h #include <iostream> using namespace std; hello.cpp #include “someHeader.h” // BUT I DON’T WANT THAT // NAMESPACE! YOU BASTARD! int main() { cout << “Hello world!”; } So this is bad… This is called “Namespace Pollution” Why? The .H file is forcing CPP files that include it to use its namespace. And that’s just selfish. Instead, just move the “using” commands into your C++ files.
42. 42. alcohol.h class Alcohol { ... }; student.h #include “redbull.h” "alcohol.h" class Student { ... private: RedBull bottles[5]; Alcohol }; main.cpp #include “student.h” #include "alcohol.h" int main() { Student larry; Alcohol vodka; Topic #1: Include And in fact, this works right Etiquette now, because student.h does include alcohol.h. C. Never assume that a header file will include some other header file will you. Now your main.cpp fileif the author But what happens forcompile correctly regardlessdecides to change of student.h of what’s contained in the otherimplementation? What can you do? it’s header files it uses! Always DIRECTLY include the header files you need RIGHT where you need them. main.cpp defines an Alcohol variable but Utoh! Now main.cpp no longer it doesn’t #include"alcohol.h". In this case, main.cpp has a compiles!  Why? Student variable student.h will It assumes that and an Alcohol variable. So doesn’t it. just Because itit shouldinclude Why?include alcohol.h for include both of their header files. alcohol.h but it defines an Alcohol variable! cout << larry << “ is drinking “ << vodka; }
43. 43. Topic #2: Preprocessor Directives C++ has several special commands which you sometimes need in your programs. Command 1: #define You can use the #define command to define new constants: file.cpp #define PI 3.14159 #define FOOBAR void someFunc(void) { cout << PI; } You can also use #define to define a new constant without specifying a value! Why you ask? We’ll see!
44. 44. Preprocessor Directives Command 2: #ifdef and #endif Since a constant has You can use the #ifdef command to check if FOOBAR was was defined I’llI’ll NOT defined been defined already… The compiler only compiles the code between the #ifdef and the #endif if the constant was defined. compile the code ignore the code until #endif. until #endif. file.cpp #define FOOBAR #ifdef FOOBAR /* void someFunc(void) { cout << PI; } #endif */
45. 45. Preprocessor Directives Command 2: #ifndef and #endif Since FOOBAR was Since FOOBAR You can use the #ifndef command to check if a constant has NOT defined was defined I’llI’ll NOT been defined already… compile the code ignore the code until #endif. until #endif. The compiler file.cpp only compiles the code #define FOOBAR between the #ifndef and the #endif #ifndef FOOBAR /* if the constant was NOT defined. void someFunc(void) { cout << PI; } #endif */
46. 46. Separate Compilation calc.h class Calculator { public: void compute(); ... }; calc.cpp #include “calc.h” Can anyone see what the problem is? student.h #include “calc.h” class Student { public: void study() ... private: Calculator myCalc; }; main.cpp #include “student.h” int Calculator::compute() student.cpp #include “calc.h” { #include “student.h” main.cpp ... int main() #include “student.h” } int Student::study() { { Student int main()grace; cout << myCalc.compute();{ Calculator hp; } ... Student grace; When–using class composition,(and Now something interesting it Then you can use them like this in bad) happens if I each class in a in helps to define use both classes your main program… separate pair of .cpp/.h files. my main program. Let’s see! grace.study(); ... hp.compute(); grace.study(); }
47. 47. Separate Compilation calc.h student.h So what’s problem? class Calculator the#include “calc.h” { class Student public: Well, since main.cpp and student.h both { void compute(); included calc.h, we ended up with two public: ... That’s bad! }; definitions of Calc! void study() ... private: calc.cpp This can result in a compiler error! Calculator myCalc; #include “calc.h” }; main.cpp #include “student.h” #include “calc.h” int main() { Student grace; Calculator hp; ... grace.study(); hp.compute(); } main.cpp class Student we fix it, you ask? #include “student.h” int Calc::compute() So how do student.cpp { #include “calc.h” #include “student.h” main.cpp public: ... Here’s how… int main() #include “student.h” } void study() int Student::study() ... { { private: Student int main()grace; Calculator myCalc; cout << myCalc.compute();{ Calculator hp; } }; ... Student grace; grace.study(); ... Your main program first includes calc.h… again! Then student.h then …and finally, main.cpp hp.compute(); grace.study(); includes student.h… includes calc.h! }
48. 48. main.cpp Separate Compilation calc.h when the Now, #ifndef CALC_H compiler compiles #ifndef STUDENT_H #define STUDENT_H #ifndef CALC_H #define CALC_H class Calculator { public: void compute(); }; #endif // for CALC_H Once the compilation is done, the compiler Compiler: Since no Compiler: all user discards The of the one’s defined STUDENT_H, no Compiler: SinceI’ll #has defined commands... this code, it will ignore the redefined definitions! Compiler: The user STUDENT_H. I’ll keep compiling. one’s defined class Calculator has defined remember keep CALC_H, I’llthat! class Student student.h { Compiler: CALC_H. I’ll { And you compiling. that only can see public: public: #ifndef STUDENT_H remember that! STUDENT_H has void study() one copy of each definition void compute(); private: Compiler: Ok, I just been defined! #define STUDENT_H Calculator myCalc; ... finished with the is now included! }; }; CALC_H has been #include “calc.h” #ifndef CALC_H #endif // for STUDENT_H defined! block of code… #endif // for CALC_H #ifndef CALC_H class Student #define CALC_H Compiler: Ok, I just class Calculator { finished Ok, I this { MakeCompiler:with the any sure you do just public: public: finished with the void compute(); void study() you Compiler: The time #ifndef CALC_H files define header block of constant }; #ifndef STUDENT_H An Add “include#endif a for CALC_H So what would our // include guard is CALC_H code… ... from now on… block of defined. was alreadycode… int special check that fully-compiled main() private: guards” to each { I’ll ignore the code Student prevents duplicate grace; Calculator myCalc; your #endif!safer! program look like It makes the code header file. Calculator hp; until }; ... header inclusions. now? #endif // for STUDENT_H #define CALC_H grace.study(); hp.compute();
49. 49. alcohol.h class Alcohol { public: void drink() { cout << “glug!”; } }; student.h #include “alcohol.h” class Student { public: void beIrresponsible(); ... private: Alcohol *myBottle; }; Last Topic: Knowing WHEN to Include .H Files You might think that any time you refer to a class, you should include it’s .h file So I need to include first… Right? alcohol.h, right? Well, not so fast! I use the Alcohol class (which is defined in alcohol.h) to define a member variable
50. 50. A.h class FirstClass { public: void someFunc() { ... } }; B.h #include “A.h” class SecondClass { public: void otherFunc() { FirstClass y; FirstClass b[10]; y.someFunc(); return(y); } private: FirstClass x; FirstClass a[10]; }; Last Topic: Knowing WHEN to Include .H Files Here are the rules… You must include the header file (containing the full Why? Because C++ needs toof a class) definition know the class’s details in order to define actual variables Any time… with it or to let you call methods from 1. You define a regular variable it! of that class’s type, OR 2. You use the variable in any way (call a method on it, return it, etc). On the other hand…
51. 51. A.h class FirstClass { Last Topic: Knowing This line tells C++ that your WHEN to Include .H Files class exists, but doesn’t actually public: void someFunc() { ... the gory details. define all } If you do NONE of the Since none of this code actually but you… previous items, }; B.h class FirstClass; class SecondClass { uses the “guts” of the class (as // enough! 1. Use the class the code did on the previous to define a parameter to slide), this is all C++ needs to a function, OR know!Use the class as the 2. public: void goober(FirstClass p1); FirstClass hoober(int a); void joober(FirstClass &p1); void koober(FirstClass *p1); void loober() { FirstClass *ptr; } private: FirstClass *ptr1, *z[10]; }; return type for a func, OR 3. Use the class to define a pointer or reference variable Then you DON’T need to include the class’s .H file. (You may do so, but you don’t need to) Instead, all you need to do is give C++ a hint that your class exists (and is defined elsewhere). Here’s how you do that:
52. 52. A.h class FirstClass { … // public:thousands of lines …voidthousands of{lines} // someFunc() ... }; B.h #include “A.h” // really slow! class SecondClass { public: private: }; Last Topic: Knowing WHEN to Include .H Files Wow – so confusing! Why not just always use #include to avoid the confusion? There are two reasons: 1. If a .h file is large (thousands of lines), #including it when you don’t strictly need to slows down your compilation! 2. If two classes refer to each other, and both classes try to #include the other’s .H file, you’ll get a compile error.
53. 53. Students and Classrooms Every student knows what class he/she is in… Every class has a roster of its students… CS 32 Kerry Cyril Kerry Cyril Math 31b Lydia Hal Lydia Hal These type of cyclical relationships cause #include problems!
54. 54. Last Topic: Self-referential Classes And here we have the Student refersto thethat each Student Since our Student classclass. Note Student class, HereSince ouraClassRoomclass refers holds aclassroomclass we have ClassRoomclass the problem? bunch of Students… Do you see that to the knows whichincludes ClassRoom.h… in… it classroom he or she it includes Student.h is Student.h ClassRoom.h #include “ClassRoom.h” #include “Student.h” class Student { public: ... class ClassRoom { public: ... private: ClassRoom *m_myRoom; }; private: Student m_studs[100]; }; Student.cpp ClassRoom.cpp #include “Student.h” #include “ClassRoom.h” void Student::printMyClassRoom() { cout <<“I’m in Boelter #” << m_myRoom->getRmNum(); } void ClassRoom::printRoster() { for (int i=0;i<100;i++) cout << m_studs[i].getName(); }
55. 55. Last Topic: Self-referential Classes main.cpp #include “Student.h” Student.h #include “ClassRoom.h” class Student { public: ... private: The compiler will keep ClassRoom }; going forever!!!!! *m_myRoom; ClassRoom.h #include “Student.h” int main() { Student david; … } class ClassRoom { public: ... private: Student m_studs[100]; };
56. 56. So how do we solve this cyclical nightmare? Step #1: Look at the two class definitions in your .h files. At least one of them should NOT need the full #include. Question: This our JUST defines a pointer Which ofclass two classes doesn’t need the full #include? Why? to a ClassRoom. It does NOT hold a full ClassRoom variable and therefore doesn’t require the full class definition here!!! Student.h This class defines actual Student variables… It therefore requires the full class definition to work! ClassRoom.h #include “ClassRoom.h” #include “Student.h” class Student { public: ... class ClassRoom { public: ... private: ClassRoom *m_myRoom; }; private: Student m_studs[100]; };
57. 57. So how do we solve this cyclical nightmare? Step #2: Take the class that does NOT require the full #include (forget the other one) and update its header file: Replace the #include “XXXX.h” statement with the following: class YYY; Where YYY is the other class name (e.g., ClassRoom). Student.h ThisThe Compiler says: line tells ClassRoom.h the compiler: The Programmer says: “Alright smartypants, I’ll trust you. #includeCompiler replies: The “Student.h” “I think I’ll have a little fun! Let’s“Hey Compiler, there’s “Wannt playclass called class Student what the compiler says ifclass ClassRoom me about But if won’t tell another nasty? Ok. see {I are 9000 SYNTAX ERRORS { Here define a full variable!”ClassRoom’s functions and variables ‘ClassRoom’ but we’re not public: public: for deal with!” ... can’t goingyou to define any actual …then you to tell you exactly ... whatvariables like just yet.” it looks with it X #include “ClassRoom.h” class ClassRoom; private: ClassRoom *m_myRoom;, haha; }; private: Student m_studs[100]; or }; any member functions on it.” call
58. 58. So how doAll you need is this we solve this cyclical nightmare? class declaration; line to give the compiler a heads-up. Step #3: Almost done. Now update the CPP file for this class by #including the other header file normally. Since this .H file JUST has a pointer to a ClassRoom (but no This line tells the compiler: code in this file that uses or defines #include So you must a full ClassRoom variable)… “Hey Compiler, since my CPP its header file here! file is going to actually Student.h use class’s functionality, I’ll #include “ClassRoom.h” You’re calling a member tell you all the details class ClassRoom; now The Compiler says: function of ClassRoom about the class.” Student.cpp class Student here… #include “Student.h” { #include public: “ClassRoom.h” void Student::printMyClassRoom() ... { cout <<“I’m in Boelter #” << private: m_myRoom->getRmNum(); ClassRoom *m_myRoom; } }; Ah, ok, now I can see the full definition of what a ClassRoom Thevariable reallyhappy as compiler is is… long as you #include the Feel free to call its getRmNum() classmethod if you like! you definition before actually use the class in your functions…
59. 59. So how do we solve this cyclical nightmare? The Compiler says: Step #4: Finally, make sure that your Hey! Wait a second, you’re Student.h calling the getRmNum( ) class doesn’t have any other #include “ClassRoom.h” class ClassRoom; function but you haven’t defined member functions that violate class Student the full class. No WAY! our #include vs class rules… { public: void beObnoxious() { void beObnoxious(); … Fix it or DIE! cout << m_myRoom->getRmNum() << “ sucks!”; } private: ClassRoom *m_myRoom; }; If you have defined one or more functions DIRECTLY in Alright. That’s better. your class definition AND they Student.cpp use your other class in a #include “Student.h” Don’t let it happen again. significant way, then you must #include “ClassRoom.h” void Student::printMyClassRoom() MOVE them to the CPP file. The Compiler says: {void Student::beObnoxious() { cout << m_myRoom->getRmNum() << “ sucks!”; } cout <<“I’m in Boelter #” << m_myRoom->getRmNum(); }
60. 60. So how do we solve this cyclical nightmare? Now let’s look at both of our classes… Notice, we no longer have a cyclical reference! Woohoo! Student.h #include “ClassRoom.h” class ClassRoom; ClassRoom.h #include “Student.h” class Student { public: ... class ClassRoom { public: ... private: ClassRoom *m_myRoom; }; private: Student m_studs[100]; }; Student.cpp #include “Student.h” #include “ClassRoom.h” void Student::printMyClassRoom() { cout <<“I’m in Boelter #” << m_myRoom->getRmNum(); } ClassRoom.cpp #include “ClassRoom.h” void ClassRoom::printRoster() { for (int i=0;i<100;i++) cout << m_studs[i].getName(); }
61. 61. Class Challenge RULES • The class will split into left and right teams • One student from each team will come up to the board • Each student can either – write one new line of code to solve the problem OR – fix a single error in the code their teammates have already written • Then the next two people come up, etc. • The team that completes their program first wins! Team #1 VOID float FUNC() int Team #2 int func(int * v) int { while(v > 0)
62. 62. Challenge #1 Write a class called quadratic which represents a second-order quadratic equation, e.g.: 4x2+3x+5 When you construct a quadratic class, you pass in the three coefficients (e.g., 4, 3, and 5) for the equation. The class also has an evaluate method which allows you pass in a value for x. The function will return the value of f(x). The class has a destructor which prints out “goodbye”
63. 63. Challenge #2 Write a class called MathNerd which represents a math nerd. Every MathNerd has his own special quadratic equation! When you construct a MathNerd, he always wants you to specify the first two coefficients (for the x2 and x) for his equation. The MathNerd always selects a value of PI for his final coefficient. The MathNerd had a getMyValue function which accepts a value for x and should return f(x) for the nerd’s QE.