Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Lecture 2 constructors:composition
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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. Can you guess?
Programming Language Inventor
Or
Serial Killer
See if you can guess who uses a keyboard and
who uses a chainsaw!
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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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.
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;
};
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;
};
39. A Few Final Topics
Let’s talk about three final topics that you’ll need in
order to solve your Project #1…
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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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.
Notas do Editor
Philip Wadler. He invented the “Haskell” language.