Classes

advertisement
Classes
●
●
●
We've seen that structs can be used to bundle
data together into a single object.
As well being able to store state, it's useful if
objects can do things.
We're going to take the idea of an Employee at
a company.
1
Classes
●
structs and classes are very similar behind the
scenes but are used for different things:
–
A struct should be used when you want to bundle
together some related data (e.g. our command-line
arguments)
–
A class should be used when the data inside are
correlated and you want to provide an interface to
that e.g. a std::vector keeps track of its data and the
number of elements.
2
Defining a class
●
A class is defined in a similar way to a struct
//Employee.h
class Employee {
public:
std::string name; ///< The name of the Employee
int salary; ///< Total salary of the Employee in pounds
};
●
●
Key difference is the public specifier
By default a class's members are hidden to the
outside. You need to explicitly make them
available
3
Constructors
●
A constructor is used to set up the initial values
of an object
●
It is named the same as the class
●
It is declared inside the class
class Employee {
public:
Employee(const std::string& name);
};
std::string name; ///< The name of the Employee
int salary; ///< Total salary of the Employee in pounds
4
Constructors
●
A constructor is defined like any other function
●
Put the definition in the .cpp file
●
You need to specify the scope with ::
●
Access the members of the class directly
Employee::Employee(const std::string& employeeName) {
name = employeeName;
salary = 0;
}
●
This is equivalent to:
int salary; //Declare
salary = 0; //Initialise
5
Constructors
●
It's always safer (and sometimes necessary) to
initialise variables at definition
Employee::Employee(const std::string& employeeName)
: name{employeeName}, salary{0}
{
}
●
Which is more like:
std::string name {employeeName}; //Declare and initialise
int salary {0}; //Declare and initialise
●
Data members declared as const must be
initialised this way
6
Default values for members
●
You can set default values of data members
directly in the class definition
class Employee {
public:
Employee(const std::string& name);
};
std::string name; ///< The name of the Employee
int salary = 0; ///< Total salary of the Employee in pounds
●
This defines the default value used for
initialisation in case one is not given in the
constructor
7
Constructing an object
●
std::string and std::vector are defined as classes
so we use a similar syntax to that we used
there:
#include "Employee.h"
int main() {
Employee jane {"Jane"}; //Calls the contructor we declared
std::cout << jane.name;
}
8
Aside: naming conventions
●
●
Most software projects have naming
conventions. In this course we use:
–
UpperCamelCase for class and struct names
–
lowerCamelCase for function and variable names
–
trailingUnderscore_ for data members
This makes it easier to see at a glance the
context of a name in the source code
9
Exercise 1
●
●
Make a class skeleton for a CaesarCipher with
the key as a member variable
The constructor should take the key as an
argument
10
Information hiding
●
●
There is a general principle in programming of
information hiding. You should separate what
something does from how it does it
This is implemented in C++ classes by marking
member variables as private and providing
public functions to make the information
available
11
Information hiding
●
●
●
The name is stored as a single std::string
In the future we may want to store it as a pair
of strings for first and last name
Getters and setters provide this interface:
class Employee {
public:
void setName(const std::string& newName);
std::string name() const;
private:
std::string name_; ///< The name of the Employee
};
12
Getters and setters
●
Most getters and setters are simple
void Employee::setName(const std::string& newName) {
name_ = newName;
}
std::string Employee::name() const {
return name_;
}
●
But they could be more complex as we will see
later
std::string Employee::name() const {
return firstName_ + " " + lastName_; //for example...
}
13
const functions
●
●
The name() function had a const label after it
This means that nothing inside that function can
change the object it is acting on
std::string Employee::name() const {
name_ = "Fran"; //Compiler error
return name_; //for example...
}
●
It also means you can call it on a const object
const Employee bill {"Bill"};
bill.setName("John"); //Compiler error
std::cout << bill.name(); //This is fine
●
Member functions should be made const unless
14
explicitly needed otherwise
Member functions
●
●
Member functions can do anything, not just get
and set
Classes can perform actions:
class Employee {
public:
std::string speak(const std::string& phrase) const;
//...
};
std::string Employee::speak(const std::string& phrase) const {
return "'" + phrase + "', said " + name();
}
15
Exercise 2
●
●
Add a member function to CaesarCipher which
encodes a string and returns the result
You should use the key_ data member as the
key
16
Documentation
●
●
●
●
All classes, functions and data members should
be commented
A class comment should describe what the class
is used for and how to use it
A function comment should describe all
arguments, the return value, any side effects
and if applicable any pre- or post- conditions
A standard syntax exists called Doxygen
17
Doxygen
●
●
●
Doxygen uses a special comment block syntax.
Single line comments start with /// and multiline comments start with /**
It defines a number of special commands which
start with a backslash for structured information
Documentation comment comes just before the
thing it is annotating
18
/**
* Employee has a salary and a name
* Use it by doing ...
*/
class Employee {
public:
/**
* Create a new Employee with a name and default values for salary
*
* \param name the name of the Employee
*/
Employee(const std::string& newName);
/**
* Sets the salary of the employee.
*
* \param newSalary the value to set the salary to
*/
void setSalary(int newSalary);
/// \return the base gross salary of the employee
int salary() const;
/// \return the name of the employee
std::string name() const;
//...
19
We'll cover how to generate this automatically in
more detail next week
20
Exercise 3
●
●
Add some Doxygen documentation to your
CaesarCipher class
Make sure all the functions and data members
are documented as well as the class overall
21
Enumerations
●
●
Enums provides a way of defining a set of
named values
They are useful when there is a small finite set
and you want to perform different actions
depending on the value
22
Enumerations
●
Declaring an enum is like declaring a class
/// The rank of the employee
enum class Rank {
Junior, ///< A new person at the company
Senior, ///< Someone whos been here a while
Chief
///< Someone super special
};
●
It makes a new type which can be instantiated:
Rank personsRank {Rank::Senior};
if(personsRank == Rank::Senior) {
std::cout << "Senior" << std::endl;
}
23
Enums and switches
●
Enums are very useful when mixed with switch
statements
Rank personsRank {Rank::Senior};
switch(personsRank) {
case Rank::Junior:
return 0;
case Rank::Senior:
return 3000;
}
●
The compiler will warn you that you missed one
of the entries in the enum (Rank::Chief)
24
Enum conversions
●
Even though enums are just integers behind the
scenes, you can't convert freely between them
enum class Colour {
Red,
Blue,
Green
};
Colour c {Colour::Red};
c = Rank::Senior; //COMPILER ERROR
//Rank::Senior is '1', doesn't set 'c' to 'Blue'
if(c == Rank::Junior) { //COMPILER ERROR
std::cout << "This doesn't make sense" << std::endl;
}
25
Exercise 4
●
Add an enum called CipherMode to designate
the encryption mode (encrypt or decrypt)
●
It only needs the two states
●
Make sure you document the enum
26
Operator overloading
●
Built-in types in C++ know how to perform
mathematical operations
●
They can do +, -, *, / between them
●
They can also be printed with std::cout <<
●
More complex types can do more like
std::vector and its subscript[] operator
27
Operator overloading
●
●
We can provide this functionality for our own
types by providing specially named function,
either to our class or which take our class as an
argument.
When it sees std::cout << employee the compiler
will look for a function called operator<< which
takes a std::ostream& and an Employee as
arguments
28
Stream operator
●
We define the overload function like any other
std::ostream& operator<<(std::ostream& os,
const Employee& employee) {
os << " Name: " << employee.name() << std::endl;
os << "Salary: " << employee.salary() << std::endl;
return os; //Return the reference we were passed
}
●
●
Similarly you can overload other operators, see
en.cppreference.com/w/cpp/language/operators
Only overload those which make sense.
Employee+Employee doesn't mean anything!
29
Download