Problems:
CS100 Homework 5 (Spring 2023)
Download the materials for problem 3 from Piazza.
(April 6) 20:20 Problem 3 and bonus have been updated. All submissions have been rejudged. In case we made any mistakes during this procedure, please re-submit your code. We are sorry for causing any inconvenience.
Please note that we do not allow the use of standard library containers in any form. Direct use of standard library containers will result in compilation error. If you managed to escape our compile-time check, you will still get zero points. We will have our ways to check this.
(April 7) 9:57 Problem 2 has been released. Deadline has been postponed to April 13, 23:59.
(April 8) 14:25 Problem 3 and bonus: Accepted submissions have been rejudged. See Piazza for details.
(April 10) 0:59 Problem 2 bonus added. See Piazza for details.
(April 13) 23:27 In Problem 2, split(str, sep) should return a vector containing one empty string, instead of an empty vector, if str == "". Deadline is postponed to April 14, 23:59. Sorry for not making this clear.
Checklist for C++ Beginners
This problem is to answer questions.
👉 Go to GitHub for the HTML page file
Playing with Strings and Vectors
Implement the following four functions.
1 | std::string strip(const std::string &str); |
-
strip(str)
Returns the string obtained from str by removing all the leading and trailing whitespaces. A character c is a whitespace if std::isspace(c) is true. For example,
1
assert(strip(" wefafwefw \n") == "wefafwefw");
The assertion above should succeed.
-
join(sep, strings)
Concatenate the strings strings, during which the string sep is inserted in between each given string. Then return the result. For example:
1
2std::vector<std::string> strings = {"hello", "world", "cxx23"};
assert(join(", ", strings) == "hello, world, cxx23");The assertion above should succeed.
If the vector
strings
is empty, return an empty string. -
split(str, sep)
Using sep as the delimiter, split the string str into a vector of strings and return that vector. For example,
1
2
3
4std::vector<std::string> ans{"", "aaa", "", "bbb", "cdefg"};
assert(split("xaaaxxbbbxcdefg", "x") == ans);
ans = {"", "x"};
assert(split("xxx", "xx") == ans);The assertions above should succeed. It is guaranteed that
sep
is not an empty string. -
swapcase(str)
Returns the string obtained from
str
with cases swapped, i.e. lowercase letters are changed to their uppercase forms, and uppercase letters are changed to their lowercase forms. Other characters remain unchanged. For example,swapcase("123..abcDEF")
is equal to"123..ABCdef"
.
Submission
Submit your code containing these four functions to OJ.
Dynamic Array
Implement your own version of the dynamic array Dynarray
class. This is almost the same as the example in Lecture 17.
Expected Usage
A Dynarray
represents an "array" of ints, the length of which is determined at run-time and the elements are stored in dynamically allocated memory. A Dynarray
should manage the memory it uses correctly: It should allocate adequate memory when initialized, and deallocate the memory when it is destroyed to avoid memory leaks.
Initialization
Let n
be a non-negative integer and x
be an integer. Let begin
and end
be of type const int *
satisfying begin <= end
, with begin
pointing at the first element of some array and end
pointing at the element after the last element of that array. A Dynarray
can be constructed in the following five possible ways:
-
Dynarray a;
Initializes the object `a` to be an "array" of length `n`, all elements **value-initialized**. **Note**: A constructor with one parameter also defines a **type conversion**. For example, the `std::string` class has a constructor that accepts one argument of type `const char *`, so the implicit conversion from C-style strings to C++ strings is supported. However, we definitely don't want to see the abuse of such constructors:1
2
3Initializes the object `a` to be an empty "array" whose length is `0`. (Default-initialziation)
- ```cpp
Dynarray a(n);or1
Dynarray a = 42;
In order to forbid the use of this constructor as an implicit type conversion, you should add the `explicit` keyword:1
2void fun(Dynarray a);
fun(42); // constructs Dynarray(42) and passes it to `a`1
2
3
4class Dynarray {
// ...
explicit Dynarray(std::size_t) /* ... */
}; -
Dynarray a(n, x);
Initializes the object `a` to be an "array" of length `end - begin`. The elements are obtained from the range `[begin, end)`. For example,1
2
3Initializes the object `a` to be an "array" of length `n`, all elements initialized to be `x`.
- ```cpp
Dynarray a(begin, end);1
2const int arr[10] = {19, 64, 10, 16, 67, 6, 17, 86, 7, 29};
Dynarray a(arr + 3, arr + 7); // a contains the elements {16, 67, 6, 17} -
Dynarray b = a;
1
2
3
4
5
6
7
8
9
10Copy-initialization. See below.
#### Copy control
Let `a` be some existing `Dynarray` object. The following are equivalent ways of initializing a new `Dynarray` object:
```cpp
Dynarray b = a;
Dynarray b(a);
Dynarray b{a};
This is the copy-initialization from a
. Our Dynarray
adopts the value semantics: Such copy-initialization should allocate another block of memory for b
and copy the elements from a
. After that, the data (elements) owned by a
and b
should be independent: Modification to some element in a
should not influence the elements in b
.
If b
is also an existing Dynarray
object, the following assignment can be performed:
1 | b = a; |
This is the copy-assignment from a
. After that, b
should be a copy of a
. Any modification to an element of a
should not influence the elements in b
.
Your copy-assignment operator must be self-assignment safe.
Basic information
Let a
be an object of type const Dynarray
. The following operations should be supported:
-
a.size()
Returns a `bool` value indicating whether the `Dynarray` is empty or not. A `Dynarray` is said to be *empty* if its length is zero.1
2
3Returns the length of the "array", that is, the number of elements in `a`. It should be of type `std::size_t`, which is defined in `<cstddef>`.
- ```cpp
a.empty()
Element access
Let a
be an object of type Dynarray
and ca
be an object of type const Dynarray
. Let n
be a non-negative integer. The following operations should be supported:
-
a.at(n)
1
2
3
4Returns a **reference** to the element indexed `n` in `a`. It is both readable and modifiable since `a` is not `const`. For example:
```cpp
a.at(n) = 42;
std::cout << a.at(n) << std::endl; -
ca.at(n)
1
2
3
4Returns a **reference-to-`const`** to the element indexed `n` in `ca`. It should be read-only, since `ca` is `const`. For example:
```cpp
std::cout << ca.at(n) << std::endl; // OK
ca.at(n) = 42; // This should lead to a compile-error.
Moreover, to keep in consistent with the behaviors of the standard library containers, Dynarray::at
should do bounds-checking. If n
is not in the range [0, a.size())
, you need to throw an exception std::out_of_range
. To throw this exception, write
1 | throw std::out_of_range{"Dynarray index out of range!"}; |
The exception class std::out_of_range
is defined in the standard library file <stdexcept>
.
Examples
Write your code in dynarray.hpp
. We have provided a template for you to begin with, although it contains only some preprocessor directives.
We have also provided a compile_test.cpp
for you which contains the compile-time checks of your implementation. Members missing, const
qualifier missing, or incorrect return types will be detected. You can also try concept_test.cpp
, which uses C++20 concepts and is more readable with less magic code.
Here is a sample usage of the Dynarray
:
1 |
|
Input:
1 | 5 |
Output:
1 | [5, 4, 3, 2, 1] |
Submission
Submit the contents of your dynarray.hpp
to the OJ.
Self-test on OJ
Input an integer to run the -th testcase.
Bonus
If your copy-assignment operator is exception-safe, or more specifically, offers strong exception safety guarantee, you can get the extra 10 points. Submit it to Problem 504 on OJ.
Related materials: C++ Primer (5th edition) section 13.2.1, section 13.3; Effective C++ (3rd edition) Item 29.