COMP6771 - Advanced C++ Programming
Weekly Exercise - Week 2
With my own solution (might be incorrect)
Answer the following questions
cppint get_age(); // (*)
int main() {
}
cppint get_age();
int age = get_age(); // (*)
cppint main() {
auto age = 20; // (*)
}
cppint main() {
auto age = 20;
std::cout << age << std::endl; // (*)
}
cppint get_age();
int get_age() { // (*)
return 6771;
}
Answer the following questions
cpp/* 1 */ auto put(char) -> void;
/* 2 */ auto put(int) -> void;
/* 3 */ auto put(const char) -> void;
/* 4 */ auto put(char &) -> void;
put('a');
Which overload of put would be selected and why?
put was called with a char and overload 3 is just a redeclaration of overload 1.char is implicitly promotable to int and so overload 2 is the best match.put was called with a temporary const char.put was called with a temporary char and temporaries preferentially bind to references.cpp/* 1 */ auto put(char) -> void;
/* 2 */ auto put(char &) -> void;
/* 3 */ auto put(const char &) -> void;
char c = 'a';
put(c);
Which overload of put would be selected and why?
put was called with a char.put was called with a mutable char and and references have higher priority.put was called with a const char and const references have higher priority.cpp/* 1 */ auto memcpy(char *dst, const char *src, int n = 0) -> void *;
/* 2 */ auto memcpy(const char *dst, char * const src) -> char *;
char *dst = /* appropriate initialisation... */;
const char *src = /* appropriate initialisation... */;
void *ptr = memcpy(dst, src);
Which overload of memcpy would be selected and why?
ptr's type bettersrc argument has higher priority than the corresponding bottom-level const src in overload 1.cpp/* 1 */ auto min(int(&arr)[2]) -> int;
/* 2 */ auto min(int *arr) -> int;
/* 3 */ auto min(int(&arr)[]) -> int;
auto fn(int buf[3]) -> void {
min(buf);
}
Which overload of min would be selected and why?
min was called with an array of length 3, 3 is close to 2, so this is the best match.buf argument decays to int * and so overload 2 is the best match.int(&)[2] nor int * match int(&)[3] perfectly but a reference to an array of unknown length does, so this is the best match.cpp/* 1 */ auto sink(int i, ...);
/* 2 */ auto sink(int i, short s);
/* 3 */ auto sink(...);
auto L = std::numeric_limits<long>::max();
sink(1, L);
Which overload of sink would be selected and why?
long to shortAnswer the following questions
cpp// api.h
int rand();
// me.cpp
#include "api.h"
int rand() {
return 42;
}
// you.cpp
int rand() {
return 6771;
}
// client.cpp
#include "api.h"
int i = rand();
you.cpp did not include api.hrand().rand().cppnamespace constants {
#define N 6771
}
int N = constants::N;
int main() {
int ints[N] = {1, 2, 3};
}
constants is a bad name for a namespaceint N is changed to int 6771.N is not const and so cannot be used in ints[N].cpp#include <vector>
int main() {
std::vector<int> v;
unsigned i;
while (i-- > 0) {
v.push_back(i);
}
}
i is just a variable declaration and the real i hasn't been defined yet.i is uninitialised and so its use is illegal.v is not used after the for-loop.cppint main() {
int *ptr = new int{42};
*ptr = 6771;
return *ptr;
}
main().new can fail allocation and throws an exception if that happensint{42} is invalid syntax.ptr was nullptr or not before dereferencing.C++ has a poignant emphasis on strong type-safety. To that end it offers a type-safe version of the C-style cast called static_cast which only allows conversion between compatible types e.g. int to float, void * to int * etc., and will not allow unrelated casts e.g. void * to int.
In this exercise we will use static_cast to safely scale a vector of integers by a double value and return a new vector of doubles.
In src/2.4/scale.h there is documentation for scale() which does this conversion.
Implement this function in src/2.4/scale.cpp.
You will also need to write at least two tests for it in src/2.4/scale.test.cpp.
To improve ease of use, also add a default value of 0.5 for the scaling factor. This will allow users to not have to pass a scale factor when commonly scaling a vector in half.
C++// scale.h
auto scale(std::vector<int>& ivec, double factor=0.5) -> std::vector<double>;
// scale.cpp
auto scale(std::vector<int>& ivec, double factor) -> std::vector<double> {
std::vector<double> dvec(ivec.size());
for (size_t i = 0; i < ivec.size(); i++) {
dvec[i] = ivec[i] * factor;
}
return dvec;
}
The Standard Template Library (aka the STL, now part of the C++ standard proper) has three fundamental concepts: containers, iterators, and algorithms. Iterators are the glue that sits between containers of data and the algorithms that operate on them. By using these three concepts together, code reuse is maximised and composition of existing code becomes very easy.
In this exercise you will be using std::vector<int>::iterator to implement a less general version of std::mismatch, one of the many algorithms provided by the standard library.
There is documentation for our version of mismatch() in src/2.5/mismatch.cpp. Complete this function and write at least three more tests to verify your code is correct. Two have already been provided for you.
C++auto mismatch(std::vector<int>& v1, std::vector<int>& v2) -> std::pair<iter, iter> {
auto v1_iter = v1.begin();
auto v2_iter = v2.begin();
while(v1_iter != v1.end() and v2_iter != v2.end()) {
if (*v1_iter != *v2_iter) {
return std::make_pair(v1_iter, v2_iter);
}
++v1_iter;
++v2_iter;
}
return std::make_pair(v1_iter, v2_iter);
}
The purpose of this exercise is to get experience using different parts of the C++ Standard Library's helpful types and data structures. In src/permutation.h, there is documentation for a function that, given two strings, determines if one string is a permutation of the other. In this instance, a string s1 is a permuation of another string s2 if:
You need to implement this function in src/2.6/permutation.cpp and write at least two tests in src/2.6/permutation.test.cpp.
C++auto is_permutation(const std::string& x, const std::string& y) -> bool
{
if (x.empty() and y.empty()) {
return true;
}
if (x.size() != y.size()) {
return false;
}
std::map<char, int> letter_map_x;
std::map<char, int> letter_map_y;
for (const auto& c : x) {
++letter_map_x[c];
}
for (const auto& c : y) {
++letter_map_y[c];
}
for (const auto& el : letter_map_x) {
if (el.second != letter_map_y[el.first]) {
return false;
}
}
return true;
}
The C++ Standard Library provides many algorithms, one very widely-used one being std::sort. std::sort accepts two iterators denoting a range and performs an optimised sort on that range.
In this exercise, we shall explore what requirements std::sort expects this pair of iterators to satisfy and how the sequence containers std::vector, std::list, and std::array satisfy these requirements.
In src/2.7/assortment.cpp there are 3 overloads for a function sort() which accepts a vector, array, and list of integers. There are also three test cases in src/2.7/assortment.test.cpp for sanity checking.
Try implementing each sort() function using std::sort.
You may notice that the program will not compile.
Consider these questions:
std::vector is always handy...)Modify your implementation such that now the tests pass.
C++#include "assortment.h"
#include <algorithm>
auto sort(std::vector<int>& ivec) -> void
{
std::sort(ivec.begin(), ivec.end());
}
auto sort(std::array<int, 4>& iarr) -> void
{
std::sort(iarr.begin(), iarr.end());
}
auto sort(std::list<int>& ilist) -> void
{
ilist.sort();
}
C++ builds upon many constructs found in C. For example:
std::function (and more) over raw function pointersenum class over enumstd::optional over "magic" invalid values such as nullptr for an absent T*In this exercise we shall implement a paint-mixing algorithm to gain familiarity with these new constructs. Namely:
enum class called paint.sizeof(<any paint>) == 1.std::function.std::optional.In src/2.8/mixing_paint.h, there is documentation for a function mix that accepts a mixing strategy and vector of paints and mixes them according to the given strategy.
Mixing follows the left-fold algorithm, for example, if we were summing integers...:
cppauto add(int x, int y) -> int { return x + y; }
auto sum(const std::vector<int> nums, int init) -> int {
for (auto i : nums) {
init = add(init, i);
}
return init;
}
Here, the accumulator is on the left of the add() function and the next element in the list is on the right.
There is also documentation for a default mixing strategy wacky_colour which you will also need to implement.
Complete these functions in src/2.8/mixer.cpp and write at least two tests for mixer and two tests for wacky_colour in src/2.8/mixer.test.cpp.
C++auto wacky_colour(paint p1, paint p2) -> std::optional<paint>
{
if (p1 == red and p2 == green) {
return yellow;
}
if (p1 == red and p2 == blue) {
return magenta;
}
if (p1 == green and p2 == blue) {
return cyan;
}
if (p1 == yellow and p2 == magenta) {
return brown;
}
if (p1 == cyan and p2 == magenta) {
return brown;
}
if (p1 == brown and p2 == brown) {
return brown;
}
return std::nullopt;
}
auto mix(const std::vector<paint>& paints, std::function<std::optional<paint>(paint, paint)> fn) -> std::optional<paint>
{
std::optional<paint> result = paints[0];
for (size_t i = 1; i < paints.size(); i++) {
result = fn(result.value(), paints[i]);
if (not result) {
return std::nullopt;
}
}
return result;
}
本文作者:Jeff Wu
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!