renaming labs

This commit is contained in:
Jidong Xiao
2025-01-07 17:16:10 -05:00
parent ad8bdb01ee
commit 190e7fd7f4
18 changed files with 1071 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
# Lab 6 — Reversing Data 8 Ways: STL Vectors vs. STL Lists vs. Homemade Linked Lists
## Checkpoint 1A: Reverse with STL Vector Swaps
*estimate: TBD*
Read the starter code [checkpoint1.cpp](checkpoint1.cpp) and complete the function *reverse* that reverses the contents of an STL vector of integers. For example, if the contents of the vector are in increasing order before the call to reverse_vector, then they will be in decreasing order afterwards. For this checkpoint, use indexing/subscripting/[] on the vector, not iterators (or pointers). You may not use a second vector or array or list.
The trick is to step through the vector one location at a time, swapping values between the first half of the
vector and the second half. As examples, the value at location 0 and the value at location size()-1 must
be swapped, and the value at location 1 and the value at location size()-2 must be swapped.
Make sure your code works with even and odd length vectors. Also add a few more tests to the main function
to make sure your code will work for the special cases of an empty vector and vectors of size 1 and 2.
## Checkpoint 1B: Reverse with STL List Swaps
*estimate: TBD*
Copy your code from Checkpoint 1A to a new file. Then, convert this code to use STL Lists instead of STL
vectors. Start by replacing vector with list everywhere. And youll need to replace your subscripting
with iterators.
You may want to use a straightforward concept we did not discuss in lecture: a reverse iterator. A reverse
iterator is designed to step through a list from the back to the front. An example will make the main
properties clear:
```cpp
std::list<int> a;
unsigned int i;
for ( i=1; i<10; ++i ){
a.push_back( i*i );
}
std::list<int>::reverse_iterator ri;
for( ri = a.rbegin(); ri != a.rend(); ++ri ){
std::cout << *ri << std::endl;
}
```
This code will print out the values 81, 64, 49, . . . , 1, in order, on separate lines. You can also compile and run this [example program](reverse_iterator.cpp).
Observe the type for the reverse iterator, the use of the functions rbegin and rend to provide iterators that delimit the bounds on
the reverse iterator, and the use of the ++ operator to take one step backwards through the list. It is very
important to realize that rbegin and end are NOT the same thing! One of the challenges here will be
determining when to stop (when youve reached the halfway point in the list). You may use an integer
counter variable to help you do this.
For this checkpoint you should not use *erase*, or *insert*, or the *push* or *pop* functions.
Note, youll probably need to add the keyword typename in front of your templated iterator types to unconfuse the compiler.
```cpp
typename std::list<T>::iterator itr = data.begin();
```
**To complete this checkpoint**, show a TA your debugged functions to reverse STL vectors and STL lists
by element swapping. Be sure to ask your TA/mentors any questions you have about regular vs. reverse
iterators for lists and vectors.
## Checkpoint 2: Reverse with STL List Using Insert/Erase/Push/Pop
*estimate: TBD*
Form a team of 4. You may form a team of 2 or 3 only with approval from your graduate lab TA.
Each student should make a copy of their solution file for Checkpoint 1B. And then, each student should
rewrite their STL list reverse function:
- **STUDENT 1** Using only front(), pop_front(), and insert().
- **STUDENT 2** Using only back(), pop_back(), insert(), and iterator increment.
- **STUDENT 3** Using only erase(), push_front(), and iterator dereference.
- **STUDENT 4** Using only erase(), push_back(), iterator dereference, and iterator decrement.
Each of these solutions is allowed to use a for or while loop and a temporary variable to store a single
element, but should not use any auxiliary data structures (no array, no vector, no additional list, etc.)
Note that these solutions are quite different than the algorithms that reverse a vector or list by swapping
values. If the algorithm is paused and printed midway, the vector contents will be different than the algorithms
for Checkpoint 1. (Go ahead, add more calls to the print function in the middle of your reverse function.) Test
and debug your own code before helping your teammates. Discuss the similarities and differences between
the solutions to each version of the reverse function.
**To complete this checkpoint**, as a team, present your debugged solutions to a TA or mentor.
## Checkpoint 3: Reversing a Homemade Linked List
*estimate: TBD*
This checkpoint is an individual checkpoint. (But you can ask your teammates questions if you get stuck.)
Pull out some paper. Following the conventions from lecture, draw a picture of a “homemade” singly-linked
list that stores the values 1, 2, 3, and 4. Make a variable on the stack named my_list of type Node* that
points to the first node in the chain (the node storing the value 1). The 4 node objects should be separate
blobs of memory dynamically-allocated on the heap.
Now, modify this diagram to reverse the list you can do this by only changing pointers! You should use the
existing node objects. Dont copy the entire diagram or make any new nodes. You should not change the
values inside of any node dont swap values like we did for Checkpoint 1.
Then, write pseudo-code to reverse this list, just changing the pointers as you diagrammed above. You may
use helper variables (of type Node*) but no other data structures or variables. Remember that when we
directly manipulate homemade linked lists we dont use iterators.
Finally, read the starter code of checkpoint 3: [checkpoint3.cpp](checkpoint3.cpp). Complete the reverse function using your diagram and pseudocode as a guide. Test and debug the code. Add a few additional test cases to the main function to ensure your code works with an empty list,
and lists with one or two values. Also add a test or two of a node chain with something other than ints.
If you have time, write 2 versions of this function, one version should be iterative (using a for or while loop)
and one version should be recursive.
**Note**: this reverse function takes a pointer as its argument, but we are passing this pointer by reference, because we want to modify this pointer. To understand the concept of passing a pointer by reference, you are recommended to read and run this [example program](reference_to_a_pointer.cpp).
**To complete this checkpoint**, show a TA or mentor your diagram and your debugged function(s) to
reverse a homemade singly-linked list.

