Professional Documents
Culture Documents
C++ 2024H1 Assignment-12
C++ 2024H1 Assignment-12
C++ 2024H1 Assignment-12
Total Marks : 25
April 4, 2024
Question 1
Which of the following smart pointers has/have shared ownership policy? [MSQ, Marks 2]
c) std::unique ptr(C++11)
d) std::shared ptr(C++11)
Answer: b), d)
Explanation:
std::auto ptr (C++03) and std::unique ptr (C++11) follow exclusive ownership, whereas
std::shared ptr and std::weak ptr (C++11) follow shared ownership.
1
Question 2
Consider the program (in C++11) given below. [MCQ, Marks 2]
#include <iostream>
#include <functional>
a) 1
b) -4
c) -5
d) -13
Answer: a)
Explanation:
The call f(1, 2, 3) results in binding 1 to 1, 3 to 3, and 2 remains unused. The formal
arguments to the call to print are as print(1, 5, 3, 10), which is evaluated as 1.
2
Question 3
Consider the program (in C++11) given below that squares each element of vector vec and
sums all the squared elements. [MSQ, Marks 2]
#include <iostream>
#include <thread>
#include <vector>
#include <functional>
struct summation {
summation(std::vector<int>& iv, int* sum) : iv_{iv}, sum_{sum} { }
void operator()() {
for(auto& i : iv_) {
i *= i;
*sum_ += i;
}
}
std::vector<int>& iv_;
int* sum_;
};
int main() {
std::vector<int> vec { 1, 2, 3, 4 };
int res = 0;
std::thread t { ______________________ }; //LINE-1
t.join();
for(auto i : vec)
std::cout << i << " ";
std::cout << res << " ";
return 0;
}
Fill in the blank at LINE-1 with appropriate option(s) such that the output becomes 1 4 9
16 30 .
a) summation(vec, res)
b) summation(vec, &res)
c) summation(std::ref(vec), &res)
Answer: b), c)
Explanation:
The constructor of summation receives vec as pass-by-reference and res as pass-by-address.
At option a), the actual parameter res is not an address. Therefore, it is a wrong option.
At option b), the actual parameter vec is a correct way to perform pass-by-reference (or pass-
by-value) and &res is passed-by-address. Therefore, it is a correct option.
At option c), the actual parameter std::ref(vec) is a pass-by-reference explicitly and &res
is passed-by-address. Therefore, it is a correct option.
At option d), the actual parameter summation of std::bind function is a call to the default
constructor of functor summation, which is not provided. Therefore, it is a wrong option.
3
Question 4
Consider the program (in C++11) given below. [MCQ, Marks 2]
#include <iostream>
class myclass{
public:
void print(){ }
};
int main() {
SmartPointer<myclass> ptr(new myclass);
myclass* mc = new myclass;
*ptr = *mc;
ptr->print();
return 0;
}
Answer: b)
Explanation:
The statement SmartPointer<myclass> ptr(new myclass); calls the constructor of myclass
first and then calls the constructor of SmartPointer, which print SmartPtr::ctor(),.
The statement *ptr = *mc; calls the function operator*() from SmartPointer, which prints
SmartPtr::operator*(),.
4
Finally, The statement ptr->print(); calls the function operator->() from SmartPointer,
which prints SmartPtr::operator->(),.
5
Question 5
Consider the following code segment (in C++11). [MCQ, Marks 2]
#include<iostream>
#include <memory>
int main(){
std::shared_ptr<char> p1(new char(’a’));
{
std::shared_ptr<char> p2(p1);
std::cout << "rc = " << p1.use_count() << " ";
}
std::shared_ptr<char> p3 = p1;
std::cout << "rc = " << p1.use_count() << " ";
fun(p1);
std::cout << "rc = " << p1.use_count() << " ";
p3.reset(new char(’A’));
std::cout << "rc = " << p1.use_count() << " ";
return 0;
}
a) rc = 2 rc = 3 rc = 4 rc = 4 rc = 3
b) rc = 2 rc = 3 rc = 4 rc = 3 rc = 2
c) rc = 2 rc = 2 rc = 4 rc = 2 rc = 1
d) rc = 2 rc = 2 rc = 3 rc = 2 rc = 1
Answer: c)
Explanation:
The code is explained in the comment:
#include<iostream>
#include <memory>
int main(){
std::shared_ptr<char> p1(new char(’a’)); //rc = 1
{
std::shared_ptr<char> p2(p1); //rc = 2
std::cout << "rc = " << p1.use_count() << " "; //rc = 2
} //rc = 1 since the pointer defined
6
std::shared_ptr<char> p3 = p1; //rc = 2
std::cout << "rc = " << p1.use_count() << " "; //rc = 2
fun(p1);
std::cout << "rc = " << p1.use_count() << " "; //rc = 2 since local pointers
//in fun are deleted
p3.reset(new char(’A’)); //rc = 1
std::cout << "rc = " << p1.use_count() << " "; //rc = 1
return 0;
}}
7
Question 6
Consider the code segment (C++11) given below. [MCQ, Marks 2]
#include <iostream>
#include <memory>
int main() {
auto p1 = std::make_shared<char>(’a’);
auto p2 = p1;
std::weak_ptr<char> wp1 = p1;
std::weak_ptr<char> wp2 = p1;
p1.reset(new char(’A’));
if(auto p = wp1.lock())
std::cout << *p << std::endl;
else
std::cout << "wp1 is expired" << std::endl;
update(p2);
if(auto p = wp2.lock())
std::cout << *p << std::endl;
else
std::cout << "wp2 is expired" << std::endl;
return 0;
}
a) A B
b) wp1 is expired
wp2 is expired
c) A
wp2 is expired
d) a
wp2 is expired
Answer: d)
Explanation:
The weak ptr<char> wp1 points to an object, which initially was referred by shared ptr p1
and p2. Although, p1 is reset, p2 still refer the object. Thus, wp1 also refer to the same object
storing ’a’ and the first output is ’a’.
The call update(p2 passed p2 as pass-by-reference, which is reset within update. Thus, wp2
refers to nothing and the second output is wp2 is expired.
8
Question 7
Consider the code segment (C++11) given below. [MSQ, Marks 2]
#include <iostream>
#include <vector>
#include <thread>
int main(){
std::vector<int> vi;
std::thread t {&add, std::ref(vi)};
vi.push_back(3);
t.join();
for(auto i : vi)
std::cout << i << " ";
return 0;
}
a) 3 0 1
b) 0 1
c) 0 3
d) 0 3 1
Answer: a), d)
Explanation:
Since the vector will be printed after function add() and main() add all the values to the
vector, the vector must contain all the values 0, 1, 2. However, they can be added in any
possible order. Therefore, option a) and d) are correct.
9
Question 8
Consider the following code segment (in C++11). [MSQ, Marks 2]
#include <iostream>
#include <thread>
#include <mutex>
struct printer{
int no_papers = 0;
};
struct scanner{
int no_papers = 0;
};
std::mutex mtx_ptr;
std::mutex mtx_snr;
static printer pr;
static scanner sc;
void spooler1(int n) {
std::unique_lock<std::mutex> lck1(mtx_ptr);
std::unique_lock<std::mutex> lck2(mtx_snr);
pr.no_papers += n;
sc.no_papers += n;
std::cout << pr.no_papers << ", " << sc.no_papers << std::endl;
}
void spooler2(int n) {
std::unique_lock<std::mutex> lck1(mtx_snr);
std::unique_lock<std::mutex> lck2(mtx_ptr);
pr.no_papers += n;
sc.no_papers += n;
std::cout << pr.no_papers << ", " << sc.no_papers << std::endl;
}
int main(){
std::thread t1{spooler1, 5};
std::thread t2{spooler2, 8};
t1.join();
t2.join();
return 0;
}
Which of the following cannot be TRUE about the output of the above program?
a) It generates output as:
5, 5
13, 13
10
c) It generates output as:
8, 8
5, 5
d) It results in deadlock
Answer: c)
Explanation:
Since the code in spooler1 and spooler2 execute in a mutual exclusive manner, the output
can be:
5, 5
13, 13
or
8, 8
13, 13
It may also happen that t1 holds lock on mtx ptr, and t2 holds lock on mtx snr. Then, t1
request to lock on mtx snr, and t2 requests lock on mtx ptr. It results in a deadlock.
However, option c) can not be an output.
Intentionally kept as MSQ
11
Question 9
Consider the code segment (C++11) given below. [MSQ, Marks 2]
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx_rs;
struct resource{ int no_of_instances {10}; };
int main(){
resource r;
std::thread t1 {consume, std::ref(r), "X", 3};
std::thread t2 {consume, std::ref(r), "Y", 4};
std::thread t3 {consume, std::ref(r), "Z", 5};
t1.join(); t2.join(); t3.join();
return 0;
}}
What is/are the output/s that is/are not possible? (think logically)
b) consumtion by Z : failed
consumtion by X : instances available = 2
consumtion by Y : failed
Answer: b), d)
Explanation: Since the entire code of consume is executed after locking mutex, the code is
executed in a mutual exclusive manner. Thus, option b) and d) cannot occur.
12
Programming Questions
Question 1
Consider the following program (in C++14).
• Fill in the blank at LINE-1 with an appropriate definition of mutex mtx rs.
• Fill in the blanks at LINE-2 and LINE-3 with appropriate statements such that all the
modification to temp and nos of instances happen in a mutual exclusive manner.
The program must satisfy the sample input and output. Marks: 3
#include <iostream>
#include <thread>
#include <functional>
#include <chrono>
#include <mutex>
_______________________________; //LINE-1
class resource{
public:
resource() : nos_of_instances_{0} {}
void produce(int n){
_______________________________________; //LINE-2
temp_ = n;
int delay = (int)((double)std::rand() / (double)(RAND_MAX)* 10);
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
nos_of_instances_ += temp_;
}
void consume(int n){
______________________________________; //LINE-3
temp_ = n;
int delay = (int)((double)std::rand() / (double)(RAND_MAX)* 20);
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
nos_of_instances_ -= temp_;
}
int get_nos_of_instances(){
return nos_of_instances_;
}
private:
int nos_of_instances_;
int temp_;
};
13
for(int i = n - 1; i >= 0; i--){
int delay = (int)((double)std::rand() / (double)(RAND_MAX)* 15);
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
r.consume(i + 1);
}
}
int main(){
int n, m;
std::cin >> n;
std::cin >> m;
resource r;
std::thread t1{ std::bind(produce, std::ref(r), n) };
std::thread t2{ std::bind(consume, std::ref(r), m) };
t1.join();
t2.join();
std::cout << r.get_nos_of_instances();
return 0;
}
Public 1
Input: 200 200
Output: 0
Public 2
Input: 200 100
Output: 15050
Public 3
Input: 200 300
Output: -25050
Private
Input: 300 300
Output: 0
Answer:
LINE-1: std::mutex mtx rs
LINE-2: std::unique lock<std::mutex> lck(mtx rs)
LINE-3: std::unique lock<std::mutex> lck(mtx rs)
Explanation:
At LINE-1, mutex can be defined as:
std::mutex mtx rs
At LINE-2 and LINE-3 we can use the following statement:
std::unique lock<std::mutex> lck(mtx rs) such that all the modification to temp and
nos of instances happen in a mutual exclusive manner.
14
Question 2
Consider the following program (in C++11) that computes nth prime.
• Fill in the blank at LINE-1 with appropriate header to overload function operator.
• Fill in the blank at LINE-2 to invoke the functor prime numbers asynchronously.
• Fill in the blank at LINE-3 to receive the output from functor prime numbers.
The program must satisfy the sample input and output. Marks: 3
#include <iostream>
#include <future>
#include <cmath>
struct prime_numbers{
const long long n_;
prime_numbers(const long long& n) : n_(n) { }
bool is_prime(long long m){
if (m <= 1)
return false;
for (int i = 2; i <= sqrt(m); i++)
if (m % i == 0)
return false;
return true;
}
______________________________________ { //LINE-1
long long num = 2;
for(int i = 0; i < n_; num++) {
if (is_prime(num)) {
++i;
}
}
return num - 1;
}
};
int main() {
int n;
std::cin >> n;
std::cout << get_nth_prime(n);
return 0;
}
Public 1
Input: 150
Output: 863
15
Public 2
Input: 200
Output: 1223
Private
Input: 1000
Output: 7919
Answer:
LINE-1: long long operator()()
LINE-2: std::async(prime numbers(n))
LINE-3: a.get()
Explanation:
The function header at LINE-1 to overload the function operator can be: long long operator()()
At LINE-2 the asynchronous call to the functor NthPrime() can be made as:
auto a = std::async(prime numbers(n));
At LINE-3 can use the statement: a.get() to receive the result of functor prime numbers.
16
Question 3
Consider the following program in C++11 to represent a doubly linked list, which allows adding
items at the front of the list. Complete the as per the instructions given below:
• Fill in the blank at LINE-1 with appropriate statement so that class node declares dlist
as its friend.
• Fill in the blank at LINE-2 with appropriate statements to traverse the list in forward
direction,
• Fill in the blank at LINE-3 with appropriate statements to traverse the list in backward
direction,
#include<iostream>
#include<memory>
class node;
class dlist{
public:
dlist() = default;
void insert(const int& item);
void forward_traverse();
void backward_traverse();
private:
std::shared_ptr<node> first = nullptr;
std::shared_ptr<node> last = nullptr;
};
class node{
public:
node() = delete;
node(int info) : info_{info}, next{nullptr} {}
_________________________________; //LINE-1
private:
int info_;
std::shared_ptr<node> next;
std::weak_ptr<node> prev;
};
void dlist::forward_traverse(){
for(____________________________________) //LINE-2
std::cout << t->info_ << " ";
}
void dlist::backward_traverse(){
for(____________________________________) //LINE-3
std::cout << p->info_ << " ";
}
void dlist::insert(const int& item){
std::shared_ptr<node> n = std::make_shared<node>(item);
if(first == nullptr){
first = n;
last = first;
17
}
else{
n->next = first;
first->prev = n;
first = n;
}
}
int main(){
dlist dl;
int n, m;
std::cin >> n;
for(int i = 0; i < n; i++){
std::cin >> m;
dl.insert(m);
}
dl.forward_traverse();
std::cout << std::endl;
dl.backward_traverse();
return 0;
}
Public 1
Input:
4
10 20 30 40
Output:
40 30 20 10
10 20 30 40
Public 2
Input:
5
1 2 3 4 5
Output:
5 4 3 2 1
1 2 3 4 5
Private
Input:
6
10 9 8 7 6 5
Output:
5 6 7 8 9 10
10 9 8 7 6 5
Answer:
LINE-1: friend dlist
LINE-2: std::shared ptr<node> t = first; t != nullptr; t = t->next
LINE-3: std::weak ptr<node> t = last; auto p = t.lock(); t = p->prev
18
Explanation:
At LINE-1, we can fill in the blank with friend dlist so that dlist becomes friend of node.
The given program uses smart pointers instead of raw pointers to avoid explicit memory
management. In case of raw pointers, if the allocated memories are not freed as required,
it can result in memory leakage.
Furthermore, in the node class if next and prev both are shared ptr<node> type, it will cause
circular references, which also results in memory leakage. Thus, to avoid the problem of circular
references, next is declared as of type shared ptr<node> and prev as of type weak ptr<node>.
Since in class node, next is a shared ptr, the for loop for forward traversal must be:
for(std::shared ptr<node> t = first; t != nullptr; t = t->next)
Since in class node, prev is a weak ptr, the for loop for reverse traversal must be:
for(std::weak ptr<node> t = last; auto p = t.lock(); t = p->prev)
Note that last is a shared ptr while prev is a weak ptr. So we need to devise a way to
navigate between the two. Recall that weak ptr cannot be used to access the pointee. So
we first get weak ptr t from last. Now for access, we get a shared ptr p by locking the
weak ptr t. This will be used in the loop body. Finally, to progress backward, we get p->prev
and keep in the weak ptr t.
19