What We Will Cover
Continuations
Homework Questions?
^ top
8.1: Creating and Using Structures
Learner Outcomes
At the end of the lesson the student will be able to:
- Code structures containing multiple values
- Use structures in a program
|
^ top
8.1.1: Structuring Data
- Sometimes you want to keep data together in a group
- For instance, suppose we were programming an inventory system for a grocery store
- We want to keep various information about each product in our computer like that shown in the following table
- How would we store the data for each product in our program?
| Name |
Price |
| Milk |
3.95 |
| Bread |
2.99 |
| Cheese |
3.95 |
^ top
8.1.2: Creating Structures
Example Program Using a struct to Group Related Data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#include <iostream>
using namespace std;
struct Product {
string name;
double price;
};
int main(void) {
Product milk;
milk.name = "Milk";
milk.price = 3.95;
cout << "Product data:"
<< "\nName: " << milk.name
<< "\nPrice: " << milk.price
<< endl;
}
|
Declaring a struct Variable
- Notice how we create a variable of the
struct we defined:
Product milk;
- The
struct name becomes a new data type
- After the
struct name, we write a name for our new struct variable
^ top
8.1.3: Member Variables and Assignment
Shortcut Assignment
Input and Output
^ top
8.1.4: Structures and Functions
- Structures can be function parameters, either value parameters or reference parameters
- However, passing by reference is more efficient
- In addition, we can return structures from functions
- The following example program uses structures for parameters and return types
- Note that we create three different products using the single
struct
Example Program Using struct's with Functions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
#include <iostream>
using namespace std;
struct Product {
string name;
double price;
};
// Read product data from a user
Product readProduct();
// Display product data to the screen
void print(Product& prod);
int main(void) {
Product milk;
milk.name = "Milk";
milk.price = 3.95;
Product bread = { "Bread", 2.99 };
Product cheese = readProduct();
cout << "Data for product #1:";
print(milk);
cout << "Data for product #2:";
print(bread);
cout << "Data for product #3:";
print(cheese);
}
Product readProduct() {
Product prod;
cout << "Enter a name: ";
getline(cin, prod.name);
cout << "Enter a price: ";
cin >> prod.price;
return prod;
}
void print(Product& prod) {
cout << "\nName: " << prod.name
<< "\nPrice: " << prod.price
<< "\n\n";
}
|
^ top
Exercise 8.1
In this exercise we look at how to use a structure. Make certain you follow each step exactly to prevent problems in this and future exercises.
Specifications
- Copy the following program into a text editor, save it as
rectstruct.cpp, and then compile the starter code to make sure you copied it correctly.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
#include <iostream>
using namespace std;
struct Rectangle {
// Put member variables here
};
// Read structure data from the keyboard
void read(Rectangle& rect);
// Print structure data to the console
void print(Rectangle& rect);
int main() {
// Place statements to create a struct here
// Place statements to call functions here
return 0;
}
// Define the functions here
|
- Add member variables
length and width of type int in the Rectangle struct area.
For more information see section 8.1.2: Creating Structures.
- Using the specified prototype, write a
read() function that prompts the user for the length and width member variables. Place the function definitions after main(). In addition, add code to the main() function to create a Rectangle structure and call the read() function. For example:
Rectangle spongeBob;
read(spongeBob);
When your program runs, the display should look like this:
Enter the rectangle length: 10
Enter the rectangle width: 12
The numbers after the colons are entered by the user. For more information see section 8.1.4: Structures and Functions.
- Using the specified prototype, write a
print() function that displays the length and width variables contained inside the struct with an "x" between them. In addition, add code to the main() function to call the print() function after the call to the read() function. For example:
print(spongeBob);
When your program runs, the display should look like this:
Enter the rectangle length: 10
Enter the rectangle width: 12
10 x 12
If your code does not work correctly, compare it to the listing shown below.
- Submit your final program source code to Blackboard as part of assignment 8.
Listing of rectstruct.cpp

