Section 15.10 Vectors of Structs
Now imagine we have a large number of students and we would like to be able to write code that loops through all of the students to perform various calculations. To accommodate this, we would want to make a vector of type
Student
.
// Make the students vector
vector<Student> students;
// Add two students to the vector
Student student1 = {
"Beth Jones",
{ 84.2, 94.3, 96.7 }
};
Student student2 = {
"John Smith",
{ 78.2, 88.3, 92.7 }
};
students.push_back(student1);
students.push_back(student2);
That code would result in memory that could be visualized as follows:
students
vectorThat memory diagram can be helpful to figure out how we reference a particular piece of information.
-
students
is the top level name of the vector. -
The two elements of the vector are accessed using the
at
method. So to access the first student, we would usestudents.at(0)
. At that point, we are naming the box that is above 0. It is the entireStudent
struct for Beth. -
To access a member of the struct, we use
.fieldName
. So to access the name of that student, we would usestudents.at(0).name
. -
To access the exam scores of that student, we would use
students.at(0).exams
. That names the entire vector of scores for that student. To get a particular exam we would use.at
to index into the vector. Thusstudents.at(0).exams.at(1)
would get the second score of the first student. (94.3)
Subsection 15.10.1 Avoiding Copies
As we work with a collection of structs, we will want to be careful to avoid making unwanted copies of structs. If we are just reading data, making copies of data will be slightly inefficient, but should be otherwise harmless. However, when we are modifying data, copies can easily introduce bugs. If we by accident make a copy of an item and then modify the copy but expect the original to change, that will cause unexpected results in our code.
The key to remember is that assignment into a struct variable copies. Consider this failed attempt to set the first studentβs first exam score to 100:
vector<Student> students;
// assume code here adds some students to the vector
// the first student is "Beth Jones" and has exam scores of 84.2, 94.3, 96.7
// This COPIES the first student
Student s1 = students.at(0);
s1.exams.at(0) = 100.0;
s1
is a copy of the data from the vector. It is a duplicate of the βBeth Jonesβ Student struct:
students
vector and s1That is great if we wanted to copy the student. But in this case, we were just trying to make it easier to refer to that student. We didnβt want to write out
students.at(0).exams.at(0)
. s1
was supposed to be a shorter alias for students.at(0)
. But because s1
is a copy, we never managed to change the score of the original βBeth Jonesβ struct.
To avoid making a copy, we can use a reference. Remember that a reference is an alias - another name for a thing that already exists. In this situation, that is what we really wantβa new name for the existing struct. So we should declare
s1
to be a reference variable and use that to refer to the student:
vector<Student> students;
// assume code here adds some students to the vector
// Make a reference to the first student
Student& s1 = students.at(0);
s1.exams.at(0) = 100.0; // This modifies the student in the vector
students
vector and s1 as a referenceNow
s1.exams.at(0) = 100.0
correctly changes the score in the vector.
A common place to run into this issue is while trying to loop through students. Say I want to clear all of the exams for each student. I can loop through the students using an enhanced for loop or a counting loop. I just need to make sure that I use a reference to store any temporary name for the student:
for (Student& s : students) {
s.exams.clear();
}
// or
for (size_t i = 0; i < students.size(); ++i) {
Student& s = students.at(i);
s.exams.clear();
}
But if I forget to use a reference, I will make a copy of the student and then clear the exams for that copy. The original student will be unchanged:
for (Student s : students) { {
s.exams.clear();
// BAD: This copies the student...
for (size_t i = 0; i < students.size(); ++i) {
Student s = students.at(i);
s.exams.clear();
// BAD: This copies the student...
Checkpoint 15.10.2.
Checkpoint 15.10.3.
You have attempted of activities on this page.