1
CSCI 104
Classes
Mark Redekopp
David Kempe
Sandra Batista
2
OVERVIEW AND CONCEPTS
3
C Structs
• Needed a way to group values that are
related, but have different data types
• NOTE: struct has changed in C++! struct Person{
char name[20];
–C };
int age;
• Only data members int main()
{
• Some declaration nuances // Anyone can modify
– C++ // b/c members are public
Person p1;
• Like a class (data + member functions) p1.age = -34;
// probably not correct
• Default access is public return 0;
}
4
Classes & OO Ideas
• Classes are used as the primary way to organize code Protect yourself from users &
• Encapsulation protect your users from
– Place data and operations on data into one code unit themselves
– Keep state hidden/separate from other struct Machine{
programmers (or yourself) via private members Piece* pieces;
Engine* engine;
• Abstraction };
– Depend only on an interface! int main()
• Ex. a microwave…Do you know how it works? {
Machine m;
But can you use it?
– Hide implementation details to create low init_subsystemA(&m);
degree of coupling between different
change_subsystemB(&m);
components
• Unit of composition replace_subsystemC(&m);
– Create really large and powerful software systems from m.start();
tiny components // Seg. Fault!! Why?
}
• Define small pieces that can be used to compose larger pieces
– Delegation/separation of responsibility
• Polymorphism & Inheritance
– More on this later…
5
Coupling
• Coupling refers to how much components depend on each
other's implementation details (i.e. how much work it is to
remove one component and drop in a new implementation of
it)
– Placing a new battery in your car vs. a new engine
– Adding a USB device vs. a new video card to your laptop
• OO Design seeks to reduce coupling as much as possible by
– Creating well-defined interfaces to change (write) or access (read) the
state of an object
– Allow alternate implementations that may be more appropriate for
different cases
6
PARTS OF A CLASS
7
Parts of a C++ Class
• What are the main parts of a class IntLinkedList {
public:
class? IntLinkedList( );
IntLinkedList( int n ) ;
– Member variables ˜IntLinkedList( );
void prepend(int n);
• What data must be stored? void remove(int toRemove);
– Constructor(s) void printList();
void printReverse();
• How do you build an instance? private :
void printHelper(Item *p);
– Member functions Item ∗head;
• How does the user need to interact };
with the stored data?
– Destructor
• How do you clean up an after an
instance?
8
Notes About Classes
• Member data can be public or private (for now)
– Defaults is private (only class functions can access)
– Must explicitly declare something public
• Most common C++ operators will not work by default
(e.g. ==, +, <<, >>, etc.)
– You can't cout an object ( cout << myobject; won't work )
– The only one you get for free is '=' and even that may not work the
way you want (more on this soon)
• Classes may be used just like any other data type (e.g. int)
– Get pointers/references to them (Obj*, Obj&)
– Pass them to functions (by copy, reference or pointer)
– Dynamically allocate them (new Obj, new Obj[100])
– Return them from functions (Obj f1(int x);)
9
C++ Classes: Constructors
• Called when a class is instantiated
– C++ won't automatically initialize member variables class IntLinkedList {
public:
– No return value IntLinkedList( );
• Default Constructor IntLinkedList(int n);
˜IntLinkedList( );
– Can have one or none in a class ...
– Basic no-argument constructor };
– Has the name ClassName()
– If class has no constructors, C++ will make a default
• But it is just an empty constructor (e.g. Item::Item() { } )
• Overloaded Constructors
– Can have zero or more
– These constructors take in arguments
– Appropriate version is called based on how many and what type of
arguments are passed when a particular object is created
– If you define a constructor with arguments you should also define a default
constructor (otherwise no default constructor will be available)
10
Identify that Constructor
#include <string>
• Prototype what constructors #include <vector>
using namespace std;
are being called here int main()
{
string s1;
string s2("abc");
vector<int> dat(30);
return 0;
}
11
Identify that Constructor
#include <string>
• Prototype what constructors #include <vector>
using namespace std;
are being called here int main()
• s1
{
string s1;
string s2("abc");
– string::string()
// default constructor vector<int> dat(30);
• s2 return 0;
}
– string::string(const char* )
• dat
– vector<int>::vector<int>( int );
12
Initializing data members of a class
CONSTRUCTOR INITIALIZATION
LISTS
13
Consider this Struct/Class
• Examine this struct/class definition…
– How can I initialize the members?
#include <string>
#include <vector>
string name
struct Student
{ Student(); // constructor int id
std::string name; scores
int id;
std::vector<double> scores;
// say I want 10 test scores per student
};
int main()
{
Student s1;
}
14
Composite Objects
string name
#include <string> int id
#include <vector>
scores
• Fun Fact 1: Memory for an
struct Student
object comes alive before '{' {
std::string name;
of the constructor code int id;
• Fun Fact 2: Constructors std::vector<double> scores;
// say I want 10 test scores per student
for objects get called (and Student() /* mem allocated here */
can ONLY EVER get called) { // Can I call string & vector
// constructors to init. members?
at the time memory is name("Tommy Trojan");
id = 12313;
allocated scores(10);
}
};
int main()
{ Student s1;
//...
}
15
Initializing Members
• To recap: When an object is constructed the
individual members are constructed first
– Members constructors are called before object's
constructor
Class Obj
{ public: Type1 mem1 TypeA(){…}
Obj();
// public members
private: Type2 mem2 Members are
TypeB(){…}
Type1 mem1; constructed
Type2 mem2; first…
Type3 mem3 TypeC(){…}
Type3 mem3;
}; Obj …then Object
Obj(){…} constructor
called after
16
What NOT to do!
• So we CANNOT call constructors on data members INSIDE the
constructor)
– So what can we do??? Use initialization lists!
#include <string>
#include <vector> Stack Area of RAM
struct Student 0xbe4 scores
{ std::string name;
int id; 0xbe8 Tommy name
Student()
std::vector<double> scores; 0xbec 004000ca0
Return
link
// say I want 10 test scores per student
0xbf0
Student() /* mem allocated here */ name
main 0xbf4
{ // Can I do this to init. members? id s1
string name("Tommy"); // or 0xbf8 scores
// name("Tommy") 0xbfc Return
00400120
id = 12313; link
vector <double> scores(10); This would be
} "constructing"
}; name twice. It's
int main() too late to do it in
{ Student s1; the {…}
//...
}
17
Old Initialization Approach
Student::Student() Student::Student() :
{ name(), id(), scores()
name = "Tommy Trojan"; // calls to default constructors
id = 12313 {
scores.resize(10); name = "Tommy Trojan"; // now modify
} id = 12313
scores.resize(10);
}
If you write this…
The compiler will still generate this.
• Though you do not see it, realize that the default
constructors are implicitly called for each data
member before entering the {…}
• You can then assign values (left side code)
– But this is a 2-step process: default construct, then
replace with desired value
18
New Initialization Approach
Student::Student() : Student::Student() :
name(), id(), scores() /* compiler generated */ name("Tommy"), id(12313), scores(10)
{ {
name = "Tommy Trojan"; }
id = 12313
scores.resize(10);
}
Default constructors implicitly called and You would have to call the member
then values reassigned in constructor constructors in the initialization list context
• We can initialize with a 1-step process using a
C++ constructor initialization list
– Constructor(param_list) : member1(param/val), …, memberN(param/val)
{…}
• We are really calling the respective constructors
for each data member at the time memory is
allocated
19
Summary
Student::Student() Student::Student() :
{ name(), id(), scores()
name = "Tommy Trojan"; // calls to default constructors
id = 12313 {
scores.resize(10); name = "Tommy Trojan";
} id = 12313
scores.resize(10);
}
You can still assign data But any member not in the initialization list will
members in the {…} have its default constructor invoked before the
{…}
• You can still assign values in the constructor but realize that the
default constructors will have been called already
• So generally if you know what value you want to assign a data
member it's good practice to do it in the initialization list
Student::Student() :
name("Tommy"), id(12313), scores(10)
{ }
This would be the preferred approach especially for
any non-scalar members (i.e. an object)
Exercise: cpp/cs104/classes/constructor_init2
20
Calling Constructors
• You CANNOT use one constructor as a helper function to help
initialize members
– DON'T call one constructor from another constructor for your class
struct Student string name
{ std::string name;
int id
int id;
std::vector<double> scores; scores
Student()
{ name = "Tommy Trojan"; // default
id = -1; // default
scores(10); // default 10 assignments
}
Student(string n) Can we use Student() inside Student(string
{ Student(); name) to init the data members to defaults
name = n; and then just replace the name?
}
};
No!! Calling a constructor always allocates
another object. So rather than initializing the
int main()
members of s1, we have created some new,
{
anonymous Student object which will die at the
Student s1("Jane Doe");
end of the constructor
// more code...
}
21
Allocating and Deallocating Members
Type1 mem1 TypeA(){…}
• Members of an object
First
have their constructor Type2 mem2 TypeB(){…}
called automatically Type3 mem3 TypeC(){…}
before the Object's
ObjB
constructor executes ObjB(){…} Second
Construction
• When an object is Destructor is
~ObjB(){…} called first to
destructed the members ObjB cleanup whatever
members are
are destructed Type1 mem1 ~TypeA(){…} referencing…
automatically AFTER the …then the
object's destructor runs Type2 mem2 ~TypeB(){…}
destructor for
each data
Type3 mem3 ~TypeC(){…} member is called
automatically
Destruction
22
C++ Classes: Destructors
• Destructors are called when an object goes out of
scope or is freed from the heap (by “delete”) class Item
{ string s1;
• Destructors int* x; s1 "Hi"
– Can have one or none (if no destructor defined by the public:
x 0x148
programmer, compiler will generate an empty destructor) Item();
~Item();
– Have no return value
};
– Have the name ~ClassName() 0x148 7
– Data members of an object have their destructor's called Item::Item()
automatically upon completion of the destructor. { s1 = "Hi";
x = new int;
• Why use a destructor? *x = 7;
– Not necessary in simple cases }
– Clean up resources that won't go away automatically (e.g. Item::~Item()
when data members are pointing to dynamically {
allocated memory that should be deallocated when the delete x;
object goes out of scope) } // data members
// destructed here
– Destructors are only needed only if you need to do more
than that (i.e. if you need to release resources, close files,
deallocate what pointers are point to, etc.)
– The destructor need only clean up resources that are
referenced by data members.
23
OTHER IMPORTANT CLASS DETAILS
24
Member Functions
• Object member access uses
class Item
{ int val;
public:
dot (.) operator void foo();
void bar() const;
• Pointer-to-object member
};
void Item::foo()
{ val = 5; }
access uses arrow (->)
void Item::bar() const
operator { }
• Member functions have int main()
{
Item x;
access to all data members x.foo();
Item *y = &x;
of a class (*y).bar();
y->bar(); // equivalent
return 0;
• Use “const” keyword if it }
won't change member data
25
'const' Keyword
• const keyword can be used with
– Input arguments to ensure they aren't modified
– After a member function to ensure data members aren't modified by
the function
– Return values to ensure they aren't modified
string
string arg1 = "Hi" arg1
int& z =
objA.memFunc1(arg1);
int const &
ObjectA
int const & memFunc1(const string& s) const
{ return s == "Hi" ? mem1 : mem2; }
int string int
mem1 mem2 mem3 This Photo by Unknown Author
is licensed under CC BY-SA
26
Exercises
• cpp/cs104/classes/const_members
• cpp/cs104/classes/const_members2
• cpp/cs104/classes/const_return
27
C++ Classes: Other Notes
• Classes are generally split across two #ifndef STRING_H
#define STRING_H
files class string{
string();
– ClassName.h – Contains interface size_t length() const;
description /* ... */
};
– ClassName.cpp – Contains #endif
implementation details
• Make sure you remember to prevent string.h
multiple inclusion errors with your
header file by using #ifndef, #define, #include "string.h"
and #endif string::string()
#ifndef CLASSNAME_H { /* ... */ }
#define CLASSNAME_H size_t string::length() const
{ /* ... */ }
class ClassName { … };
string.cpp
#endif
28
Multiple Inclusion
class string{
• Often separate files may ... };
#include's of the same header string.h
file #include "string.h"
class Widget{
public:
• This may cause compiling string s;
};
errors when a duplicate
widget.h
declaration is encountered #include "string.h"
– See example #include "widget.h"
int main()
• Would like a way to include only { }
once and if another attempt to main.cpp
include is encountered, ignore it class string { // inc. from string.h
};
class string{ // inc. from widget.h
};
class Widget{
... }
int main()
{ }
main.cpp after preprocessing
29
Conditional Compiler Directives
#ifndef STRING_H
#define STRING_H
• Compiler directives start with class string{ ... };
#endif
'#'
– #define XXX string.h
• Sets a flag named XXX in the
#include "string.h"
compiler class Widget{
– #ifdef, #ifndef XXX … #endif public:
string s;
• Continue compiling code below };
until #endif, if XXX is (is not)
widget.h
defined
#include "string.h"
• Encapsulate header #include "string.h"
declarations inside a main.cpp
– #ifndef XX class string{ // inc. from string.h
#define XX };
… class Widget{ // inc. from widget.h
#endif ...
main.cpp after preprocessing
30
CONDITIONAL COMPILATION
31
Conditional Compilation
• Often used to compile int main()
additional DEBUG code {
int x, sum=0, data[10];
– Place code that is only needed for ...
debugging and that you would not want to for(int i=0; i < 10; i++){
execute in a release version sum += data[i];
#ifdef DEBUG
• Place code in a cout << "Current sum is ";
cout << sum << endl;
#ifdef NAME...#endif bracket #endif
}
• Compiler will only compile if a
cout << "Total sum is ";
#define NAME is found cout << sum << endl;
• Can specify #define in:
– source code stuff.cpp
– At compiler command line with $ g++ -o stuff –DDEBUG stuff.cpp
(-DNAME) flag
• g++ -o stuff –DDEGUG
stuff.cpp