I have to analyze this C++ code which involves exceptions, but I’m not used to analyzing what code is supposed to do. There’s several things about it I don’t understand. Additionally, I don’t have experience with a buffer data structure or advanced details of throwing exceptions in C++.
What is the max size of the data structure?
- That is to say, if a Seq_Buffer is made with size_ = 100, how many elements can be stored?
- My intuition tells me that if you create something with size 100, it’s max size will be 100, but I can’t be sure with facts.
Constructors can throw exceptions in c++?
- Can the constructor for Seq_Buffer throw an exception?
- Assuming this is true, why does that work without explicating stating a try, catch, or throw block? I thought that was the only way to get exceptions
I’ve tried searching for these two questions, but I’m really quite lost. Thank you for taking the time to read.I’ve tried searching for these two questions, but I’m really quite lost. Thank you for taking the time to read.
The code for the Seq_Buffer class with its constructor is below:
#ifndef SEQBUFFER_H
#define SEQBUFFER_H
#include <memory>
#include <experimental/optional>
#include "optional.h"
// Exceptions classes
struct full_buffer {};
struct empty_buffer {};
// Sequential buffer for values of type T
template <typename T>
class seq_buffer {
public:
// Constructs a buffer for n elements
seq_buffer(int n) :
size_{n},
buf_{new item_type[size_]}
{
}
// Default destructor
~seq_buffer() = default;
// Size of buffer
int size() const noexcept {
return size_;
}
// Is buffer empty?
bool empty() const noexcept {
return next_read_ == next_write_;
}
// Is buffer full?
bool full() const noexcept {
const int next = next_position(next_write_);
return next == next_read_;
}
// Put element x into buffer with marker last.
// An empty element signals end of buffer.
void put(const optional<T> & x);
// Gets a pair with next element and last indication.
// Pair is accessed through members first and second
optional<T> get();
private:
// Compute next position after p following circular order
int next_position(int p) const noexcept {
return p + ((p+1>=size_)?(1-size_):1);
}
private:
// Size of buffer
const int size_;
using item_type = optional<T>;
// Unique pointer to buffer of size_ elements.
std::unique_ptr<item_type[]> buf_;
// Next position to read
int next_read_ = 0;
// Next position to write
int next_write_ = 0;
};
template <typename T>
void seq_buffer<T>::put(const optional<T> & x)
{
const int next = next_position(next_write_);
if (next == next_read_) throw full_buffer{};
if (!x) {
buf_[next_write_] = {};
}
else {
buf_[next_write_] = *x;
}
next_write_ = next;
}
template <typename T>
optional<T> seq_buffer<T>::get()
{
if (empty()) throw empty_buffer{};
auto res = buf_[next_read_];
next_read_ = next_position(next_read_);
return res;
}
#endif
newwhich can throwbad_allocif out of memory. It also constructs objects of the unknown typeitem_type. Its constructor could possibly also throw an exception.auto a = b;andcout << (a + b);. (Two examples, three opportunities to throw, can you spot them all?) There's really no way around that, short of disabling exception support via compiler flags.