lecture 2 constructors:composition

63
Wednesday, Jan 11 th • 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)

Upload: rohan-chitalia

Post on 20-Jun-2015

104 views

Category:

Technology


0 download

DESCRIPTION

none

TRANSCRIPT

Page 1: Lecture 2 constructors:composition

Wednesday, Jan 11th

• 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)

Page 2: Lecture 2 constructors:composition

class CSNerd{public:void Init(int PCs, bool usesMac){

m_numPCs = PCs; // # of PCs owned m_macUser = usesMac;}

int getNerdScore(void){

if(m_macUser == true) return(0); //not nerdy; “artistic” return(10 * m_numPCs);}

private: int m_numPCs; bool m_macUser;}; 

main(){ CSNerd david; david.Init(2,false); // geeky cout << david.getNerdScore();}

Constructors: Class InitializationEvery class should

have an initialization function that can be used to reset new variables before

they’re used.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);}

m_numPCs m_macUser

david

2 false 2 false

10*2 = 20

But there’s one problem with such an Init function… What is

it?

Here’s a hint!

Right! Our programmer might forget to call the Init function before using the

variable… What’ll happen?

Well, remember, all simple variables (e.g., ints, bools, etc.) in C++ start out with random values unless they’re explicitly initialized!

So if you forget to call the Init function, your CSNerd’s member

variables will have random values. Not what you’d want.

-32 false

Ack! How can someone have -32 computers?

Page 3: Lecture 2 constructors:composition

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;}; 

ConstructorsWouldn’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’sexactly what the C++

constructor does!

main(){ CSNerd david; cout << david.getNerdScore();}

Page 4: Lecture 2 constructors:composition

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;}; 

Constructorsmain(){ CSNerd chen;

chen.Init(3,true); cout << chen.getNerdScore();}

CSNerd

A constructor is a special member function that automatically initializes every new variable you create of that class.

The constructor is called automatically

every time you create a new

instance of your class.

(3,true);

(int PCs, bool usesMac)

CSNerd(int PCs, bool usesMac){ m_numPCs = PCs; m_macUser = usesMac; }

int getNerdScore(void){ if(m_macUser == true) return(0); return(10 * m_numPCs);}

m_numPCs m_macUser

chen

3 true

true3Instead of being called Init, the constructor

function has the same name as the class! (Confusing, huh?)

Since the constructor is called automatically any time you define a new

variable…

there’s no chance of a new variable being

uninitialized accidentally.

Page 5: Lecture 2 constructors:composition

Constructors

You can define the constructor in the class

declaration (see above)…

class CSNerd{public:CSNerd(int PCs, bool usesMac){

m_numPCs = PCs; m_macUser = usesMac;}

...

private: int m_numPCs; bool m_macUser;};

class CSNerd{public:

 

CSNerd(int PCs, bool usesMac){ m_numPCs = PCs; m_macUser = usesMac;}

CSNerd(int PCs, bool usesMac){ m_numPCs = PCs; m_macUser = usesMac;}

CSNerd::

;

Or, outside the class declaration, like this…

...

private: int m_numPCs; bool m_macUser;};

To summarize… A constructor is a special function in your class that initializes a new variable when its first created.

The constructor function MUST have the SAME NAME AS THE CLASS!

The constructor has no return type! It’s not allowed! Notice it’s not void, int, or bool.

return(true); //illegal!}

And thus it’s not allowed to return a value.

Like all other member functions, the constructor’s logic can be defined inside or outside the class declaration.

Boy, isn’t that syntax ugly?

Just remember, to define a constructor

outside the class declaration:

use the class name, followed by ::, followed

by the class name.

And remember to also include just the function

header, followed by a semicolon, in the class

declaration itself.

Page 6: Lecture 2 constructors:composition

