Skip to content

Commit 09f12cc

Browse files
committed
release: assignment 5
1 parent 13a2854 commit 09f12cc

File tree

9 files changed

+1130
-0
lines changed

9 files changed

+1130
-0
lines changed

assignment5/README.md

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
<p align="center">
2+
<img src="docs/logo.jpeg" alt="A logo of Treebook, a fictional Stanford-based social media startup" style="width: 300px; height: auto;" />
3+
</p>
4+
5+
# Assignment 5: Treebook
6+
7+
Due Saturday, November 15th at 11:59PM
8+
9+
## Overview
10+
11+
The newest Stanford social media startup is Treebook, and you’re a founding member of the team! To get the product off the ground and compete with an unnamed, completely legally unaffiliated app from Harvard, you’ve been assigned the task of implementing user profiles.
12+
13+
For this assignment, you will be implementing parts of a class to allow for operator overloads, as well as to modify some aspects of the special member functions.
14+
15+
There are two files you'll work with for this assignment:
16+
17+
* `user.h` - Contains the declaration for the `User` class that you will extend with special member functions and operators.
18+
* `user.cpp` - Contains the definition of the `User` class.
19+
20+
To download the starter code for this assignment, please see the instructions for [**Getting Started**](../README.md#getting-started) on the course assignments repository.
21+
22+
## Running your code
23+
24+
To run your code, first you'll need to compile it. Open up a terminal (if you are using VSCode, hit <kbd>Ctrl+\`</kbd> or go to **Terminal > New Terminal** at the top). Then make sure that you are in the `assign5/` directory and run:
25+
26+
```sh
27+
g++ -std=c++20 main.cpp user.cpp -o main
28+
```
29+
30+
Assuming that your code compiles without any compiler errors, you can now do:
31+
32+
```sh
33+
./main
34+
```
35+
36+
which will actually run the `main` function in `main.cpp`.
37+
38+
As you are following the instructions below, we recommend intermittently compiling/testing with the autograder as a way to make sure you're on the right track!
39+
40+
> [!NOTE]
41+
>
42+
> ### Note for Windows
43+
>
44+
> On Windows, you may need to compile your code using
45+
>
46+
> ```sh
47+
> g++ -static-libstdc++ -std=c++20 main.cpp user.cpp -o main
48+
> ```
49+
>
50+
> in order to see output. Also, the output executable may be called `main.exe`, in which case you'll run your code with:
51+
>
52+
> ```sh
53+
> ./main.exe
54+
> ```
55+
56+
## Part 1: Viewing Profiles
57+
58+
Take a look at the `user.h` header file. Your coworkers have begun to write a `User` class that will store the name and friends list of each user who joins your social media platform! In order to keep this class super efficient, they have chosen to represent the list of friends as a raw pointer array of `std::string` (kind of like how a `std::vector` stores its elements behind the scenes). Thankfully, they have already written logic for creating a new `User` and for adding friends to an existing `User`'s friend list (`add_friend`), but they've begun to experience some strange issues when working with `User` objects.
59+
60+
To begin with, there's no easy way to print information about each `User` object to the console, which has made debugging at Treebook difficult. To help your coworkers out, write an `operator<<` method that prints a `User` to a `std::ostream`. **This operator should be declared as a friend function in `user.h` and implemented in `user.cpp`.** For example, a user named `"Alice"` with friends `"Bob"` and `"Charlie"` should give the following output when printed to the console:
61+
62+
```
63+
User(name=Alice, friends=[Bob, Charlie])
64+
```
65+
66+
Note: `operator<<` should not print any newline characters.
67+
68+
> [!IMPORTANT]
69+
> In your implementation of `operator<<`, you will need to access and loop through the `_friends` private field of the `User` class in order to print out a user's friends. Normally, you cannot access private fields inside of a class in a non-member function—in this case, we can get around this restriction by marking `operator<<` as a **friend function inside of the `User` class.** See the slides for Tuesday's lecture for more information!
70+
71+
## Part 2: Unfriendly Behaviour
72+
73+
With the help of your `operator<<`, your coworkers have been able to make good progress on the social media app. However, they can't quite wrap their head around some seemingly bizzare issues that occur when they try to make copies of `User` objects in memory. Having recently taken CS106L, you suspect that it might have something to do with the special member functions (or the lack thereof) on the `User` class. To fix this issue, we'll implement our own versions of the special member functions (SMFs) for the `User` class, and remove some of the others for which the compiler generated versions are insufficient.
74+
75+
To be specific, you will need to:
76+
77+
1. Implement a destructor for the `User` class. To do so, implement the `~User()` SMF.
78+
2. Make the `User` class copy constructible. To do so, implement the `User(const User& user)` SMF.
79+
3. Make the `User` class copy assignable. To do so, implement the `User& operator=(const User& user)` SMF.
80+
4. Prevent the `User` class from being move constructed. To do so, delete the `User(User&& user)` SMF.
81+
5. Prevent the `User` class from being move assigned. To do so, delete the `User& operator=(User&& user)` SMF.
82+
83+
In performing these tasks, you are expected to make changes to **both** the `user.h` and `user.cpp` files.
84+
85+
> [!IMPORTANT]
86+
> In your implementations of points 2 and 3 above, you will need to copy the contents of the `_friends` array. Recall from Thursday's lecture on special member functions that you can copy a pointer array by first allocating memory for a new one (possibly within a member initializer list), and then copying over the elements with a for loop.
87+
> Make sure that you also set the `_size`, `_capacity`, and `_name` of the instance you are changing as well!
88+
89+
## Part 3: Always Be Friending
90+
91+
After making changes to the special member functions, you've been able to scale out Treebook across Stanford and word has started to spread at other universities! However, your coworkers and you have found that some common use cases for the `User` class are either inconvenient or impossible given how the class is currently written, and you think you might be able to fix this by implementing some custom operators.
92+
93+
You will overload two operators for the `User` class. **Please implement both of these operators as member functions** (i.e. declare them inside of the `User` class in `user.h` and provide implementations in `user.cpp`).
94+
95+
### `operator+=`
96+
97+
The `+=` operator will representing adding a user to another user's friend list. This should be symmetric, meaning that adding, for example, Charlie to Alice's friend list should cause Alice to also be in Charlie's list. For example, consider this code:
98+
99+
```cpp
100+
User alice("Alice");
101+
User charlie("Charlie");
102+
103+
alice += charlie;
104+
std::cout << alice << std::endl;
105+
std::cout << charlie << std::endl;
106+
107+
// Expected output:
108+
// User(name=Alice, friends=[Charlie])
109+
// User(name=Charlie, friends=[Alice])
110+
```
111+
112+
The function signature for this operator should be `User& operator+=(User& rhs)`. Note that like the copy-assignment operator, it returns a reference to itself.
113+
114+
### `operator<`
115+
116+
Recall that the `<` operator is required to store users in a `std::set`, as `std::set` is implemented in terms of the comparison operator. Implement `operator<` to compare users alphabetically by name. For example:
117+
118+
```cpp
119+
User alice("Alice");
120+
User charlie("Charlie");
121+
122+
if (alice < charlie)
123+
std::cout << "Alice is less than Charlie";
124+
else
125+
std::cout << "Charlie is less than Alice";
126+
127+
// Expected output:
128+
// Alice is less than Charlie
129+
```
130+
131+
The function signature for this operator should be `bool operator<(const User& rhs) const`.
132+
133+
## 🚀 Submission Instructions
134+
135+
If you pass all tests, you are ready to submit! To submit the assignment:
136+
1. Please complete the feedback form [at this link](https://forms.gle/tfLJSKnuUbUx9Xdi6).
137+
2. Submit your assignment on [Paperless](https://paperless.stanford.edu)!
138+
139+
Your deliverable should be:
140+
141+
- `user.h`
142+
- `user.cpp`
143+
144+
You may resubmit as many times as you'd like before the deadline.
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import functools
2+
from typing import List
3+
from utils import Autograder, ASSIGNMENT_DIR
4+
5+
import os
6+
import subprocess
7+
8+
9+
def get_main_exe_path() -> os.PathLike:
10+
main_path = os.path.join(ASSIGNMENT_DIR, "main")
11+
if not os.path.isfile(main_path):
12+
main_path = os.path.join(ASSIGNMENT_DIR, "main.exe")
13+
if not os.path.isfile(main_path):
14+
raise RuntimeError(
15+
"Could not find a main executable. Did you compile your code with the command in the README?"
16+
)
17+
return main_path
18+
19+
20+
@functools.lru_cache
21+
def get_memory_leak_exit_code() -> int:
22+
main_path = get_main_exe_path()
23+
result = subprocess.check_output([main_path, "memory_leak_exit_code"], text=True)
24+
return int(result.strip())
25+
26+
27+
def verify_output(test_case: str, expected: List[str]):
28+
main_path = get_main_exe_path()
29+
result = subprocess.run(
30+
[main_path, test_case],
31+
stdout=subprocess.PIPE,
32+
stderr=subprocess.PIPE,
33+
text=True,
34+
)
35+
36+
if result.returncode == get_memory_leak_exit_code():
37+
raise RuntimeError(f"💧Memory leak detected: {result.stderr}")
38+
39+
if result.returncode != 0:
40+
raise RuntimeError(result.stderr)
41+
42+
actual = result.stdout.strip()
43+
if not actual:
44+
actual = []
45+
else:
46+
actual = actual.split("\n")
47+
48+
if actual != expected:
49+
nl = "\n\t"
50+
raise RuntimeError(
51+
f"Test output did not match expected. Expected: \n{nl}{nl.join(expected)}\n\nGot: \n{nl}{nl.join(actual)}"
52+
)
53+
54+
55+
if __name__ == "__main__":
56+
grader = Autograder()
57+
grader.add_part(
58+
"Part 1: operator<<",
59+
lambda: verify_output(
60+
"insertion_operator",
61+
[
62+
"User(name=Alice, friends=[])",
63+
"User(name=Bob, friends=[Alice])",
64+
"User(name=Charlie, friends=[Alice, Bob])",
65+
],
66+
),
67+
)
68+
69+
grader.add_part("Part 2: Destructor", lambda: verify_output("destructor", []))
70+
71+
grader.add_part(
72+
"Part 2: Copy Constructor",
73+
lambda: verify_output(
74+
"copy_constructor",
75+
[
76+
"User(name=Alice, friends=[F, F, F, F, F, F, F, F, F])",
77+
"User(name=Alice, friends=[Bob, Alice, Charlie, F, F, F, F, F, F])",
78+
],
79+
),
80+
)
81+
82+
grader.add_part(
83+
"Part 2: Copy Assignment Operator",
84+
lambda: verify_output(
85+
"copy_assignment",
86+
[
87+
"User(name=Bob, friends=[BF, BF, BF, BF, BF, BF, BF, BF, BF])",
88+
"User(name=Bob, friends=[BF, BF, BF, BF, BF, BF, BF, BF, BF])",
89+
],
90+
),
91+
)
92+
93+
grader.add_part(
94+
"Part 2: Move Constructor", lambda: verify_output("move_constructor", [])
95+
)
96+
grader.add_part(
97+
"Part 2: Move Assignment Operator", lambda: verify_output("move_assignment", [])
98+
)
99+
100+
grader.add_part(
101+
"Part 3: operator+=",
102+
lambda: verify_output(
103+
"compound_assignment",
104+
["User(name=Alice, friends=[Bob])", "User(name=Bob, friends=[Alice])"],
105+
),
106+
)
107+
108+
grader.add_part(
109+
"Part 3: operator<",
110+
lambda: verify_output(
111+
"comparable",
112+
[
113+
"A < B is true",
114+
"B < A is false",
115+
"B < C is true",
116+
"C < B is false",
117+
"C < A is false",
118+
"A < C is true",
119+
],
120+
),
121+
)
122+
123+
grader.run(),

0 commit comments

Comments
 (0)