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.