| CSE 701 Practice Exam 2: Chapters 4-7 McMaster University, Fall 2025 |
Notes about the practice exam This practice exam is in the same format, length, and difficulty level as the real exam, and covers the same material. You are prepared for the real exam if you can solve all the questions in this practice exam on your own, with no help from anyone else, human or AI. Tips for effective AI usage: If you just give the exam questions to an LLM and copy its answers, you will never learn how to solve questions yourself, and you will not be prepared for the actual exam. However, once you solved the practice exam on your own, you can ask an LLM to check your answers and give you feedback, or even generate additional practice exams for you in a similar format. |
Instructions You have 1.5 hours to solve the exam. There are 7 parts in total, and the points sum up to 100. The only allowed material during the exam is the course lecture notes, in printed form. You are not allowed to write anything on the printed notes or modify them in any way before or after printing, but you can use a highlighter. Computers, phones, tablets, smart watches, smart glasses, and other digital devices cannot be used during the exam. Any students found using a digital device of any kind during the exam will be charged with academic dishonesty. In this exam, you will write a C++ program that defines and tests a class according to the specifications given below. The program will be written in multiple parts. When all the parts are combined, they must form a complete and correct program, with all the header files, main() function, and so on, such that if you enter it as-is into VS Code, it will compile and run successfully without any errors or warnings (with all the warning flags recommended in this course enabled) and produce the expected output. In addition to ensuring correct C++ syntax, you must also make sure to closely follow all the programming guidelines and best practices we learned in class, as outlined in the lecture notes - especially those inside the colored warning boxes. |
Introduction You are going to write a C++ class rectangle, which represents a rectangle. The class has three constructors: - Constructor with no arguments: Constructs a degenerate rectangle with all 4 sides equal to zero.
- Constructor with one argument: Constructs a square with all 4 sides equal to the argument.
- Constructor with two arguments: Constructs a rectangle with 2 horizontal sides equal to the first argument and 2 vertical sides equal to the second argument.
Note that a rectangle has 4 sides, but we only need to store 2 values, since the top and bottom sides are always the same and the left and right sides are always the same. The class has four member functions: get_x(): Gets the horizontal side length. get_y(): Gets the vertical side length. set() (2 overloads): Sets both side lengths to new values. One overload takes a single argument (for squares) and one overload takes two arguments (for rectangles). operator*=(): Scales all sides of the rectangle by a given scalar in place. The class also has one friend function: operator<<(): Inserts the sides of the rectangle into a stream in the format [x, y]. The class has two related external functions: operator*() (2 overloads): Returns a copy of the rectangle scaled by a given scalar, without modifying the rectangle itself. One overload is for multiplication from the right, and the other overload is for multiplication from the left. The class is encapsulated, meaning that the user cannot set the private x and y data members directly. Instead, we require the user to use the constructors and other relevant functions, and ensure that all operations maintain the following class invariants: - None of the sides can be negative.
- If one side is zero, the other must also be zero (i.e. there is exactly one degenerate rectangle).
If any of the invariants are not satisfied, the relevant functions should throw one of the following three custom exceptions: degenerate_rectangle: Thrown when attempting to create or set a rectangle to a degenerate state where one side is zero and the other is non-zero, with the error message "In a degenerate rectangle, both sides must be zero!". negative_scalar: Thrown when attempting to scale a rectangle by a negative scalar, with the error message "Scalar cannot be negative!". negative_sides: Thrown when attempting to create or set a rectangle with a negative side length, with the error message "Sides cannot be negative!". Important: The code written in the parts below should form a complete program; each part should be written under the previous part. Do not write the solutions to the individual parts in separate places; the exam is divided into parts only to guide you through writing the complete program. There is no need to split the class into separate files; just write a single .cpp file containing the class and the main() function. |
Part 1: Class prototype (15 points) Write down the complete class prototype for the rectangle class, including all data members, constructors, member functions, friend functions, and related external functions as described above. Your class prototype should look like this: // Relevant header files
using namespace std;
class rectangle
{
public:
// Public member function and friend function declarations
private:
// Private data members
};
// Related external function declarations
Do not write the contents of any functions yet; just write their declarations, including parameter types and return types. All the functions will be defined separately outside the class prototype, not inline. |
Part 2: Custom exceptions (15 points) Under the class prototype, define the three exceptions degenerate_rectangle, negative_scalar, and negative_sides. They should be derived from invalid_argument with appropriate constructors that include the relevant error messages. |
Part 3: Constructors and setter functions (15 points) Under the custom exceptions, write down the code for the three constructors and the two set() member functions, ensuring that the class invariants are enforced. The user should not be able to construct or modify a rectangle with any invalid input without generating the appropriate exceptions. |
Part 4: Getter functions (10 points) Under the constructors and setter functions, write down the get_x() and get_y() member functions. |
Part 5: Insertion operator (10 points) Under the getter functions, write down the operator<<() friend function. |
Part 6: Scaling operators (15 points) Under the insertion operator, write down the operator*=() member function and the two external operator*() functions, ensuring that the class invariants are enforced. The user should not be able to scale a rectangle with any invalid input without generating the appropriate exceptions. |
Part 7: Test program (20 points) Under the scaling operators, write down the main() function of the program, which will test and demonstrate the use of your new class. Your test program should do the following: - Create a degenerate rectangle
A using the default constructor and print it out. - Create a square
B using the second constructor with all sides of length 2 and print it out. - Create a rectangle
C using the third constructor with sides of length 3 and 4 and print it out. - Attempt to create a rectangle
D with sides of length -5 (negative) and 6. This should be inside a try-catch block and any thrown exception must be caught and printed out. - Attempt to create a rectangle
E with sides of length 7 and 0. This should be inside a try-catch block and any thrown exception must be caught and printed out. - Change all the sides of
C to 5 using set() (the overload with one argument) and print it out. - Change the sides of
C to 8 and 9 using set(). - Get the side lengths of
C using get_x() and get_y() and print them out individually in the format x: <value>, y: <value>. - Scale
C by 10 using operator*=() and print it out. - Attempt to scale
C by -11 (negative) using operator*=(). This should be inside a try-catch block and any thrown exception must be caught and printed out. - Print out
C * 12 and 13 * C. The output of your program must be exactly as follows: [0, 0]
[2, 2]
[3, 4]
Error: Sides cannot be negative!
Error: In a degenerate rectangle, both sides must be zero!
[5, 5]
x: 8, y: 9
[80, 90]
Error: Scalar cannot be negative!
[960, 1080]
[1040, 1170]
|
Note for the practice test The real test will be very similar to the practice test. You will also be asked to create a class according to specified parameters and a test program. It is highly recommended to solve the practice exam on paper, so you can get used to writing code on paper. However, once you are done you should copy your code to a computer and make sure it compiles and the test program produces the expected output. Enable all warning flags recommended in this course, and the clang-tidy and cppcheck linters, and ensure that there are no warnings or errors in your code. |
Guidelines Here are some important C++ programming guidelines to follow when solving this practice exam and the real exam: - Always use
const for any variable or function argument that should not be modified, and for any member function that does not modify the object. - Use
constexpr for constants known at compile time. - Always use portable integer types with the appropriate signedness and bit width for the task at hand.
- Always use floating-point types with the appropriate range and precision for the task at hand. (In practice, this usually means using
double unless you have a very good reason to use something else.) - Never use uninitialized variables, access array elements out of range, or dereference null or invalid pointers.
- Beware of integer or floating-point overflows, and floating-point rounding errors.
- Beware of implicit type conversions that may lead to unexpected results.
- Avoid global variables.
- Variables should always occupy the smallest possible scope. For example, a
for loop variable should be declared within the for statement itself, not beforehand. - Never shadow variables.
- Pass all objects larger than 64 bits by reference to avoid unnecessary copying.
- Always use references instead of pointers unless you really know what you're doing.
- Never call a function that might throw an exception without handling the exception using a
try-catch block. - Use range-based
for loops (with correct use of const and/or references) instead of C-style for loops wherever appropriate. - Use
array or vector instead of C-style arrays and string instead of C-style strings wherever appropriate. - Always strictly follow the object-oriented programming principles of encapsulation and class invariants. All data members must be private, and only accessed or modified through public member functions that enforce the class invariants.
- Never use C-style
enum types; use enum class instead. - When overloading operators, always ensure that they behave in a way consistent with their usual mathematical meaning and/or programming conventions.
- Always anticipate and handle all possible errors, such as when opening files or processing user input.
- Always generate random numbers by seeding a Mersenne Twister using a random device, then passing it to a distribution.
Please note that these are general guidelines; not all of them apply to this practice exam or to the real exam. This list will not be included in the real exam, but all these guidelines can be found in the lecture notes, which you will have access to during the exam. |
| © 2025 Barak Shoshany |