class CSNerd{public:CSNerd(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 If a constructor requires parameters:

You must to provide values for those

parameters when you create a new

variable:

main(){

CSNerd ed(1,true); // OK CSNerd alan; // invalid!

}

)

main(){

CSNerd ed(1,true);//invalid! CSNerd alan; // OK!

}

= 0; = false;

Page 7: Lecture 2 constructors:composition

class CSNerd{public:CSNerd(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;}; 

ConstructorsJust like any C++

function, a constructor can have one or more default

parameters…main(){

CSNerd lyn(1,false);

CSNerd ned(5); // OK!

CSNerd dave; //invalid!

cout << lyn.getNerdScore();

}

= true

1 false

5

true

Page 8: Lecture 2 constructors:composition

class CSNerd{public:CSNerd(int PCs, bool usesMac = true) {

m_numPCs = PCs; m_macUser = usesMac;}

private: int m_numPCs; bool m_macUser;};

ConstructorsYour class can have

many different constructors. (this is called overloading

constructors)main(){

CSNerd lyn(1,false);

CSNerd ned(5); // OK!

CSNerd dave; //invalid!

cout << lyn.getNerdScore();

}

int getNerdScore(void){ if(m_macUser == true) return(0); return(10 * m_numPCs);}

CSNerd() { m_numPCs = 1; m_macUser = false;}

// OK!!!

One more thing: If you have 2 or more constructors,

they cannot have the exact same parameters/types.

CSNerd(int PCs=1, bool usesMac=true)

C++: “I’m confused! Should I call this constructor with default

parameters…”

C++: “…or should I call this constructor with no parameters?”

Page 9: Lecture 2 constructors:composition

ConstructorsIf you don’t define any constructors at all…

main(){

CSNerd carey; // OK

}

In this case, your member variables are

never initialized.

cout << carey.getNerdScore(); //??

class CSNerd{public: int getNerdScore(void) { if(m_macUser == true) return(0); return(10 * m_numPCs); }

private: int m_numPCs; bool m_macUser;}; 

CSNerd() // generated by compiler{ // I do nothing at all. // I’m not worthy!!!!!}

then C++ will provide an implicit, default

constructor for you that basically does nothing!

Page 10: Lecture 2 constructors:composition

Constructors & Arraysclass CSNerd{public:CSNerd(int PCs, bool usesMac = true) {

m_numPCs = PCs; m_macUser = usesMac;}

private: int m_numPCs; bool m_macUser;};

CSNerd() { m_numPCs = 1; m_macUser = false;}

...main(){ CSNerd lec1[4]; cout << lec1[0].getNerdScore();}

1false

1false

1false

1false

If you want to have an array of your class, your class must have a constuctor

that requires no arguments!

When you define an array, the constructor is run on

every element in the array!

Page 11: Lecture 2 constructors:composition

Constructors

class CSNerd{public:

CSNerd(int PCs, bool usesMac) { m_numPCs = PCs; m_macUser = usesMac; }...

There are several different ways to initialize your member variables in a constructor. Here’s the easiest way:

Here’s a more complex way that does the same

thing…Class CSNerd{public:

CSNerd(int PCs, bool usesMac) { // I don’t need to do anything! }...

Or, if you’re really

masochistic, you can do a little of

both.

:m_numPCs(PCs),m_macUser(usesMac)

m_macUser = usesMac;

This is called an

“initializer list”

Page 12: Lecture 2 constructors:composition

Constructor Challengeclass CSProf{public:

...private: string m_name; int m_age;};

Define a constructor that initializes a

CSProf.The user can specify the name of the prof

and his/her age.

If the user omits the age, then the prof’s default age is 39.

int main(void){ CSProf a(“David”,52); CSProf b(“Carey”);}

// version #1CSProf(const string &name, int age = 39){ m_name = name; m_age = age;}

// version #2CSProf(const string &name, int age = 39);

// version #2CSProf::CSProf(const string &name, int age){ m_name = name; m_age = age;}

// version #3CSProf(const string &name, int age = 39) : m_name(name), m_age(age){ // do nothing else}

Notice you don’t put the “= 39” here

too!

// version #4CSProf::CSProf(const string &name, int age) : m_name(name), m_age(age){ // do nothing else}

// version #4CSProf(const string &name, int age = 39);

Page 13: Lecture 2 constructors:composition

When Constructors are CalledA constructor is called any time you

create a new variable for a class.

main(){

}

CSNerd carey(3,false), bill; // called once for each var

A constructor is called N times when you create an array of size N.

CSNerd arr[52]; // constructor’s called 52 times

A constructor is called when you use new to dynamically allocate a new variable or an array. (we’ll learn new

later)

CSNerd *ptr = new CSNerd(1,3); // c’tor called onceCSNerd *dyn = new CSNerd[6]; // c’tor called 6 times

The constructor is not called when you just define a pointer variable.

CSNerd *justAPtr; // c’tor is NOT called

Page 14: Lecture 2 constructors:composition

Class ChallengeName 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(); }}

Page 15: Lecture 2 constructors:composition

DestructorsJust 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(); // constructor~SomeClass(); // destructor!

