Structures
It is sometimes useful to have a collection of values of different types that may be treated as a single entity.
Consider a student records example. For each student:
student in class:
Note: Various types, hence an array may not be used.
A structure allows us to group together variables of different types:
struct StudentRecord
{
int id_num;
char first_name[31];
char last_name [31];
int hw_score;
int midterm_score;
int final_score;
char letter_grade;
};
The keyword
struct indicates a structure. StudentRecord is referred to as the structure tag (may be any legal identifier) and may be thought of as the type of the structure. The identifiers inside the braces are called member names and need to be legal identifiers.Once a structure definition has been given, the structure type may be used as you would any predefined type (
int, char, double etc.):StudentRecord Jill, John;
We assign values to structure variables by using a dot operator:
Jill.id_num = 11111;
John.midterm_score = 68;
Jill.final_score = 91;
You may use member variables anywhere you would use other variables.
strcpy(Jill.first_name, "Jill");
Two or more structures may use the same member names
struct Student323
{
int id_num;
char letter_grade;
int quiz_score;
int hw_score;
};
struct Student215
{
int id_num;
char letter_grade;
int hw_score;
};
Note: Semicolon immediately after the last brace. Variables may be declared immediately after the structure definition but we will not use that technique.
You may also cluster variables of the same type. Considering a date structure:
struct date
{
int month, day, year;
};
A structure variable may be viewed as a collection of member variables or as a single variable with several parts. Consider the following:
StudentRecord Jill, John;
Jill.id_num = 111111;
strcpy(Jill.first_name, "Jill");
strcpy(Jill.last_name, "Doe");
Jill.hw_score = 95;
Jill.midterm_score = 98;
Jill.final_score = 88;
Jill.letter_grade = 'A';
John = Jill;
The assignment statement above is equivalent to:
John.id_num = Jill.id_num;
strcpy(John.first_name, Jill.firs_name);
strcpy(John.last_name, Jill.last_name);
John.hw_score = Jill.hw_score;
John.midterm_score = Jill.midterm_score;
John.final_score = Jill.final_score;
John.letter_score = Jill.letter_grade;
Testing for equality as below is not allowed:
if (John == Jill)
cout << "These are the same\n";
else
cout << "Not the same\n";
Structures as function arguments
Since instances of structures work like variables we may make them call-by-value or call-by-reference arguments to functions as well as the return value of functions:
void print_record( StudentRecord student)
{
cout << "Student ID: " << student.id_num
<< " Name: " << student.first_name
<< " Homework: " << student.hw_score
<< " Midterm: " << student.midterm_score
<< " Final: " << student.final_score
<< " Letter Grade: " << student.letter_grade
<< endl << endl;
}
Classes and Objects
A class extends the structure construct by allowing member functions as well as member variables. To define classes we need to specify the values that the object can take on as well as associated member functions. Consider a simple class, which implements "student":
class Student
{
public:
void output();
Notice the similarity to structures. The differences are:
To define a member function:
void Student::output()
// outputs the grade
{
cout << "A\n";
}
Student::output
john.final_score
Recall the mechanism for referencing structure members as well as the invocation of member functions
eof, get, etc. for streams. We use the same mechanism to reference member variables and functions:Student john, jill, jack;
john.final_score = 91;
jill.midterm_score = 92;
int score1;
cout << "Enter the midterm for jack: ";
cin >> score1;
// setting a score using the setscore function
jack.setscore(score1, ‘m’);
if (jack.midterm >= 90)
cout << "Excellent\n";
Let us consider the temperature conversion program.
Define a class called
temperature:Note: The output member function should print the temperature on the screen in whatever scale is requested (C or F).
#include <iostream.h>
class Temperature
{
public:
void set(double new_degrees, char new_scale);
// set the member variables with these values
void output(char req_scale);
// print the temperature to the screen
// in the requested scale
double degrees;
char scale;
};
int main()
{
double temp;
char scale;
Temperature this_room;
cout << "Enter temperature and scale (C/F): ";
cin >> temp >> scale;
this_room.set(temp, scale);
cout << "\nTemperature \n" << "in this room is ";
this_room.output('F');
cout << " (or ";
this_room.output('C');
cout << ")\n\n";
return 0;
}
void Temperature::set(double new_degrees,
char new_scale)
{
if ((new_scale != 'F') && (new_scale != 'C'))
cout << "Unsupported scale: " << new_scale;
else
{
degrees = new_degrees;
scale = new_scale;
}
}
void Temperature::output(char req_scale)
{
if ((req_scale != 'F') && (req_scale != 'C'))
cout << "Unsupported scale: " << req_scale;
else
{
if (req_scale == scale)
cout << degrees << scale;
else if (req_scale == 'F')
cout << (9.0/5.0) * degrees + 32 << "F";
else
cout << (5.0/9.0) * (degrees - 32) << "C";
}
}
Public and Private members
In order to allow the separation of the details of implementation from usage, C++ provides two ways to declare member variables in a class, public and private.
Consider the following class definition:
class DayOfYear
{
public:
void input(); // input day of year from keyboard
void output(); // output day of year to screen
void set(int new_month, int new_day); // set date
int get_month(); // return month, 1=Jan, etc.
int get_day(); // return day of month
private:
int month;
int day;
};
Consider the following declaration:
DayofYear today;
Since
month and day are private, the following statements are illegal:Today.month = 5; today.day = 20;
To set the date, we now have to call the member function ‘
set’today.set(5,20);
Also, to get the value of the day and month:
if (today.get_month()==11 && today.get_day()==20)
cout << "Good Luck!\n";
To input values:
today.input();
The member function
input reads two values from the keyboard for month and day and assigns them to the corresponding private variables. void DayOfYear::input()As a general rule we do not want users of the class to be able to directly access internal data structures. This is why we make all member variables private.
We typically divide member variables into two sections. One for private and the other for public members. This is not a C++ requirement, you may several public or private sections as shown below.
class Sample
{
public:
void do_this();
int stuff;
private:
void do_that();
int another_thing;
public:
int do_the_other();
double a_third_var;
};
Variables are by default private, but it is a good practice to explicitly indicate membership.
Tips for Dealing with Classes
DayOfYear today, examday;
if (today == examday)
cout << "Good luck!\n";
Accessor functions resolves this problem since:
if (today.get_day()==examday.get_day() && today.get_month()==examday.get_month())
cout << " Good luck!\n";
DayOfYear today, classday;
classday.input();
today = classday;
This is equivalent to:
today.day = classday.day;
today.month = classday.month;
(assuming our first example where month is an int) This is true despite the fact that day and month are private member variables.
Constructors for initialization
A good way to initialize an object is to declare a member function called a constructor. A constructor is a member function that is automatically called when an object of that class is declared. It is used to initialize the values of some or all of the member variables (and do any other sort of initialization that might be necessary). You define a constructor the same way as you define any other member function for the class except for two differences.
To add a constructor to the
DayOfYear classclass DayOfYear
{
public:
DayOfYear(int init_month, int init_day);
void input();
void output();
int get_month(); // returns month, 1 = Jan, etc.
int get_day(); // returns the day of the month
private:
int month;
int day;
};
The constructor:
DayOfYear::DayOfYear(int init_month, int init_day)
{
day = init_day;
month = init_month;
return;
}
To use this constructor?
DayOfYear today(5, 21), tomorrow(5, 22);
Note that we must do it this way; we cannot make a call to a constructor like an ordinary member function, that is, the following is illegal:
DayOfYear today;
today.DayOfYear(5,21);
It is useful to include a constructor that doesn't take any arguments. Such a constructor is called a default constructor.
Modify the change the
DayOfYear class to have the following public functionDayOfYear::DayOfYear()
{
day = 0;
month = 0;
return;
}
This will serve the purpose of defining the date to be something when we don't have a value for it. We may invoke it thus:
DayOfYear examday;
Note that the following is invalid:
DayOfYear examday();
We no longer need the
set member function since we can also use the constructor to change the values of the member variables once it's been defined.DayOfYear today;
today = DayOfYear(5,21);
This is legal whereas
today.DayOfYear(5,21) is wrong? The constructor DayOfYear returns an object, which is of type DayOyYear so we need to assign it to an object of the right type.The variable can also have values when we change it.
DayOfYear tomorrow(5,22);
tomorrow = DayOfYear(5,23);
Note that now we do not need the
set functions that we had before since we can now use the constructors to achieve the same purpose. You can also make a call to the default constructor to change the values, but if you do, then you need the parentheses.today = DayOfYear();
This resets the values of the member variables to the defaults.
Always Include a Default Constructor
If you have constructors then you must have a default constructor. The
DayOfYear class may not have a default constructor, but instead a constructor that takes two arguments. The following would then be illegal:DayOfYear today;
This is because C++ interprets the above to be a call to a constructor with no arguments which does not exist, so it gives an error. You can declare a default constructor to do nothing if you want to get around this problem.
DayOfYear::DayOfYear()
{
return;
}
The final version of the
DayOfYear class follows:#include <iostream.h>
class DayOfYear
{
public:
// constructors (note: no types!!!!)
DayOfYear(); // (default) sets date to 1/1;
DayOfYear(int m, int d); // set date to m/d;
void ask_user(); // input day of year from keyboard
void output(); // output the day of year to the screen
int get_month(); // returns the month, 1 = Jan, etc.
int get_day(); // returns the day of the month
private:
int month; // These are not accessible
int day; // from outside the class
void set(int new_month, int new_day);
// set the date to a new month and day
};
DayOfYear::DayOfYear()
{
month = 1;
day = 1;
}
DayOfYear::DayOfYear(int m, int d)
{
set(m,d);
}
void DayOfYear::ask_user()
{
int d, m;
cout << "Enter the month as a number: ";
cin >> m;
cout << "Enter the day of the month: ";
cin >> d;
set(m,d);
return;
}
void DayOfYear::output()
{
cout << month << "/" << day << endl;
return;
}
void DayOfYear::set(int new_month, int new_day)
{
if (new_month < 1)
{
new_month = 1;
cout << "(Corrected month: " << new_month << ")\n";
}
else if (new_month > 12)
{
new_month = 12;
cout << "(Corrected month: " << new_month << ")\n";
}
month = new_month;
day = new_day;
return;
}
int DayOfYear::get_month()
{
return (month);
}
int DayOfYear::get_day()
{
return (day);
}
#include <iostream.h>
#include "day_of_year_3.h"
int main ()
{
DayOfYear today;
cout << "'today' is currently set to: ";
today.output();
cout << endl;
today.ask_user();
cout << "\nYou entered the following date: ";
today.output();
cout << "\nTime passes...\n" << "\nNow it's: ";
// Using constructors instead of 'set'
today = DayOfYear(6,25);
today.output();
if ((today.get_month()==12) && (today.get_day()==25))
cout << "Merry Christmas!!\n";
else
cout << "Have a nice day!\n";
cout << endl;
return 0;
}
Summary of the Properties of Classes
Programming Example: Bank Account Class
See pages 322-323. Highlights of the example.