View File

@@ -0,0 +1,47 @@
#include <iostream>
#include <string>
#include <vector>
template <class T>
void print(std::vector<T> &data, const std::string &label) {
std::cout << label << " ";
for (int i = 0; i < data.size(); i++) {
std::cout << " " << data[i];
}
std::cout << std::endl;
}
template <class T>
void reverse(std::vector<T> &data) {
// FILL IN THIS FUNCTION
}
int main() {
std::vector<int> data;
data.push_back(1);
data.push_back(2);
data.push_back(3);
data.push_back(4);
data.push_back(5);
data.push_back(6);
data.push_back(7);
print(data,"before:");
reverse(data);
print(data,"after: ");
std::vector<std::string> data2;
data2.push_back("apple");
data2.push_back("banana");
data2.push_back("carrot");
data2.push_back("date");
print(data2,"before:");
reverse(data2);
print(data2,"after: ");
}

View File

@@ -0,0 +1,63 @@
#include <iostream>
#include <string>
// Note: It's ok that all the member variables are public for this
// tiny class.
template <class T>
class Node {
public:
T value;
Node<T> *ptr;
};
template <class T>
void print(Node<T> *data, const std::string &label) {
std::cout << label;
Node<T> *tmp = data;
while (tmp != NULL) {
std::cout << " " << tmp->value;
tmp = tmp->ptr;
}
std::cout << std::endl;
}
template <class T>
void reverse(Node<T>* &input) {
// FILL IN THIS FUNCTION
}
int main() {
// manually create a linked list of nodes with 4 elements
Node<int>* my_list = new Node<int>;
my_list->value = 1;
my_list->ptr = new Node<int>;
my_list->ptr->value = 2;
my_list->ptr->ptr = new Node<int>;
my_list->ptr->ptr->value = 3;
my_list->ptr->ptr->ptr = new Node<int>;
my_list->ptr->ptr->ptr->value = 4;
my_list->ptr->ptr->ptr->ptr = NULL;
print(my_list,"my_list before");
reverse(my_list);
print(my_list,"my_list after ");
// Note: We are not deleting any of the Nodes we created... so this
// program has memory leaks!
}
// ===========================================================================

View File

@@ -0,0 +1,28 @@
/* This example demonstrates the usage of passing a pointer by reference.
* It is needed when you want to modify the pointer.
*/
#include <iostream>
// function to modify the value of a pointer through reference
void modifyPointer(int* & ptr, int& newValue) {
ptr = &newValue; // assign the address of newValue to the pointer
}
int main() {
int value = 42;
int* ptr = &value;
// print the original value of the pointer
std::cout << "Original value of pointer: " << *ptr << std::endl;
int newValue = 100; // new value to assign to the pointer
// pass the pointer by reference to the function, so that we can change the pointer
modifyPointer(ptr, newValue);
// print the modified value of the pointer
std::cout << "Modified value of pointer: " << *ptr << std::endl;
return 0;
}

View File

@@ -0,0 +1,18 @@
#include <iostream>
#include <list>
int main(){
std::list<int> a;
unsigned int i;
for ( i=1; i<10; ++i ){
a.push_back( i*i );
}
std::list<int>::reverse_iterator ri;
for( ri = a.rbegin(); ri != a.rend(); ++ri ){
std::cout << *ri << std::endl;
}
return 0;
}

