본문 바로가기
[Umich] COE Core/ENGR 101 (Matlab, C++)

[Notes] Ch.19 Structs (Runestone)

by Class of 26' Yuri Hong 2022. 12. 6.

I. Introduction  

1. Modeling Real-World Objects in Code

(1) Let’s model the autonomous rovers used to explore the dark side of Proxima b. 

=> This proliferation of variables will quickly become unmanageable. 

(2) Using vectors: Vectors can store sequences of objects

=> We no longer need an arbitrary large number of variables. The vectors just grow to accommodate new rovers. 

=> However, each attribute of the rovers’ information is still stored as a separate variable. This makes code awkward. 

ex) in a function to work with rovers, we still have to pass all attributes separately. 

 

2. A Rover Type

 

3. Defining Structs

 (1) A struct definition creates a new compound type.

(2) Afterward, you can now declare variables of that type. 

The struct definition goes at the top level of the code, not inside any function. 

struct Rover { // capitalize struct names
    int type;
   string id; 
   double charge; 
}; // don’t forget the semi-colon! 

EXERCISE) Structs in Memory

Structs are compound data types, which means that they are composed of several member variables of various types. The following code defines the Rover struct and declares two Rovers - myRover and yourRover. A compound object (like a struct) requires space to store each of its member variables.

EX) In the above code, we don’t specify an initial value for any of the Rover’s member variables. What can we say about how member variables in a struct are initialized?

Answer: Ints and doubles are not initialized by default. (ex. They’re just set to some random values that happened to be there in memory), but strings are initialized to the empty string (“”).

 

II. Struct Basics 

1. Member Access with the Dot Operator

Use the dot operator to access a member variable. This allows an individual member as the target of an assignment.

 

2. Initializing structs

A special syntax can be used to initialize structs. Specify an initial value for each member inside curly braces. 

3. Copying structs

 Variables of the same struct type can be copied to each other. The built-in behavior is a straightforward member-by-member copy.

 

EXERCISE) Creating a Struct 

Create a struct called Planet. This struct should contain two member variables: a string for the name of the planet, and an integer for the density of the planet. 

Once you have defined the struct, create two planets and specify initial values for them. The first planet should be named “Mars” and have a density of 3933. 

The second one should be named “Jupiter” and have a density of 1326. 

Finally, print out the densities of Mars and Jupiter.

* You can have a struct within a struct. 

EX) 

We can include Moon variables in the Planet struct: 

 

III. Vectors of Structs  

1. Vectors of Structs

 

(1) Let’s model the rovers with a vector of Rover structs. 

 

(2) If there’s a default value, you can initialize them like this: 

(3) We could also read info about the fleet of rovers from a file.

2. Recall: General File I/O Pattern 

(1) The primary data structure (ex. The vector) lives in main. 

(2) Open the file stream in main. 

(3) Pass the stream and data structure into a function by reference. 

(4) The function reads data from the stream into the data structure. 

 

EX) What is the key difference between vectors and structs? 

A: Structs can hold different types of variables where a vector can hold only one. 

 

IV. Printing a Struct  

The built in << operator won’t work on our custom types. If we try to print out a Rover using cout << myRover; we’ll get an error! The compiler doesn’t know how to print out a Rover data type - it only knows how to print basic types.

* You can specify behavior for << (and other operators) for custom types by defining special operator overload functions. 

Since the default << behavior won’t work for Rovers, let’s write a function to print out one of our Rovers to an output stream (e.g., cout or a file). Fill in the code for the printRover function below.

V. Working with Structs  

Just like vectors, structs are a large type, so it’s expensive to make a copy of them. Because of that, we don’t want to pass structs by value to a function. If we want to modify our struct inside the function, we can pass by reference to work with the original struct. Otherwise, we can pass by const reference, which allows us to work with the original struct safely (without modifying it).

 

EX)  We want to write a function called chargeNeeded that takes in a Rover as a parameter and returns how much battery charge is needed until the rover is at full capacity. What is the correct prototype for this function?

A: double chargeNeeded(const Rover &rover);

=> We aren’t modifying our rover to figure out the charge needed, we want to use pass by const reference. 

 