 private:... // whatever

};

SomeClass::~SomeClass(){ // destructor code}

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.

class SomeClass{public:SomeClass(); // constructor~SomeClass() // destructor!

{ // destructor code } private:... // whatever

};

Page 16: Lecture 2 constructors:composition

Why do we need a Destructor?

class InternetBanking {public: void startBanking(string nm,string pw) { m_ic.connect(“payperusebank.com”); m_ic.send(nm); // send name m_ic.send(pw); // send password }

void stopBanking(){ m_ic.disconnect(); }

void deposit(float amount) { … }

private: InternetConnection m_ic;

}; 

Here we have a class that allows us to do internet

banking.

~InternetBanking(){ m_ic.disconnect();}

int main(void){ InternetBanking ib; string name, pw; cin >> name >> pw; ib.startBanking(name,pw); ib.deposit(300);}

Let’s see what happens when we use this class without a destructor.

PayPerUseBank.com(we charge by the minute!)

Ben DoverCSRules

Ben DoverCSRules

ib

Hmmm… ib disappears when main exits, but the

user forgot to call stopBanking, so our PC is

still connected to (and being charged by) the

bank!

$300

Of course, had we added a destructor… None of this would have happened…

Now, when our ib variable goes away,

C++ will automatically call it’s destructor!!!

$1000

$990$1290$1280$1270$1260Ben’s

Balance:

Wow – we just logged on and they’re already charging fees!

Page 17: Lecture 2 constructors:composition

When must you have a destructor?

Any time a class…

Allocates data using the new commandOpens a disk file

Opens a network connection

Your class must have a destructor that…

Don’t forget or you’ll FAIL!

Frees the allocated memoryCloses the disk file

Disconnects the network connection

Page 18: Lecture 2 constructors:composition

DestructorsSo when is a destructor called anyway?

Local function variables are destructed any time a function exits.

void Dingleberry(void){ SomeClass c;

} c’s destructor is called.

Local variables defined in a block are destructed when the block finishes.

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 somethingdelete ib; The d’tor is called for ib by C++ here

Dynamically allocated variables are destructed when delete is called before the memory is actually freed by the operating system.

Oh, and what about arrays?

When an array goes away, the destructor is

called for every item in the array (just like construction).

Page 19: Lecture 2 constructors:composition

Class Challenge #2Name all the times the CSNerd destructor is called

in this example…

void foo(){ CSNerd a, *b;}

void main(void){ int j; CSNerd *ptr, x; ptr = new CSNerd; for (j=0;j<3;j++) { CSNerd a[5]; foo(); } delete ptr;}

class CSNerd{public: CSNerd() { m_numPCs = 1; m_macUser = true; }

~CSNerd() { cout <<“Argh! I’m dying. Where’s my

iphone?”; } ...private: int m_numPCs; bool m_macUser;};

Question: How could we make our program more

efficient?(Hint: Look where this is

pointing)

Page 20: Lecture 2 constructors:composition

Can you guess?

Programming Language Inventor

Or

Serial Killer

See if you can guess who uses a keyboard and who uses a chainsaw!

Page 21: Lecture 2 constructors:composition

Class CompositionClass composition: If a class contains one or more classes as member variables, then you are using class composition:

class GasTank{ …};

class Car{ ...private: GasTank myTank;};

Class Composition

class Valve{ …};

class Heart{… private: Valve m_valves[4];};

class TinMan{ ...private: Heart m_myHeart;};

Class Composition

Class Composition

Page 22: Lecture 2 constructors:composition

Compositionclass Stomach{public:

Stomach() { gas = 0; } ~Stomach() { cout << “boom!”; } void eat() { cout << “yummy”; gas ++; } void fart() { gas--; cout << “pffft!”; }private:

int gas;};

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:

class Nerd{

public: Nerd() { thought=“Waaa”; }

void meetCuteGirl(string &herName) {

thought = “H.O.T. HOT!”;

}

~Nerd() { thought=“Argh”; }

private: string thought; Stomach belly;};

• The outer class’s constructor(s)belly.eat();

• The outer class’s functions

belly.fart(); • The outer class’s destructor

belly.eat( ); Ok, that was easy… But now let’s look into how

construction and destruction work with

composition…

Page 23: Lecture 2 constructors:composition

Composition and Construction

When we create a Stomach variable, its constructor is

called.

int main(void){ Stomach s; ... // use s} 

sgas 0

And when the variable goes away, its destructor is

called…

boom!

class Stomach{public:

Stomach() { gas = 0; } ~Stomach() { cout << “boom!”; } void eat() { cout << “yummy”; gas ++; } void fart() { gas--; cout << “pffft!”; }private:

int gas;};

Page 24: Lecture 2 constructors:composition

When we create a Nerd variable, Nerd’s constructor

must be called.

int main(void){ Nerd david; ...} 

But our Nerd contains a Stomach, so its constructor

must be called too!

So which constructor is called first? Nerd’s or

Stomach’s?

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

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;};

Hint: How can Nerd’s constructor use the

belly variable if belly hasn’t been

constructed?

Page 25: Lecture 2 constructors:composition

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;};

david thought

belly gas

int main(void){ Nerd david; ...} 

C++ constructs the contained class first,

and the outer class second.

So our Stomach is constructed first and then Nerd is constructed after.

0

“Waaa”

yummy!

Composition and

Construction

1

Since the Stomach was already constructed, our Nerd class can use it in

its constructor!

Page 26: Lecture 2 constructors:composition

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;};

david thought

int main(void){ Nerd david; ...} 

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.

“Waaa”

yummy

“Argh”

Composition and Destruction

Since the Stomach has not been destructed yet,

it can still be used by Nerd!

boom!

belly gas 01

Page 27: Lecture 2 constructors:composition

Class Composition & Construction/Destruction

class Stomach{public: Stomach(void) { cout<<“Mmm food\n”; } ~Stomach() { cout << “Poof!\n”; }};

class Nerd{public: Nerd(void) { cout<<“I like integrals\n”; } ~Nerd() { cout << “Oh derivatives!\n”; } private: Stomach belly;};

int main(void){ Nerd herbert; ...} 

What does it print?

Mmm foodI like integralsOh derivativesPoof!

Page 28: Lecture 2 constructors:composition

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

Page 29: Lecture 2 constructors: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

a.eat(); // 6 farts ...} 

Stomach b; // ???

Now, what happens if we want to use our new

Stomach class in a Nerd?

Class Composition

Page 30: Lecture 2 constructors:composition

class Nerd{public: Nerd(void) { thought = "CS"; }

~Nerd() { cout << “Argh “ << thought; }

private: Stomach belly; string thought;};

Will this work?

NO!

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 Composition

Page 31: Lecture 2 constructors:composition

Class Compositionclass Nerd{public: Nerd(void) { thought = "CS"; }

~Nerd() { cout << “Argh “ << thought; }

private: Stomach belly; string thought;};

class Stomach{public: Stomach(int startFarts) { farts = startFarts; cout << “Start farts: “ << farts; }

void eat() { farts ++; }

~Stomach() { while (farts-- > 0) cout << “pffft!”; }

private: int farts;};

This won’t work because you must pass in a # of

farts any time you construct a Stomach. But our Nerd

doesn’t do that!

But doesn’t specify the

required starting # of farts!!!

Your Nerd has a Stomach variable…

Page 32: Lecture 2 constructors:composition

class Nerd{public: Nerd(void) { thought = "CS"; }

~Nerd() { cout << “Argh “ << thought; }

private: Stomach belly; string thought;};

Q: How can we fix our Nerd?

A: By properly specifying the # of farts when we construct a Stomach??

(10); // Good?

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 Composition

Page 33: Lecture 2 constructors:composition

class Nerd{public: Nerd(void) { thought = "CS"; }

~Nerd() { cout << “Argh “ << thought; }

private: Stomach belly; string thought;};

A: You MUST explicitly construct a contained class variable using an initializer

list in your constructor.

: belly(10)

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 CompositionThis means:

“Before you construct the Nerd…

first construct its Stomach by passing in a value of 10.”

Page 34: Lecture 2 constructors:composition

class Nerd{public: Nerd(void) { thought = "CS"; }

~Nerd() { cout << “Argh “ << thought; }

private: Stomach belly; string thought;};

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 Composition

: belly(10)

int main(void){ Nerd carey;

...} 

Start farts: 10

thought

carey bellyfarts 10

"CS"10

Page 35: Lecture 2 constructors:composition

class Nerd{public: Nerd( void ) { thought = “CS”; }

~Nerd() { cout << “Argh “<< thought; }

private: Stomach belly; string thought;};

Class Composition

Challenge: Modify the Nerd class so you can pass in the number of farts of gas that the Nerd starts with.

In our current Nerd, the Stomach always starts out with 10 farts of gas…

class Nerd{public: Nerd(void) { thought = “CS”; }

~Nerd() { cout << “Argh “<< thought; }

private: Stomach belly; string thought;};

: belly(10) : belly( 10 )int farts farts

Page 36: Lecture 2 constructors:composition

class Nerd{public: Nerd(int farts) : belly(farts) { thought = "CS"; }

~Nerd() { cout << “Argh “ << thought; }

private: Stomach belly; string thought;};

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 Composition

int main(void){ Nerd Carey(3);

...} 

Start farts: 3

thought

Carey bellyfarts 3

“CS”

3 33

Page 37: Lecture 2 constructors:composition

Class Composition

When you construct the NerdyCow, you should be able to specify how many farts start out in each stomach and its

thought.

Challenge:Our current Nerd only has one Stomach.

Create a NerdyCow class that has two Stomachs and a thought.

class NerdyCow{public: NerdyCow(int f1, int f2, string &idea)

{ thought = idea; }

~NerdyCow() { cout << “Moooo!”; }

private: Stomach belly1; Stomach belly2; string thought;};

: belly1(f1), belly2(f2)

class Nerd{public: Nerd(int farts) : belly(farts) { thought = "CS"; }

~Nerd() { cout << “Argh “ << thought; }

private: Stomach belly; string thought;};

Page 38: Lecture 2 constructors:composition

class NerdyCow{public: NerdyCow(int f1, int f2, string &idea)

{ thought = idea; }

~NerdyCow() { cout << “Moooo!”; }

private: Stomach belly1; Stomach belly2; string thought;};

: belly1(f1), belly2(f2)

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 Composition

int main(void){ NerdyCow earl(7,8, “milk”);

...} 

7 8 “milk”

thought

earl belly1farts 7

Start farts: 7Start farts: 8

belly2farts 8

“milk”78

Page 39: Lecture 2 constructors:composition

A Few Final TopicsLet’s talk about three final topics that you’ll need in

order to solve your Project #1…

Page 40: Lecture 2 constructors:composition

Topic #1: Include Etiquette

A. Never include a CPP file in another .CPP or .H file.

void someFunc(void){ cout << “I’m cool!”}

file1.cpp

#include “file1.cpp”

void otherFunc(void){ cout << “So am I!” someFunc();}

file2.cpp

You’ll get linker errors.

Only include .H files within a .CPP file.

Page 41: Lecture 2 constructors:composition

Topic #1: Include Etiquette

B. Never put a “using namespace” command in a header file.

#include <iostream>

someHeader.h

#include “someHeader.h”

int main(){ cout << “Hello world!”;}

hello.cpp

So this is bad…

This is called “Namespace Pollution”using namespace std;

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.

// BUT I DON’T WANT THAT// NAMESPACE! YOU BASTARD!

Page 42: Lecture 2 constructors:composition

Topic #1: Include Etiquette

C. Never assume that a header file will include some

other header file for you.

class Student { ... private: };

student.h

#include “student.h”

int main(){

Student larry; Alcohol vodka;

cout << larry << “ is drinking “ << vodka;}

main.cpp

class Alcohol{ ... };

alcohol.h

main.cpp defines an Alcohol variable but it doesn’t #include"alcohol.h".

Why? It assumes that student.h will just include alcohol.h for it.

And in fact, this works right now, because student.h does include alcohol.h.

But what happens if the author of student.h decides

to change it’s implementation?

#include "alcohol.h"

Alcohol bottles[5];

#include “redbull.h”

RedBull bottles[5];

Utoh! Now main.cpp no longer compiles!

Why? Because it doesn’t include alcohol.h but it defines an Alcohol

variable!

What can you do?

Always DIRECTLY include the header files you need RIGHT

where you need them.

In this case, main.cpp has a Student variable and an

Alcohol variable. So it should include both of their header

files.

#include "alcohol.h"

Now your main.cpp file will compile correctly regardless of what’s

contained in the other header files it uses!

Page 43: Lecture 2 constructors:composition

Topic #2: Preprocessor Directives

C++ has several special commands which you sometimesneed in your programs.

Command 1: #define

You can use the #define command to define new constants:

#define PI 3.14159

void someFunc(void){ cout << PI;}

file.cpp

You can also use #define to define a new constant without specifying a value! Why you ask? We’ll see!

#define FOOBAR

Page 44: Lecture 2 constructors:composition

Preprocessor Directives

Command 2: #ifdef and #endif

You can use the #ifdef command to check if a constant hasbeen defined already…

#define FOOBAR

#ifdef FOOBARvoid someFunc(void){ cout << PI;}#endif

file.cppThe compiler only compiles the code

between the #ifdef and the #endif

if the constant was defined.

Since FOOBAR was defined I’ll

compile the code until

#endif.

Since FOOBAR was NOT defined

I’ll ignore the code until #endif.

/*

*/

Page 45: Lecture 2 constructors:composition

Preprocessor Directives

Command 2: #ifndef and #endif

You can use the #ifndef command to check if a constant hasNOT been defined already…

#define FOOBAR

#ifndef FOOBARvoid someFunc(void){ cout << PI;}#endif

file.cpp

Since FOOBAR was defined I’ll ignore the code

until #endif.

/*

*/

Since FOOBAR was NOT defined I’ll compile the

code until #endif.The compiler

only compiles the code between the

#ifndef and the #endif if the constant was NOT

defined.

Page 46: Lecture 2 constructors:composition

Separate Compilation

When using class composition, it helps to define each class in a separate pair of .cpp/.h files.

class Calculator{public: void compute(); ...};

calc.h

#include “calc.h”

int Calculator::compute(){ ...}

calc.cpp

#include “calc.h”

class Student{public: void study() ...private: Calculator myCalc;};

student.h

#include “student.h”

int Student::study(){ cout << myCalc.compute();}

student.cpp

#include “student.h”

int main(){ Student grace; ... grace.study();}

main.cpp

Then you can use them like this in your main program…

Now – something interesting (and bad) happens if I use both classes in my main program. Let’s see!

#include “student.h”#include “calc.h”

int main(){ Student grace; Calculator hp; ... grace.study(); hp.compute();}

main.cpp

Can anyone see what the problem is?

Page 47: Lecture 2 constructors:composition

Separate Compilation

class Calculator{public: void compute(); ...};

calc.h

#include “calc.h”

int Calc::compute(){ ...}

calc.cpp

#include “calc.h”

class Student{public: void study() ...private: Calculator myCalc;};

student.h

#include “student.h”

int Student::study(){ cout << myCalc.compute();}

student.cpp

#include “student.h”

int main(){ Student grace; ... grace.study();}

main.cpp

Your main program first includes student.h…

Then student.h then includes calc.h!

#include “student.h”#include “calc.h”

int main(){ Student grace; Calculator hp; ... grace.study(); hp.compute();}

main.cpp

main.cpp#include “student.h”#include “calc.h”int main(){ Student grace; Calculator hp; ... grace.study(); hp.compute();}#include “calc.h”

class Student{public: void study() ...private: Calculator myCalc;};

class Calculator{public: void compute(); ...};

…and finally, main.cpp includes calc.h… again!

class Calculator{public: void compute(); ...};

So what’s the problem?

Well, since main.cpp and student.h both included calc.h, we ended up with

two definitions of Calc! That’s bad!

This can result in a compiler error!

So how do we fix it, you ask?

Here’s how…

Page 48: Lecture 2 constructors:composition

Separate Compilation

class Calculator{public: void compute(); ...};

calc.h

#include “calc.h”

class Student{public: void study() ...private: Calculator myCalc;};

student.h

Add “include guards” to each

header file.

An include guard is a special check that prevents duplicate header inclusions.

#ifndef CALC_H

#endif // for CALC_H

#define CALC_H

#ifndef STUDENT_H

#define STUDENT_H

#endif // for STUDENT_H

So what would our fully-compiled

program look like now?

#ifndef STUDENT_H#define STUDENT_H

#ifndef CALC_H#define CALC_Hclass Calculator{public: void compute();};#endif // for CALC_H

class Student{public: void study()private: Calculator myCalc;};#endif // for STUDENT_H

#ifndef CALC_H#define CALC_Hclass Calculator{public: void compute();};#endif // for CALC_H

int main(){ Student grace; Calculator hp; ... grace.study(); hp.compute();}

main.cpp

Now, when the compiler compiles

this code, it will ignore the redefined

definitions!

Compiler:STUDENT_H has been defined!

CALC_H has been defined!

Once the compilation is done,

the compiler discards all of the

# commands...

Make sure you do this any time you define header files

from now on…

It makes your code safer!

Compiler: Since no one’s defined

STUDENT_H, I’ll keep compiling.

Compiler: The user has defined STUDENT_H. I’ll remember that!

Compiler: Since no one’s defined

CALC_H, I’ll keep compiling.

Compiler: The user has defined

CALC_H. I’ll remember that!Compiler: Ok, I

just finished with the #ifndef

CALC_H block of code…

Compiler: Ok, I just finished with the

#ifndef STUDENT_H block of code…

Compiler: The CALC_H constant

was already defined. I’ll ignore the code until the

#endif!

Compiler: Ok, I just finished with the #ifndef CALC_H block of code…

class Calculator{public: void compute();};

class Student{public: void study()private: Calculator myCalc;};

And you can see that only one copy of each

definition is now included!

Page 49: Lecture 2 constructors:composition

Well, not so fast!

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 first…

Right?

class Student {

public: void beIrresponsible(); ... private: Alcohol *myBottle;};

student.h

class Alcohol{

public: void drink() { cout << “glug!”; }};

alcohol.h

#include “alcohol.h”

I use the Alcohol class (which is defined in

alcohol.h)to define a member

variable

So I need to include alcohol.h, right?

Page 50: Lecture 2 constructors:composition

You must include the header file (containing the full definition of a class)

Last Topic: Knowing WHEN to Include .H

Files

Here are the rules…

class SecondClass{

public: void otherFunc() {

}

private:

};

B.h

class FirstClass{

public: void someFunc() { ... }};

A.h

FirstClass x;

1. You define a regular variable of that class’s type, OR

FirstClass a[10];

2. You use the variable in any way (call a method on it, return it, etc).

y.someFunc();

#include “A.h”

Any time…

FirstClass y;FirstClass b[10];

On the other hand…

return(y);

Why? Because C++ needs to know the class’s details in

order to define actual variables with it or to let you

call methods from it!

Page 51: Lecture 2 constructors:composition

Last Topic: Knowing WHEN to Include .H

Files

If you do NONE of the previous items, but

you…

class SecondClass{

public:

private: };

B.h

class FirstClass{

public: void someFunc() { ... }};

A.h

void goober(FirstClass p1);

1. Use the class to define a parameter to a function, OR

3. Use the class to define a pointer or reference variable

Then you DON’T needto include the class’s .H file.

(You may do so, but you don’t need to)

FirstClass *ptr1, *z[10];

class FirstClass; // enough!

2. Use the class as the return type for a func, OR

FirstClass hoober(int a); void joober(FirstClass &p1); void koober(FirstClass *p1);

void loober(){ FirstClass *ptr;} Instead, all you need to do is

give C++ a hint that your classexists (and is defined

elsewhere).Here’s how you do that:

This line tells C++ that your class exists, but doesn’t

actually define all the gory details. Since none of this code actually

uses the “guts” of the class (as the code did on the previous

slide), this is all C++ needs to know!

Page 52: Lecture 2 constructors:composition

Last Topic: Knowing WHEN to Include .H

Files

Wow – so confusing!

class SecondClass{

public:

private: };

B.h

A.h

Why not just always use #include to avoid the

confusion?

1. If a .h file is large (thousands of lines), #including it when you don’t strictly need to slows down your compilation!

#include “A.h”

There are two reasons:

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.

class FirstClass{

public: void someFunc() { ... }};

class FirstClass{

… // thousands of lines … // thousands of lines };

// really slow!

Page 53: Lecture 2 constructors:composition

Students and ClassroomsEvery student knows what class he/she is in…

CS 32

Math 31b

Every class has a roster of its students…

Kerry

Cyril

Lydia

Hal

KerryCyril

LydiaHal

These type of cyclical relationships cause #include problems!

Page 54: Lecture 2 constructors:composition

And here we have the Student class. Note that each Student knows which classroom he or she is in…

Since our ClassRoom class refers to the Student class it includes Student.h

Since our Student class refers to the classroom class, it includes ClassRoom.h…

Here we have a ClassRoom class that holds a bunch of Students…

Last Topic: Self-referential Classes

#include “ClassRoom.h”

class Student{public: ... private: ClassRoom *m_myRoom;};

Student.h#include “Student.h”

class ClassRoom{public: ... private: Student m_studs[100];};

ClassRoom.h

#include “Student.h”

void Student::printMyClassRoom(){ cout <<“I’m in Boelter #” << m_myRoom->getRmNum(); }

Student.cpp#include “ClassRoom.h”

void ClassRoom::printRoster(){ for (int i=0;i<100;i++) cout << m_studs[i].getName();}

ClassRoom.cpp

Do you see the problem?

Page 55: Lecture 2 constructors:composition

Last Topic: Self-referential Classes

#include “ClassRoom.h”

class Student{public: ... private: ClassRoom *m_myRoom;};

Student.h

#include “Student.h”

class ClassRoom{public: ... private: Student m_studs[100];};

ClassRoom.h

int main(){ Student david; …}

main.cpp#include “Student.h”

class Student{public: ... private: ClassRoom *m_myRoom;};

#include “ClassRoom.h”

#include “Student.h”

class ClassRoom{public: ... private: Student m_studs[100];};

class Student{public: ... private: ClassRoom *m_myRoom;};

#include “ClassRoom.h”

The compiler will keep going forever!!!!!

Page 56: Lecture 2 constructors:composition

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: Which of our two classes doesn’t need the full #include? Why?

#include “ClassRoom.h”

class Student{public: ... private: ClassRoom *m_myRoom;};

Student.h#include “Student.h”

class ClassRoom{public: ... private: Student m_studs[100];};

ClassRoom.h

This class defines actual Student variables… It

therefore requires the full class definition to work!

This class JUST defines a pointer to a ClassRoom. It does

NOT hold a full ClassRoom variable and therefore doesn’t require the full class definition

here!!!

Page 57: Lecture 2 constructors:composition

#include “ClassRoom.h”

class Student{public: ... private: ClassRoom *m_myRoom;};

Student.h

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).

class ClassRoom;

This line tells the compiler:

“Hey Compiler, there’s another class called

‘ClassRoom’ but we’re not going to tell you

exactly what it looks like just yet.”

#include “Student.h”

class ClassRoom{public: ... private: Student m_studs[100];};

ClassRoom.hThe Compiler says:

“Alright smartypants, I’ll trust you.

But if won’t tell me about ClassRoom’s functions and

variables

…then you can’t define any actual variables with it

or call any member functions on it.”

, haha;

The Programmer says:

“I think I’ll have a little fun! Let’s see what the compiler

says if I define a full variable!”

The Compiler replies:

“Wannt play nasty? Ok. Here are 9000 SYNTAX ERRORS

for you to deal with!”X

Page 58: Lecture 2 constructors:composition

Step #3: Almost done. Now update

the CPP file for this class by #including the other header

file normally.

So how do we solve this cyclical nightmare?

#include “ClassRoom.h”

class Student{public: ... private: ClassRoom *m_myRoom;};

Student.hclass ClassRoom;

#include “Student.h”

void Student::printMyClassRoom(){ cout <<“I’m in Boelter #” << m_myRoom->getRmNum(); }

Student.cpp

#include “ClassRoom.h”

This line tells the compiler:

“Hey Compiler, since my CPP file is going to

actually use class’s functionality, I’ll now tell you all the

details about the class.”

The Compiler says:

Ah, ok, now I can see the full definition of what a ClassRoom

variable really is…

Feel free to call its getRmNum() method if you like!

The compiler is happy as long as you #include the

class definition before you actually use the class

in your functions…

You’re calling a member function of ClassRoom here…

So you must #include its header file here!

Since this .H file JUST has a pointer to a ClassRoom (but no

code in this file that uses or defines a full ClassRoom

variable)…

All you need is this class declaration;

line to give the compiler a heads-up.

Page 59: Lecture 2 constructors:composition

Step #4: Finally, make sure that your class doesn’t have any other

member functions that violate our #include vs class

rules…

So how do we solve this cyclical nightmare?

#include “Student.h”

void Student::printMyClassRoom(){ cout <<“I’m in Boelter #” << m_myRoom->getRmNum(); }

Student.cpp

#include “ClassRoom.h”

#include “ClassRoom.h”

class Student{public: … private: ClassRoom *m_myRoom;};

Student.hclass ClassRoom;

If you have defined one or more functions DIRECTLY in

your class definition AND they use your other class in a significant way, then you

must MOVE them to the CPP file.

void beObnoxious() { cout << m_myRoom->getRmNum() << “ sucks!”; }

The Compiler says:

Hey! Wait a second, you’re calling the getRmNum( ) function but you haven’t defined the full class. No

WAY!Fix it or DIE!

void beObnoxious();

void Student::beObnoxious() { cout << m_myRoom->getRmNum() << “ sucks!”; }

The Compiler says:

Alright. That’s better.

Don’t let it happen again.

Page 60: Lecture 2 constructors:composition

So how do we solve this cyclical nightmare?

#include “Student.h”

void Student::printMyClassRoom(){ cout <<“I’m in Boelter #” << m_myRoom->getRmNum(); }

Student.cpp

#include “ClassRoom.h”

#include “ClassRoom.h”

class Student{public: ... private: ClassRoom *m_myRoom;};

Student.hclass ClassRoom;

Now let’s look at both of our classes… Notice, we no longer have a cyclical reference! Woohoo!

#include “Student.h”

class ClassRoom{public: ... private: Student m_studs[100];};

ClassRoom.h

#include “ClassRoom.h”

void ClassRoom::printRoster(){ for (int i=0;i<100;i++) cout << m_studs[i].getName();}

ClassRoom.cpp

Page 61: Lecture 2 constructors:composition

Class Challenge

• 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!

RULESTeam #1 Team #2

VOID FUNC() int func(int v)int int

*float

{ while(v > 0)

Page 62: Lecture 2 constructors:composition

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 thethree 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”

Page 63: Lecture 2 constructors:composition

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 youto 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 acceptsa value for x and should return f(x) for the nerd’s QE.