Beispiellösung
Content |
#ifndef ASSERT_HPP #define ASSERT_HPP #include <iostream> #ifdef NDEBUG #define ASSERT(condition) ((void)0) #else #define ASSERT(condition) (safe_assert((condition), #condition, \ __FILE__, __LINE__, __func__)) #endif inline void safe_assert(bool condition, const char* condition_text, const char* filename, int line, const char* function) noexcept { if (!condition) { try { std::cerr << "Assertion failed: " << condition_text << ", file " << filename << ", line " << line << ", function " << function << std::endl; } catch (...) { /* we want to keep the noexcept promise */ } std::abort(); } } #endif
#ifndef ARRAY_HPP #define ARRAY_HPP #include <algorithm> #include <cstdlib> #include <initializer_list> #include <iterator> #include <new> #include <utility> #include "assert.hpp" template<typename T> class Array { private: using Tag = enum {AllocateStorage}; Array(Tag tag, std::size_t size) : size(size), data(size > 0? static_cast<T*>(operator new(sizeof(T) * size)) : nullptr) { } public: Array() noexcept : size(0), data(nullptr) { } Array(std::size_t size) : Array(AllocateStorage, size) { for (std::size_t index = 0; index < size; ++index) { new (data + index) T(); } } Array(std::size_t size, const T& t) : Array(AllocateStorage, size) { for (std::size_t index = 0; index < size; ++index) { new (data + index) T(t); } } template<typename Iterator> Array(Iterator it1, Iterator it2) : Array(AllocateStorage, std::distance(it1, it2)) { std::size_t index = 0; while (it1 != it2 && index < size) { new (data + index++) T(*it1++); } size = index; // just in case } Array(std::initializer_list<T> elements) : Array(elements.begin(), elements.end()) { } Array(const Array& other) : Array(AllocateStorage, other.size) { for (std::size_t index = 0; index < size; ++index) { new (data + index) T(other.data[index]); } } friend void swap(Array& a1, Array& a2) noexcept { using std::swap; swap(a1.size, a2.size); swap(a1.data, a2.data); } Array(Array&& other) noexcept : Array() { swap(*this, other); } ~Array() { for (std::size_t index = 0; index < size; ++index) { data[index].~T(); } operator delete(data); } Array& operator=(Array other) noexcept { swap(*this, other); return *this; } std::size_t get_size() const noexcept { return size; } T& operator()(std::size_t index) noexcept { ASSERT(index < size); return data[index]; } const T& operator()(std::size_t index) const noexcept { ASSERT(index < size); return data[index]; } T* begin() noexcept { return data; } const T* begin() const noexcept { return data; } T* end() noexcept { return data + size; } const T* end() const noexcept { return data + size; } private: std::size_t size; T* data; }; #endif
#include "array.hpp" int main() { Array<int> a(10); a(11) = 2; }
theon$ g++ --coverage -Wall -o test-assertion test-assertion.cpp theon$ ./test-assertion Assertion failed: index < size, file array.hpp, line 75, function operator() theon$ rm -f core theon$
Haben Sie daran gedacht, dass es bei den Ausgabe-Operationen auch zu Ausnahmen kommen kann? Deswegen wurde die Ausgabe hier in einen try-Block eingeschlossen, der alles abfängt. Wenn die Ausgabe nicht klappt, dann haben wir eben Pech gehabt.