EX)  We want to write a function called useBattery that takes in a Rover as a parameter and sets its charge to zero. What is the correct prototype for this function?

A: void useBattery(Rover &rover)

=> We are modifying our rover by setting the charge to zero, we want to use pass by reference. 

 

EX)  Select all the errors with the following struct definition. 

 

Answer: (1) Engine should be upper-cased. 

(2) The first word of the definition should be “struct” rather than “structure”. 

(3) There is a missing semicolon after the closing curly bracket. 

 

EX)  Select all the errors with the following struct definition. 

 

Answer: (1) CombustionEngine doesn’t have a member variable  “name”, so it’s incorrect to output “cout << engine1.name”. 

(2) The variable anotherEngine is an integer, so we can’t get the “speed” property of an integer (ex. “Cout << anotherEngine.speed”) 

 IV. Selecting Rovers for a Mission

1. Selecting rovers for a mission

(1) Select a set of rovers to conduct a mission - we’d like to collect soil samples from the dark side of the planet. 

(2) First, we need to add some members to the struct: 

  • The cargo capacity of each rover: an int
  • Whether or not the rover has been selected for the mission: a bool 

2. Updating the print function

3. Example: Loading rover data from a file

4. Selecting rovers for a mission

(1) Equipment: 

  • A fleet of rovers, each at some % of full charge
  • A rover must be fully charged before departing on a mission. 
  • A battery at the base camp that can divide 2 total “units of charge” 

(2) EX) 

 

rover charge Charge needed (1-charge)
A 0.2 0.8
B 0.5 0.5
C 0.3 0.7
D 0.8 0.2

 

= 2.0 “units of charge” so we can take A,B,C rovers on the mission, but not D because the battery is out of “charge”.

(3) Problem: Find the set of rovers with the greatest capacity, subject to our charge constraint (maximum 2 “units” of charge). 

(4) Idea: A rover with a high ratio of capacity vs. needed charge is best. 

  • A helper function

(5) Testing: it breaks when the charge is already 100% due to a divide by zero. 

=> Fix the function

We added a couple of member variables to our struct, and updated our printRover and loadRovers functions. We defined the problem of selecting rovers for a mission and wrote a helper function to calculate the desirability of the rover of the mission. 

 

EXERCISE) Get Best Rover

Using the desirability function, write another function that loops through a vector of Rovers and returns the index of the most desirable one.

 

Here is the algorithm that the bestRover function should follow:

1. bestRover should take in two parameters: a vector of Rovers called rovers, and a double value called availableCharge

(1) The second parameter indicates how much charge we have to get our rovers to full charge.

(2) We had a battery with an available charge of 2, but we want to make our function as general as possible, so we will pass this value into the function as a parameter.

2. If rovers is an empty vector, return -1. This is a useful value to return because it will never be a real index into a vector, and the user calling the function can check to see if the index is valid before using it.3HDeclare a variable called 3. bestIndex and set it equal to -1. We will use this variable to store the index of the best rover, but since we haven’t found a good rover yet, we’re going to set it equal to -1 for now.

4. Loop through all of the rovers, looking for the best rover. For each rover:

(1) See that it has NOT already been selected (e.g., member variable isSelected is false) and that the charge needed for that rover is less than the availableCharge

(2) If the conditions in (1) are met, and we haven’t picked a best rover yet (e.g., bestIndex equals -1), then set bestIndex to the index of this rover.

(3) Otherwise, if the conditions in (1) are met, but we have already picked a rover (e.g., bestIndex doesn’t equal -1), check if the desirability of this rover is better than the desirability of the rover at the current bestIndex.

5. Return an integer, bestIndex.

 IIV. Summary

1. We can create our own compound data types using structs. Structs are composed of several member variables of various types.

2. We can use the dot operator to access a member variable in a struct. 

3. We can specify initial values for each member variable. If we don’t specify an initial value for the member variables, the member variables are uninitialized by default (with the exception of strings, which default to the empty string). 

4. We can also copy structs using a member-by-member copy. 

5. The built in << operator doesn’t work on structs, so we need to write our own functions to print out structs. 

6. Like vectors, structs should either be passed by reference to functions (if the function needs to modify the struct) or passed by const reference (if the function doesn’t need to modify the struct).

 

댓글