As time permits, be prepared to answer the Check Yourself questions in the section: 8.1.5: Summary.
^ top
8.1.5: Summary
- Structures contains multiple values of possibly different types
- Structure definitions are usually placed outside of any function definition
- Member variables can be used just as any other variable of the same type
- Structures can be arguments in function calls
- Structures can be the type of a value returned by a function
Check Yourself
- What is a structure used for? (8.1.2)
- Given a struct named
Foo, what code do you write to declare a variable of the struct? (8.1.2)
- What is a member variable? (8.1.3)
- How many member variables can a structure contain? (8.1.3)
- Given the following
struct and struct variable, what code do you write to disply the member variable price to the console? (8.1.3)
struct Product {
string name;
double price;
};
...
Product cheese;
- For the previous
struct and struct variable, what code do you write to assign a new value to the member variable name? (8.1.3)
- When you pass a
struct variable to a function, should you normally use value parameters or reference parameters? (8.1.4)
^ top
8.2: Coding Classes to Create Objects
Learner Outcomes
At the end of the lesson the student will be able to:
- Discuss the concept of encapsulation and information hiding
- Describe the reasons for separating the interface from the implementation
- Write class declarations that define interfaces
- Code member variables and member functions
- Design and implement accessor and mutator member functions
- Write simple no-argument constructors
- Declare a class and use it to create an object
- Call a member function of an object to perform a task
|
^ top
8.2.1: Problems With Structures
- While useful, structures have some inherent limitations and problems
- For example, consider the following
struct to store the time of day:
struct Time {
int hours;
int minutes;
int seconds;
};
- Suppose we use this
struct to track the liftoff of the space shuttle:
Time liftoff = {19, 30, 45};
- Unfortunately, we find a problem and liftoff is delayed six (6) hours
- We let the user enter this delay into our program:
int hoursDelayed;
cout << "Enter the hours of delay: ";
cin >> hoursDelayed;
liftoff.hours = liftoff.hours + hoursDelayed;
- What is the new time of liftoff?
- As we can see, structures offer no guarantee that our data will be valid
Example Program Showing a struct Problem
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
#include <iostream>
using namespace std;
struct Time {
int hours;
int minutes;
int seconds;
};
void print(Time t);
int main() {
Time liftoff = {19, 30, 45};
print(liftoff);
int hoursDelayed;
cout << "Enter the hours of delay: ";
cin >> hoursDelayed;
liftoff.hours = liftoff.hours + hoursDelayed;
print(liftoff);
return 0;
}
void print(Time t) {
cout << t.hours << ":"
<< t.minutes << ":"
<< t.seconds << endl;
}
|
^ top
8.2.2: Encapsulation and Information Hiding
- The struct problem shown above is just one of many issues that occur in large programs
- As programs become larger, we need to change the way we work on them
- Large programs usually have teams of people who work on them like:
- One or two software engineers may work on the user interface
- Another two or three may work on the logic of the application and various features
- Another may work on data storage and retrieval
- When many programmers work together, they need to take special care with the data
- The data type of an application may need to change as the program develops
- For example, we may start a program by keeping money in a variable of type
double
- Later we may decide that two
int variables are a better choice (dollars and cents)
- Any part of the program that used the
double variable would have to change
The Prescription for Larger Programs
- To make larger programs more manageable, we use classes
- A class is like a
struct but with added features to help protect program data
- Two important features of classes are:
- Encapsulation
- Information hiding
- These two concepts are closely intertwined
- You cannot have information hiding without encapsulation
- However, you can have encapsulation without information hiding, as we saw with structures
- Sometimes you will see a reference to one term when the writer means both terms
Encapsulation
Encapsulation: bundling of data and the functions operating on that data
- We saw an example of encapsulation when we looked at structures
- Encapsulation allowed us to group related variables in one container (capsule)
- If the variables in the grouping are logically related, we can more easily keep track of what is happening with the data
- For example, we can keep all the data about a product inside one container
- In addition to data, classes extend encapsulation to include member functions
- Member functions are functions that work with the member variables
- Thus, functions like
print() are grouped into the class that describes a product
Information Hiding
Information hiding: hiding the parts of the design that are most likely to change (a.k.a. data hiding)
- As programs develop, developers change them frequently
- Even after a program is first completed and released, successful programs are updated with new features
- With large programs, making a change in one area can cause problems in other areas
- By hiding the parts of a program most likely to change, a programmer can make implementing changes much easier
- The ability to change parts of a program without affecting other parts becomes more important as programs grow larger
^ top
8.2.3: Declaring Classes
Defining the Programming Interface
- The class declaration defines the programming interface for using the class
- An interface defines how other parts of the program can communicate with our class
- The interface consists of all member functions we want to define for objects of that type
- For example, the above interface declares member functions to:
- Construct a new product object
- Read data from the console and store it in the object
- Print the information about a product
- The first member function,
Product(), is known as a constructor
- The purpose of a constructor is to initialize the member variables
- The
read() function will read data from the console
- The
print() function will display information about the product to the console
- Note the member variables defined inside the class
- We declare and use class member variables like we did with structures
Class Declaration Syntax
Private Members and Information Hiding
- Note the keywords
public and private
- These keywords are known as access modifiers (a.k.a. visibility modifiers or permission labels)
- The keyword
public means that the following members are "available to the public" (other parts of the program)
- The keyword
private means that only constructors and member functions can access private members
- An access modifier is how we implement information hiding in the class container
- Setting
private accessibility for all data is good design practice
- This protects data by forcing indirect access only through
public functions
- We can then add code to the public functions to ensure that the data cannot accidentally be put into an incorrect state
- In general, you declare most class functions
public
- However, if you want to prevent a function from being used outside a class, declare it
private
- The example program above a class declaration compiles but does not do anything useful
- We will discuss the other pieces we must add to make the program do something useful in the following sections
Example Program with a Class Declaration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
#include <iostream>
using namespace std;
class Product {
public:
Product();
void read();
void print() const;
private:
string name;
double price;
};
// For testing
int main() {
// Create objects here
return 0;
}
|
Programming Style: Class Naming Conventions
- Use nouns to name classes -- they represent objects
- Start all class names with a capital letter
- The capital letter differentiates class names from variable names
^ top
8.2.4: Member Functions
Mutator Functions
- The
read() function is a mutator function - an operation that modifies the object
- After a mutator function runs, the state of the class changes (mutates)
- In this case, the
read() function changes the values of all the member variables
- Note the use of the conditional statement
- Mutator functions must leave the object in a valid state
- Most grocery stores would consider a negative price to be an error
Accessor Functions
Member Function definition Syntax
Function Naming Conventions
- As mentioned before, use verbs for function names since they perform an action
- Also start function names with a lower case letter
- Not required by syntax, but is a convention that professional programmers follow
^ top
8.2.5: Coding Constructors
Example Program with a Class Defined
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
#include <iostream>
using namespace std;
class Product {
public:
Product();
void read();
void print() const;
private:
string name;
double price;
};
Product::Product() {
name = "Unknown";
price = 0.0;
}
void Product::read() {
cout << "Enter the name of the product: ";
cin >> ws;
getline(cin, name);
cout << "Enter the price for a " << name << ": ";
cin >> price;
if (price < 0) {
cout << "Error: negative price!\n"
<< "Setting price to 0.\n";
price = 0;
}
}
void Product::print() const {
cout << name << " @ " << price << endl;
}
// For testing
int main() {
Product milk;
Product bread;
cout << "Enter the milk product data\n";
milk.read();
cout << "\nEnter the bread product data\n";
bread.read();
milk.print();
bread.print();
return 0;
}
|
^ top
8.2.6: Constructing Objects From Classes
- After defining a class, we construct one or more objects
- The syntax for constructing an object with no arguments is:
ClassName objectName = ClassName();
or
ClassName objectName;
- Where:
- ClassName: the name of the class type
- objectName: the variable of the class type
- For example, to construct a
Product object named milk:
Product milk;
- When an object is created, memory is allocated for the class variables:
private:
string name;
double price;
- The constructor gets called after the memory is allocated, which then initializes the values to a known state:
Product::Product() {
name = "Unknown";
price = 0.0;
}
- Once an object is constructed, we can call its member functions
- For instance:
milk.read();
milk.print();
- From a single class we can construct many objects:
Product milk;
Product bread;
Product cheese;
- Each of these objects have their own memory space
The Difference Between Classes and Objects
- What is the difference between a class and an object?
- A class is a template for building modules
- An object is one of the modules build from the template
- By analogy, a class is like the design of a house (blueprints)
- You need to construct the house from the design before you can use the object:

- Other analogies may help understanding:
- Cookie cutter and cookies: a class is like a cookie cutter and objects are like cookies
- Mold and Pots: a class is like a mold and objects are the clay pots coming out of the mold.
- Dog and Fido: a class is like the concept of a dog and objects are individual animals like Fido.
- Stencil and drawings: a class is like stencil and objects are the drawings made from the stencil.
- For the above analogies, which is the class and which is the object?
- In the next exercise we put these ideas into practice to create a class and then construct objects from the class template
^ top
Exercise 8.2
In this exercise we write our first class. We start by converting the struct from the previous exercise to a class. Make certain you follow each step exactly and in order to prevent problems.
Specifications
- Without destroying your file, save the
rectstruct.cpp file from the last exercise as rectangle.cpp. Compile your code to make sure you saved it correctly.
We will use the struct as a starting point for exploring classes.
- Change the word
struct to class in the rectangle.cpp file.
class Rectangle
Try to recompile the file and notice that your get error messages like:
int Rectangle::length' is private
By default all members of a class are private, which means they cannot be accessed from outside the container of the class. Thus statements like the following are not allowed:
cin >> rect.length;
- One way to allow the class to compile is to add the word
public: before the member variables. Make the change shown below and then compile and run the code:
class Rectangle {
public: // add this line
int length;
int width;
};
Setting class member variables public lets the code compile, but breaks the information hiding of the class. We need a different way to work with the member variables of the class.
- The correct way to work with member variables of the class is through a set of public functions known as the interface. The first step in creating an interface is to move the function prototypes for
read() and print() inside the class after the keyword public: and then to add the keyword private: after the function prototypes. When finished, your class should look like so far:
class Rectangle {
public:
// Read data from the keyboard
void read(Rectangle& rect);
// Print data to the console
void print(Rectangle& rect);
private:
int length;
int width;
};
Notice that we now have both member variables and member functions inside our class. The ability to encapsulate both functions and variables is one of the strengths of classes. The public member functions provide the interface to the class and the member variables are "hidden" from the rest of the program. For more information see section 8.2.3: Defining Classes.
- Our code will not compile because we still need to correct the function definitions. We need to specify that the
read() and print() function definitions are part of the class. Add the class name and scope resolution operator in front of the function names of the function definitions (the part with the curly braces) like:
void Rectangle::read(Rectangle& rect) {
void Rectangle::print(Rectangle& rect) {
Adding the class name tells the compiler that the function definitions match the prototypes declared inside the class Rectangle. For more information see section 8.2.4: Member Functions.
- We still need to make changes in
main() for our code to compile and run. Since the functions moved inside the class, we need to specify an object for the class. Add the object name before the calls to the read() and print() functions like:
Rectangle spongeBob;
spongeBob.read(spongeBob);
spongeBob.print(spongeBob);
Compile and run your program to verify all the changes you have made in the last few steps are correct.
- We still need to make changes to our class member functions. In the
read() and print() functions, remove the object parameter names from before the member variable names so the functions now look like:
void Rectangle::read(Rectangle& rect) {
cout << "Enter rectangle length: ";
cin >> length;
cout << "Enter rectangle width: ";
cin >> width;
}
void Rectangle::print(Rectangle& rect) {
cout << length << " x "
<< width << endl;
}
Note that you may get a compiler warning that your code has unused parameters. The object parameter names are no longer necessary because the member functions can access any member of the class.
- Since the parameters are no longer needed, we can remove them from all the functions and function calls throughout our program. Remove all the
Rectangle& rect parameters in your program. Also remove the object argument from the read() and print() function calls in main(). When you are finished, main() should look like:
int main() {
Rectangle spongeBob;
spongeBob.read();
spongeBob.print();
return 0;
}
Compile and run your program to verify you made all the changes correctly.
- One problem remains with our program: when the
Rectangle object is constructed the length and width variables are not initialized. You may be able to see this problem by commenting out the read() function call in main() like the following and then compiling and running the program.
int main() {
Rectangle spongeBob;
// spongeBob.read();
spongeBob.print();
return 0;
}
When you run the program you may see 0 x 0 on the screen or you may see another garbage value like 1628430752 x 4262800. As with all uninitialized variables in C++, you can be easily fooled when your program works sometimes but mysteriously fails other times.
- You may be tempted to try and correct the problem by assigning an initial value to the member variables like:
private:
int length = 0; // No!
int width = 0; // No!
However, C++ will not allow you to assign a value to member variables inside a class declaration. The solution is to use a constructor function.
- Let us add a constructor to our
Rectangle class. In the class declaration, add the following as the first statements after the keyword public:
public:
// Default constructor
Rectangle();
Then after the end of the main() function and before the other member function definitions, add the constructor definition:
Rectangle::Rectangle() {
length = 0;
width = 0;
}
For more information see section 8.2.5: Coding Constructors.
- Compile and run your program to verify you made all the changes correctly.
With the constructor defined, we can be sure that our class member variables are valid even if the print() function is called before the read() function.
- Update your
main() function to include the call to read() before the call to print(). Your main() function should look like:
int main() {
Rectangle spongeBob;
spongeBob.read();
spongeBob.print();
return 0;
}
Compile and run your program to verify you made all the changes correctly. If you have any problems, verify your code against the program listing below.
- Submit your final program source code to Blackboard as part of assignment 8.
Listing of rectangle.cpp

As time permits, be prepared to answer the Check Yourself questions in the section: 8.2.7: Summary.
^ top
8.2.7: Summary
Check Yourself
- What problem can you run into using a
struct? (8.2.1)
- What is meant by the terms "encapsulation" and "information hiding"? (8.2.2)
- What keyword identifies the interface of a class? (8.2.2)
- What is meant by the term declaration of a class? (8.2.3)
- What is a member function? (8.2.4)
- Where are member functions declared? (8.2.4)
- Where are member functions normally defined? (8.2.4)
- What feature identifies the definition of a member function? (8.2.4)
- What is the difference between an accessor and a mutator function? (8.2.4)
- What is a constructor and what is its purpose? (8.2.5)
- How is the syntax of a constructor different than other member functions? (8.2.5)
- Given a class named
Foo, what code do you write to construct an object of the class? (8.2.6)
- For the object declared in the previous question, what code do you write to call a public member function named
bar()? (8.2.6)
^ top
8.3: Improving Our Class
Learner Outcomes
At the end of the lesson the student will be able to:
- Write constructors with parameters
- Overload constructors
- Construct objects from classes with overloaded constructors
- Decide when to write member and nonmember functions
- Discuss when to use accessor and mutator functions
- Describe how to prevent "shadowing"
|
^ top
8.3.1: Constructors with Parameters
- Recall that the purpose of a constructor is to initialize all member variables for an object to a valid state
- Whenever we create an object, we need to assign specific values to the member variables
- So far, we have been assigning specific values using a
read() member function
- However, sometimes we want to create objects and assign them initial values without getting user input
- For instance, we may want to predefine some objects or assign them values from another source such as a file or database
- To accomplish this, we can write a constructor with parameters
- For instance, we could add the following constructor prototype to the interface of our
Product class:
Product(string newName, double newPrice);
- Also, we could define our constructor like this:
Product::Product(string newName, double newPrice) {
name = newName;
price = newPrice;
if (price < 0) {
cout << "Error: negative price!\n"
<< "Setting price to 0.\n";
price = 0;
}
}
- Note the use of the conditional statement
- Part of the job of a constructor is to make certain that the member variables are set to a valid state
- Setting a price less than zero would be incorrect for most businesses
^ top
8.3.2: Multiple Constructors
Example: Default Constructor (No Parameters)
Example: Constructor Definition with one Parameter
Example: Constructor Definition with two Parameters
Implicit Default Constructors
- If the programmer does not define any constructor, then the compiler supplies an empty default constructor like the following:
Product::Product() { }
- However, if the programmer defines any constructor, the compiler does not supply a default constructor
^ top
8.3.3: Constructing Objects from Overloaded Constructors
- If a class has more than one constructor, our program must decide which constructor to call
- The way that C++ resolves which constructor to call is by matching the arguments to the parameter list
- Constructor matching is done based on the number and types of the parameters
- Also, the matching only works if the order is correct
- For example, assume our class has the following three constructors:
Product();
Product(string newName);
Product(string newName, double newPrice);
- Creating the following objects calls the indicated constructor:
Product milk; // first
Product bread("Rye Bread"); // second
Product cheese("Cheddar", 6.75); // third
- Note that names play no role in matching, only the number and type in the correct order
- Also note that your can have regular functions with the same name as long as their parameters are different
Avoiding the No-Parameter Trap
Implicit Type Conversion
- If there is no exact match for the parameters, C++ will look for compatible types
- For instance, we can create the following object:
Product gouda("Gouda Cheese", 5);
- Since '
5' is an int, it does not exactly match the double parameter of the constructor
- However, C++ will automatically convert an
int to a double and call the appropriate constructor
- C++ only knows how to convert certain types, mostly the numeric types
- Thus, C++ does not automatically convert something like a
string to a double
- Thus, you cannot create a
Product object using:
Product gouda("Gouda Cheese", "5"); // wrong
^ top
8.3.4: Accessing Member Variables
Set Functions
- The simplest mutator function is one that changes a single value
- A common naming convention is to use the name of the value with the word set prepended
- Thus to set the price of a
Product, we would add the following to our interface:
void setPrice(double newPrice);
- Mutator functions should verify the value is correct before setting it
- For example, since price should not be a negative value, we define our function like:
void Product::setPrice(double newPrice) {
if (newPrice > 0.0) {
price = newPrice;
} else {
cout << "Error: negative price!\n"
<< "Setting price to 0.\n";
price = 0.0;
}
}
Get Functions
Example Class with Get and Set Functions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
#include <iostream>
using namespace std;
class Product {
public:
Product();
Product(string newName);
Product(string newName, double newPrice);
double getPrice() const;
void setPrice(double newPrice);
void read();
void print() const;
private:
string name;
double price;
};
Product::Product() {
name = "Unknown";
price = 0.0;
}
Product::Product(string newName) {
name = newName;
price = 0.0;
}
Product::Product(string newName, double newPrice) {
name = newName;
price = newPrice;
if (price < 0) {
cout << "Error: negative price!\n"
<< "Setting price to 0.\n";
price = 0;
}
}
double Product::getPrice() const {
return price;
}
void Product::setPrice(double newPrice) {
if (newPrice > 0.0) {
price = newPrice;
} else {
cout << "Error: negative price!\n"
<< "Setting price to 0.\n";
price = 0.0;
}
}
void Product::read() {
cout << "Enter the name of the product: ";
cin >> ws;
getline(cin, name);
cout << "Enter the price for a " << name << ": ";
cin >> price;
if (price < 0) {
cout << "Error: negative price!\n"
<< "Setting price to 0.\n";
price = 0;
}
}
void Product::print() const {
cout << name << " @ " << price << endl;
}
// For testing
int main() {
Product milk;
Product bread("Rye Bread");
bread.setPrice(2.99);
Product cheese("Cheddar", 6.75);
cout << "Enter the milk product data\n";
milk.read();
milk.print();
bread.print();
cheese.print();
double price = milk.getPrice();
cout << "The current price of milk is $"
<< price << ".\n";
cout << "Enter the new price: ";
double newPrice = 0;
cin >> newPrice;
milk.setPrice(newPrice);
milk.print();
return 0;
}
|
Designing Accessor and Mutator Functions
- Not every member variables needs accessor functions
- Also, not every variable needs a mutator function
- In addition, not every
get-function needs a matching set-function
- Remember that the implementation details are hidden
- Just because a class has member functions prepended with
get or set does not necessarily explain how the class is designed
- For example, a
Time class may have the following interface:
class Time {
public:
Time(int hour, int min, int sec);
Time();
int getHours() const;
int getMinutes() const;
int getSeconds() const;
int secondsFrom(Time t) const;
void addSeconds(int s);
private:
int timeInSecs;
};
- Despite using hours, minutes and seconds in the public interface, all time values are stored as seconds:
Time::Time(int hour, int min, int sec) {
timeInSecs = 60 * 60 * hour + 60 * min + sec;
}
- Then the accessor functions simply convert the seconds to hours, minutes and seconds as needed
int Time::getMinutes() const {
return (timeInSecs / 60) % 60;
}
^ top
8.3.5: Local Variables, Parameters and Shadowing
- Shadowing is when a local variable or parameter has the same name as a member variable of a class
- Local variables and parameters with same name as a member variable hide the member variable
- Any use of the variable's name will refer to the local variable or parameter
- This is the source of many an elusive bug
- You should not use the same name for a local variable or parameter and a class member variable
- Remember that a parameter is like a local variable but initialized in a special way
- Thus you should use a different name for a parameter and a member variable
- Which lines contain local shadowing in the following program and how do you correct the problem?
Example of Shadowing
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
#include <iostream>
using namespace std;
class MyRectangle {
public:
MyRectangle();
MyRectangle(double length, double width);
void print();
private:
double length;
double width;
};
MyRectangle::MyRectangle() {
length = 0;
width = 0;
}
MyRectangle::MyRectangle(double length, double width) {
length = length;
width = width;
}
void MyRectangle::print() {
cout << "length: " << length << endl
<< "width: " << width << endl;
}
// For testing
int main() {
MyRectangle rec;
MyRectangle rec3x5(3.0, 5.0);
rec.print();
rec3x5.print();
return 0;
}
|
What the Shadow Knows
^ top
Exercise 8.3
In this exercise we explore the use of more class features.
Specifications
- Without destroying your file, save a copy of your
rectangle.cpp file from the last exercise as rectangle2.cpp. Compile your code to make sure you saved it correctly.
We will be enhancing this program during this exercise.
- Add a second constructor that lets you set the both the
length and width member variables from the parameters by adding the following constructor prototype to the class declaration:
// Overloaded constructor
Rectangle(int newLength, int newWidth);
In addition, you will need to add the definition of this overloaded constructor after the end of default constructor definition and before the other member function definitions:

For more information see sections 8.3.1: Constructors with Parameters and 8.3.2: Multiple Constructors.
- In your
main() function, create at least two objects, one using each constructor. For example:
Rectangle big(100, 300);
After adding your new constructor, compile and run your program to verify your work. For more information see sections 8.3.3: Creating Objects from Overloaded Constructors.
- Write an accessor and mutator function for a member variable following the "standard"
get and set naming protocol. For example, here are the prototypes for the length variable:
int getLength();
void setLength(int newLength);
For more information see sections 8.3.4: Accessing Member Variables.
- In
main(), call all the get and set functions at least one time. For example, the following calls the get and set functions for member variable length:
cout << "Testing get and set functions\n";
Rectangle big(100, 300);
cout << "The length of big is: "
<< big.getLength() << endl;
big.setLength(50);
cout << "Rectangle big is now: ";
big.print();
You must add statements like this for member variable width as well.
- To explore shadowing, add the following member function prototype to your class declaration:
void setSize(int length, int width);
In addition, add the following function definition to your program:
void Rectangle::setSize(int length, int width) {
length = length;
width = width;
}
This function definition has a shadowing problem which you must find and correct. For more information see sections 8.3.6: Local Variables, Parameters and Shadowing.
- Add code like the following to
main() to test your fix to the shadowing problem:
cout << "Testing shadowing\n";
Rectangle shadow;
shadow.setSize(7, 14);
shadow.print();
If you do not see that the shadow rectangle is 7 x 14, then you have not corrected the problem.
- Submit your final program source code to Blackboard as part of assignment 8.
As time permits, be prepared to answer the Check Yourself questions in the section: 8.3.6: Summary.
^ top
8.3.6: Summary
- Constructors are used to initialize all the member variables of a class
- We can code a constructor with or without parameters
- A constructor without parameters is known as a default constructor
- We can use constructors with parameters to assign values to the member variables using the parameters
- We can write a class with more than one constructor
- If the class has more than one constructor, the constructor matching the argument list is used
- For example, if our class has the following three constructors:
Product();
Product(string newName);
Product(string newName, double newPrice);
- The following call the constructors in order
Product milk;
Product bread("Bread");
Product cheese("Cheddar", 6.75);
- You must use care when calling a no-parameter constructor:
Product milk2(); // wrong (but compiles)
Product milk3; // right
- Local variables and parameters with same name as a class variable hide the class variable
- You should not use the same name for a local variable and a class member variable
Check Yourself
- What is the purpose of a constructor? (8.3.1)
- How many constructors can you define for each class? (8.3.2)
- When does a compiler automatically include a constructor? (8.3.2)
- How do you call a particular constructor when there are many of them? (8.3.3)
- How does the syntax for calling a default constructor differ from calling constructors that have parameters? Why? (8.3.3)
- What types of functions can access private member variables? (8.3.4)
- Since member variables should be declared private, how do you access them outside the member functions of the class? (8.3.4)
- Should you declare accessor and mutator functions for all class member variables? Why or why not? (8.3.4)
- What is meant by the term "shadowing"? (8.3.5)
- How do you avoid "shadowing"? (8.3.5)
^ top
Wrap Up
Due Next: A7-Postal Bar Codes (4/15/10)
A8-Getting Classy (4/22/10)
- When class is over, please shut down your computer
- You may complete unfinished exercises at the end of the class or at any time before the next class.
^ top
Home
| Blackboard
| Day Schedule
| Eve Schedule
Syllabus
| Help
| FAQ's
| HowTo's
| Links
Last Updated: May 02 2010 @11:21:04
|