View File

@@ -0,0 +1,128 @@
# Lab 4 — Memory Diagrams and Memory Debugging
## Checkpoint 1
*estimate: 30-45 minutes*
- Form a group of 2 members. If the number of students in the room is not divisible by 2, the graduate TA will approve a single team with 3 members.
- Introduce yourself to your teammate. Share to your teammate something about yourself (e.g. hobbies, sports, favorite music, etc). Learn something new about your teammate (even if you already know the teammate).
- Our TA/mentor will pick 3 code snippets for each student, from the following 5 code snippets. Following the conventions used in Data Structures lecture for memory diagramming, draw a picture of the stack and the heap that result from executing the code snippet. Use a ? to represent uninitialized values.
[Code Snippet 1](fruits.cpp)
[Code Snippet 2](grains.cpp)
[Code Snippet 3](desserts.cpp)
[Code Snippet 4](veggies.cpp)
[Code Snippet 5](protein.cpp)
Show your diagram to your teammate, and ask the teammate to reverse engineer code for your diagram; at the same time, you reverse engineer code for your teammate's diagram.
When everyone has finished writing code for the drawing, discuss your answers as a group. Are differences in the original and reverse engineered code valid alternate answers? Or was something misinterpreted? Critique each others diagrams. What could be improved or drawn more clearly? What statements need to be added to each example to clean up / prevent memory leaks?
**To complete this checkpoint**, present your work to a TA/mentor.
## Checkpoint 2 & 3:
Checkpoints 2 and 3 focus on using a memory debugger. It is highly recommended that you thoroughly read the instructions for Checkpoint 2 and Checkpoint 3 before starting.
Memory debuggers will be a useful tool in many of the assignments this semester, and in C++ development if you work with the language outside of this course. While a traditional debugger lets you step through your code and examine variables, a memory debugger instead reports memory-related errors during execution and can help find memory leaks after your program has terminated. The next time you see a “segmentation fault”, or it works on your machine but not on Submitty, try running a memory debugger!
Read lecture notes [6.7](../../lectures/06_memory#67-memory-debugging), [6.8](../../lectures/06_memory#68-sample-buggy-program), [6.9](../../lectures/06_memory#69-using-dr-memory-httpwwwdrmemoryorg), [6.10](../../lectures/06_memory#610-using-valgrind-httpvalgrindorg), and [6.11](../../lectures/06_memory#610-using-valgrind-httpvalgrindorg), and try to understand the example buggy program code and what DrMemory or Valgrind say about this buggy program. Ask questions if you don't understand the code or if you don't understand the output of DrMemory or the output of Valgrind.
Please download the following 4 files needed for this lab:
[buggy_lab4.cpp](./buggy_lab4.cpp)
[first.txt](./first.txt)
[middle.txt](./middle.txt)
[last.txt](./last.txt)
## Checkpoint 2
*estimate: 20-40 minutes*
For Checkpoint 2 of this lab, we will heavily rely on dynamic memory to find the average and smallest number for a set of data from an input file. You will use a memory debugging tool such as DrMemory or Valgrind to fix memory errors and leaks in buggy_lab4.cpp. Make sure to download the provided .txt files as well.
First take a look at the code inside the identifyMeanAndMin() function. You will notice that the syntax used
throughout the program may be a little different than what you are used to. Try and familiarize yourself with
this syntax before you start working on the assignment. What does this line of code do?
```cpp
*(intArray + *numElements) = readInt;
```
Whats going on inside the *for* loop? If you are stuck on this, ask a mentor or TA for help as
soon as possible and refer back to your lecture notes on pointers and arrays.
Once you have done this, compile the program using:
```console
g++ buggy_lab4.cpp -o buggy_lab4.out -g -Wall
```
Try running the program normally using:
```console
./buggy_lab4.out
```
You will notice that a segmentation fault occurs. Now run this program using either Valgrind or DrMemory. If running using Valgrind, remember to use --leak-check=yes or --leak-check=full.
Your memory debugger should give you more context as to why a segmentation fault is occurring, especially if you have compiled with *-g*. To complete this checkpoint, add or modify code in the areas marked Parts 1, 2, and 3 to resolve all memory errors and leaks. As you are working on this, be sure to also think about the questions asked in Checkpoint 3.
It is highly recommended that you tackle one part at a time. For example, after adding a few lines of code to part 1, you will now receive different memory errors when you recompile and run the program using your memory debugger. Similarly, fixing all memory errors in part 2 will generate different memory errors that should be resolved in part 3.
In part 2 of buggy_lab4.cpp, the goal is to print out the contents of intArray in reverse order, while also calculating the sum of all elements in the array and keeping track of the smallest number encountered. Solutions that attempt to print the contents of the array in a different manner or end up with the wrong
value for the smallest number found or sum won't be accepted.
Note that:
- You are only allowed to modify or add code when asked to. This would be between the comments that
indicate parts 1, 2, and 3 inside buggy_lab4.cpp. Do not modify other parts of the code or create any
helper functions.
- You are not allowed to declare new variables; the ones provided are more than enough. Hint: how do
we create memory on the heap?
- You are not allowed to use additional data structures (like arrays or vectors).
**You will receive no credit if you do not follow the above restrictions.**
**To receive credit for this checkpoint**: Fix buggy_lab4.cpp so that it successfully prints out the average
and smallest number for a given set of data and is free of all memory errors and leaks on your local machine.
Submit buggy_lab4.cpp on Submitty and verify that there are no memory errors there as well and show a
mentor or TA both results. Also explain to a mentor TA what you added or modified in the program to
resolve all memory errors.
## Checkpoint 3
*estimate: 15-25 minutes*
As you work through Checkpoint 2, try and pay close attention the analysis given by DrMemory or Valgrind
and think about the following:
1. How does the output from your memory debugger differ when you compile your program with the -g
flag compared to when you leave it out?
2. How would you rewrite the for loop in part 2 to use the bracket [] operator instead of pointer syntax?
3. For DrMemory users, you would have encountered all of these errors in parts 1, 2, or 3 of Checkpoint 2:
```console
UNITIALIZED READ
UNADDRESSABLE ACCESS
INVALID HEAP ARGUMENT
LEAK
```
4. What do you think each of these errors mean?
5. For Valgrind users, the errors you will have seen are:
```console
Use of uninitialised value
Invalid write
Invalid read
Conditional jump or move depends on uninitialised value(s)
Mismatched free() / delete / delete []
? bytes in ? blocks are definitely lost in loss record ? of ?
```
**To receive credit for this checkpoint**, discuss your answers to these questions with a TA or mentor.
## Checkpoint 4
*remainder of the lab time*
For this checkpoint, you team up with one or two other students from your lab section. Our TA or mentor will pick one problem from the practice packet for you two (or three) to work together as a group. You should not look at the posted solutions. Work as a group (e.g., pair programming!) to come up with a complete solution to the problem.
Once you have completed and proofread and triple-checked your answer, launch your C++ development environment. First, focus on getting it to compile. What basic, minimal testing infrastructure is necessary to get your solution to compile? What small bits of syntax were you missing or incorrect or sloppy? Try to avoid making these mistakes on Thursday's test!
Then, write a simple test case to exercise your solution. Compile and run the code. As time follows, fix bugs in your solution and improve and expand your test cases.
**To complete this checkpoint**, When about 10 minutes remain in the lab, the TA and mentors will circulate through the room to finalize checkoffs. Show them your initial solution, and explain the typos and errors you found and debugged in working through your computer tested solution. Explain your testing & debugging process.

View File

@@ -0,0 +1,116 @@
#include <iostream>
#include <fstream>
#define MAX_ARRAY_SIZE 50
/** IMPORTANT: SOLUTIONS THAT DO NOT FOLLOW BELOW INSTRUCTIONS WILL RECEIVE NO CREDIT
* Do not add or modify code in this file UNLESS ASKED TO!
* You are NOT allowed to declare new variables; use the ones already declared.
* You are NOT allowed to create new helper functions or modify checkCorrectSmallest()
**/
/** DO NOT MODIFY THIS FUNCTION! **/
int checkCorrectSmallest(int* smallestNum, const std::string& filename) {
if (filename == "first.txt" || filename == "middle.txt") {
if (*smallestNum != 1) {return -1;}
}
else if (filename == "last.txt") {
if (*smallestNum != 22) {return -1;}
}
return 1;
}
/** A function that will identify the mean and smallest number
* in a set of data provided that there are at most 50 numbers
* in the file.
*
* @param filename: The name of a file that contains a list of numbers.
*
* Task: Add or modify code in the appropriate sections to fix memory and logic errors
* without using data structures (such as an array or vector) and without using any
* additional memory on the stack.
**/
void identifyMeanAndMin(const std::string& filename) {
int* numElements;
int* sum;
int* smallestNum;
float* avg;
/** PART 1: ADD CODE BELOW **/
/** PART 1: ADD CODE ABOVE **/
*numElements = 0;
*sum = 0;
int readInt;
int* intArray = new int[MAX_ARRAY_SIZE];
std::ifstream input(filename);
while (input >> readInt) {
*(intArray + *numElements) = readInt;
*numElements += 1;
}
std::cout << "Printing the contents of the array in reverse order: ";
/** PART 2: MODIFY CODE BELOW **/
for (int i = MAX_ARRAY_SIZE; i >= -1; i--) {
// If we're at the beginning of the for loop, initalize *smallestNum
// Else, compare *smallestNum to current element in the for loop
if (i == MAX_ARRAY_SIZE) {
*smallestNum = *(intArray + i);
}
else {
if (*smallestNum > *(intArray + i)) {
*smallestNum = *(intArray + i);
}
}
/** PART 2: MODIFY CODE ABOVE **/
*sum += *(intArray + i);
std::cout << *(intArray + i) << " ";
}
std::cout << std::endl;
if (checkCorrectSmallest(smallestNum, filename) == -1) {
std::cout << "ERROR: incorrect value for smallest number" << std::endl;
return;
}
*avg = *sum / float(*numElements);
std::cout << "The average for the set of numbers in " << filename << " is "
<< *avg << " and the smallest number is " << *smallestNum
<< std::endl;
/** PART 3: ADD AND/OR MODIFY CODE BELOW **/
delete intArray;
/** PART 3: ADD AND/OR MODIFY CODE ABOVE **/
}
int main() {
identifyMeanAndMin("first.txt");
std::cout << std::endl;
identifyMeanAndMin("middle.txt");
std::cout << std::endl;
identifyMeanAndMin("last.txt");
return 0;
}

View File

@@ -0,0 +1,10 @@
bool** cake;
bool pie;
bool fudge;
pie = true;
cake = new bool*[5];
cake[1] = &pie;
bool* donut = new bool;
*donut = false;
cake[2] = donut;
cake[4] = &fudge;

View File

@@ -0,0 +1,4 @@
1
2
3
4

View File

@@ -0,0 +1,10 @@
int pear = 3;
int* apple;
int banana[pear];
int* orange;
apple = new int[pear];
orange = &banana[1];
apple[0] = 6;
apple[1] = 7;
apple[2] = 8;
*orange = 5;

View File

@@ -0,0 +1,10 @@
float* oat[3];
oat[1] = new float;
*oat[1] = 3.14;
oat[2] = new float;
*oat[2] = 6.02;
float rice;
float* wheat;
wheat = oat[2];
float** barley = new float*;
*barley = oat[1];

View File

@@ -0,0 +1,49 @@
22
247
279
240
253
342
87
181
391
57
23
302
168
367
236
240
187
368
216
185
31
255
122
140
69
46
287
69
268
58
134
330
172
291
175
63
184
329
30
337
229
274
130
95
255
331
24
325
228

View File

@@ -0,0 +1,10 @@
89
34
13
5
2
1
3
8
21
55

View File

@@ -0,0 +1,11 @@
int tofu = 3;
int chicken = 2;
double** fish = new double*[tofu];
for (int beef = 0; beef < tofu; beef++) {
fish[beef] = new double[chicken];
}
fish[0][0] = 1.41421;
fish[0][1] = 1.61803;
fish[1][0] = 2.71828;
fish[1][1] = 3.14159;
fish[2][0] = 6.02214;

View File

@@ -0,0 +1,10 @@
char*** carrot;
char** broccoli;
char* tomato;
char radish = 'q';
tomato = new char;
*tomato = 'z';
broccoli = new char*;
*broccoli = tomato;
carrot = new char**;
*carrot = broccoli;

218
labs/vectors/README.md Normal file
View File

@@ -0,0 +1,218 @@
# Lab 5 — Vec Implementation
## Checkpoint 1
*estimate: 20 minutes*
- Team up with one student in your lab section. MEET SOMEONE NEW! You may not work
with someone who was on your team for Lab 4. Ask a TA or mentor to help you find a partner.
If the number of students in the room is not even, the graduate TA will approve a single team with 3
members.
- Introduce yourself to your teammate. Ask them to share something about themselves (e.g. hobbies,
sports, favorite music, etc.) Learn something new about your teammate (even if you already know
them).
- There are two sets of functions below. Each student in your team takes one set and working on it.
For each function below, assign different letters to each of the data sizes that at first glance might have impact
on the running time of the function. Be sure to consider integer value, size of vector, and length of string.
Then give the big O notation of the function in terms of those variables.
### Student 1
```cpp
int foobar (const std::vector<std::string> &a, int b) {
int answer = 0;
for (int i = 0; i < a.size(); i+=b) {
answer++;
}
return answer;
}
```
```cpp
void foo2 (const std::vector<int> &a, std::string &b) {
b.clear();
for (int i = 0; i < a.size(); i++)
{
if (a[i] > 0)
b.push_back('+');
else
b.push_back('-');
}
}
```
```cpp
std::vector<int> foo3 (const std::vector<int> &a, const std::string &b) {
return std::vector<int>(b.size(),a.size());
}
```
```cpp
int foo3 (const std::vector<std::string> &a, const std::string& b) {
int ret = 0;
for (int i=0; i<a.size(); i++){
ret += (a[i] == b);
}
return ret;
}
```
```cpp
std::vector<int> foo4 (const std::vector<int> &a) {
std::vector<int> answer = a;
for (int i = 0; i < a.size(); i++) {
if(a[i] < (a[a.size()-1]*a[a.size()-1])){
answer.erase(answer.end()-1);
}
}
return answer;
}
```
```cpp
std::vector<int> foo5 (const std::vector<int> &a, int b) {
std::vector<int> ret;
for(int i=0; i<a.size(); i++){
if(a[i] < b){
ret.insert(ret.end(),a[i]);
}
}
return ret;
}
```
### Student 2
```cpp
int foobar (const std::vector<std::string> &a, int b) {
int answer = 0;
for (int i = 0; i < a.size(); i+=b) {
answer++;
}
return answer;
}
```
```cpp
std::vector<int> bar2 (const std::vector<std::string> &a) {
std::vector<int> answer;
for (int i = 0; i < a.size(); i++) {
answer.push_back(a[i].size());
}
return answer;
}
```
```cpp
std::vector<std::string> bar3 (const std::vector<int> &a) {
std::vector<std::string> answer;
for (int i = 0; i < a.size(); i++) {
answer.push_back(std::string(a[i],'+'));
}
return answer;
}
```
```cpp
void bar3 (std::vector<std::string> &a, const std::string &b) {
for (int i = 0; i < a.size(); i++) {
a[i] = b;
}
}
```
```cpp
std::vector<int> bar4 (const std::vector<std::string> &a) {
std::vector<int> answer;
if(!a.empty()){
for (int i = 0; i < std::min(a[0].size(), a.size()); i++) {
answer.insert(answer.begin(),a[i].size());
}
}
return answer;
}
```
```cpp
void bar5 (std::vector<int> &a) {
for (int i = 0; i < a.size(); i++){
if (a[i] > 0){
a.erase(a.begin() + i);
i--;
}
}
}
```
When you finish, discuss these problems with your teammate. If your teammate hasnt finished, please help
them (but without just doing the problems for them).
Once you are both finished, type these examples into your C++ editor, add print statements, and confirm your answers are correct. What print statements will be most helpful? In your terminal, instead of running just
```console
./a.out
```
try running
```console
time ./a.out
```
and reading the real time, which is how long your program took to run. How does this change as you increase or decrease each of the data size variables you identified above?
**To complete this checkpoint**, as a team, present your work to a TA/mentor.
## Checkpoint 2
*estimate: 30-40 minutes*
Write a templated non-member function named remove_matching_elements that takes in two arguments,
a vector of type Vec&lt;T&gt; and an element of type T, and returns the number of elements that matched the
argument and were successfully removed from the vector. The order of the other elements should stay
the same. For example, if v, a Vec&lt;int&gt; object contains 6 elements: 11 22 33 11 55 22 and you call
remove_matching_elements(v,11), that call should return 2, and v should now contain: 22 33 55 22.
You should not create a new vector in your function.
Add several test cases to test_vec.cpp to show that the function works as expected. What is the order
notation of your solution in terms of n the size of the vector, and e the number of occurences of the input
element in the vector?
*Note*: when you are in a non-member function, and you want to use the iterator, which is a member type of the Vec&lt;T&gt; class, you have to use the *typename* keyword before the Vec&lt;T&gt;:: scope. For example, if you want to define an iterator named *itr*, you can do it like this:
```console
typename Vec<T>::iterator itr
```
without this *typename* keyword, if you define the iterator *itr* like this:
```console
Vec<T>::iterator itr
```
you will get a compiler error saying:
```console
error: need typename before Vec<T>::iterator because Vec<T> is a dependent scope
```
And the reason that this keyword *typename* is needed, is because without it, the compiler would think that Vec&lt;T&gt;::iterator is a member variable of the Vec&lt;T&gt; class, but this *typename* explicitly tells the compiler that Vec&lt;T&gt;::iterator is a type, rather than a member variable.
**To complete this checkpoint**, show a TA your debugged solution for remove_matching_elements and
be prepared to discuss the order notation of the function.
## Checkpoint 3
*estimate: 30 minutes*
Add a print member function to Vec to aid in debugging. (Note, neither remove_matching_elements nor
print are part of the STL standard for vector). You should print the current information stored in the
variables capacity, m_size, and m_data. Use the print function to confirm your remove_matching_elements
function is debugged. Also, write a test case that calls push_back many, many times (hint, use a for loop!)
and observe how infrequently re-allocation of the m_data array is necessary.
To verify your code does not contain memory errors or memory leaks, use Valgrind and/or Dr. Memory on
your local machine see instructions on the course webpage: Memory Debugging. Also, submit your code
to the homework server (in the practice space for lab 4), which is configured to run the memory debuggers
for this exercise. To verify that you understand the output from Valgrind and/or Dr. Memory, temporarily
add a simple bug into your implementation to cause a memory error or memory leak.
**To complete this checkpoint**, show a TA your tested & debugged program. Be prepared to demo and
discuss the Valgrind and/or Dr. Memory output: with and without memory errors and memory leaks AND
on your local machine and on the homework server.

97
labs/vectors/test_vec.cpp Normal file
View File

@@ -0,0 +1,97 @@
#include <iostream>
#include <cmath>
using namespace std;
#include "vec.h"
int main() {
// ---------------------------------------------------
// initialize v1 with 10 values... the multiples of 5
Vec<int> v1( 10, 0 );
int i;
for ( i = 0; i < v1.size(); i++) {
v1[i] = 5 * i;
}
cout << "v1.size() = " << v1.size() << ". Should be 10.\n";
cout << "Contents of v1 (multiples of 5):";
for ( i = 0; i<v1.size(); ++i ) {
cout << " " << v1[i];
}
cout << endl;
// --------------------------------------------------------------------------
// make v2 be a copy of v1, but then overwrite the 2nd half with the 1st half
Vec<int> v2( v1 );
v2[ 9 ] = v2[ 0 ];
v2[ 8 ] = v2[ 1 ];
v2[ 7 ] = v2[ 2 ];
v2[ 6 ] = v2[ 3 ];
v2[ 5 ] = v2[ 4 ];
cout << "Contents of v1 (still multiples of 5):";
for ( i = 0; i<v1.size(); ++i )
cout << " " << v1[i];
cout << endl;
cout << "Contents of v2 (now palindrome):";
for ( i = 0; i<v2.size(); ++i )
cout << " " << v2[i];
cout << endl;
// ------------------------------------------
// make v3 be a copy of v2, but then clear it
Vec<int> v3;
v3 = v2;
v3.clear();
cout << "\nAfter copying v2 to v3 and clearing v3, v2.size() = "
<< v2.size() << " and v3.size() = " << v3.size() << endl;
cout << "Contents of v2 (should be unchanged):";
for ( i = 0; i<v2.size(); ++i ) {
cout << " " << v2[i];
}
cout << endl;
// --------------
// test push back
cout << "\nNow testing push_back. Adding 3, 6, 9 to v2:\n";
v2.push_back( 3 );
v2.push_back( 6 );
v2.push_back( 9 );
cout << "v2 is now: \n";
for ( i = 0; i<v2.size(); ++i ) {
cout << " " << v2[i];
}
cout << endl;
// -----------
// test resize
v1.resize(20,100);
cout << "\nNow testing resize. Resizing v1 to have 20 elements and v2 to have 2 elements\n";
cout << "v1 is now (should have 100s at the end): \n";
for ( i = 0; i<v1.size(); ++i )
cout << " " << v1[i];
cout << endl;
v2.resize(2,100);
cout << "v2 is now: \n";
for ( i = 0; i<v2.size(); ++i )
cout << " " << v2[i];
cout << endl;
// ------------------------
// test of a vec of doubles
cout << "\nStarting from an empty vector, z, of doubles and doing\n"
<< "5 push_backs\n";
Vec<double> z;
for ( i = 0; i<5; ++i )
z.push_back( sqrt( double(10*(i+1)) ));
cout << "Contents of vector z: ";
for (int j = 0; j < z.size(); j++ )
cout << " " << z[j];
cout << endl;
// ADD MORE TEST CASES HERE
return 0;
}

141
labs/vectors/vec.h Normal file
View File

@@ -0,0 +1,141 @@
#ifndef Vec_h_
#define Vec_h_
// Simple implementation of the vector class, revised from Koenig and Moo. This
// class is implemented using a dynamically allocated array (of templated type T).
// We ensure that that m_size is always <= capacity and when a push_back or resize
// call would violate this condition, the data is copied to a larger array.
template <class T> class Vec {
public:
// TYPEDEFS
typedef T* iterator;
typedef const T* const_iterator;
// CONSTRUCTORS, ASSIGNMNENT OPERATOR, & DESTRUCTOR
Vec() { this->create(); }
Vec(int n, const T& t = T()) { this->create(n, t); }
Vec(const Vec& v) { copy(v); }
Vec& operator=(const Vec& v);
~Vec() { delete [] m_data; }
// MEMBER FUNCTIONS AND OTHER OPERATORS
T& operator[] (int i) { return m_data[i]; }
const T& operator[] (int i) const { return m_data[i]; }
void push_back(const T& t);
iterator erase(iterator p);
void resize(int n, const T& fill_in_value = T());
void clear() { delete [] m_data; create(); }
bool empty() const { return m_size == 0; }
int size() const { return m_size; }
// ITERATOR OPERATIONS
iterator begin() { return m_data; }
const_iterator begin() const { return m_data; }
iterator end() { return m_data + m_size; }
const_iterator end() const { return m_data + m_size; }
private:
// PRIVATE MEMBER FUNCTIONS
void create();
void create(int n, const T& val);
void copy(const Vec<T>& v);
// REPRESENTATION
T* m_data; // Pointer to first location in the allocated array
int m_size; // Number of elements stored in the vector
int capacity; // Number of array locations allocated, m_size <= capacity
};
// Create an empty vector (null pointers everywhere).
template <class T> void Vec<T>::create() {
m_data = NULL;
m_size = capacity = 0; // No memory allocated yet
}
// Create a vector with size n, each location having the given value
template <class T> void Vec<T>::create(int n, const T& val) {
m_data = new T[n];
m_size = capacity = n;
for (T* p = m_data; p != m_data + m_size; ++p)
*p = val;
}
// Assign one vector to another, avoiding duplicate copying.
template <class T> Vec<T>& Vec<T>::operator=(const Vec<T>& v) {
if (this != &v) {
delete [] m_data;
this -> copy(v);
}
return *this;
}
// Create the vector as a copy of the given vector.
template <class T> void Vec<T>::copy(const Vec<T>& v) {
this->capacity = v.capacity;
this->m_size = v.m_size;
this->m_data = new T[this->capacity];
// Copy the data
for (int i = 0; i < this->m_size; ++i)
this -> m_data[ i ] = v.m_data[ i ];
}
// Add an element to the end, resize if necesssary.
template <class T> void Vec<T>::push_back(const T& val) {
if (m_size == capacity) {
// Allocate a larger array, and copy the old values
// Calculate the new allocation. Make sure it is at least one.
capacity *= 2;
if (capacity < 1) capacity = 1;
// Allocate and copy the old array
T* new_data = new T[capacity];
for (int i=0; i<m_size; ++i)
new_data[i] = m_data[i];
// Delete the old array and reset the pointers
delete [] m_data;
m_data = new_data;
}
// Add the value at the last location and increment the bound
m_data[m_size] = val;
++ m_size;
}
// Shift each entry of the array after the iterator. Return the iterator,
// which will have the same value, but point to a different element.
template <class T> typename Vec<T>::iterator Vec<T>::erase(iterator p) {
// remember iterator and T* are equivalent
for (iterator q = p; q < m_data+m_size-1; ++q)
*q = *(q+1);
m_size --;
return p;
}
// If n is less than or equal to the current size, just change the size. If n is
// greater than the current size, the new slots must be filled in with the given value.
// Re-allocation should occur only if necessary. push_back should not be used.
template <class T> void Vec<T>::resize(int n, const T& fill_in_value) {
if (n <= m_size)
m_size = n;
else {
// If necessary, allocate new space and copy the old values
if (n > capacity) {
capacity = n;
T* new_data = new T[capacity];
for (int i=0; i<m_size; ++i)
new_data[i] = m_data[i];
delete [] m_data;
m_data = new_data;
}
// Now fill in the remaining values and assign the final size.
for (int i = m_size; i<n; ++i)
m_data[i] = fill_in_value;
m_size = n;
}
}
#endif