Ausnahmenbehandlungen

Content

Ausnahmenbehandlungen sind in ihrer Implementierung inhärent teuer, da der Aufrufer einer Funktion oder Methode sich für den Fall vorbereiten muss, dass das Stacksegment für den umgebenden Block im Rahmen einer Ausnahmenbehandlung abgebaut werden muss. Ein wichtiges Optimierungspotential wird daher gewonnen, wenn die Signatur einer Funktion oder Methode explizit mit dem Schlüsselwort noexcept zusichert, dass keine Ausnahmen erfolgen.

Hierzu gibt es ein paar einfache Grundregeln:

Aufgabe

Nehmen Sie die Array-Klasse aus der vierten Übungssitzung und ergänzen Sie diese mit noexcept überall, wo es zweifellos zulässig ist.

Bedenken Sie, dass assert aus #include <cassert> leider nicht als noexcept anzusehen ist, obwohl es sich um eine Funktion handelt, die bei einem Abbruch normalerweise keine Ausnahmenbehandlung auslöst, sondern eben std::abort aufruft, das interessanterweise als noexcept deklariert ist.

Vorlage

#ifndef ARRAY_HPP
#define ARRAY_HPP

#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <initializer_list>
#include <iterator>
#include <new>
#include <utility>

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() : 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) {
	 using std::swap;
	 swap(a1.size, a2.size);
	 swap(a1.data, a2.data);
      }
      Array(Array&& other) : Array() {
	 swap(*this, other);
      }
      ~Array() {
	 for (std::size_t index = 0; index < size; ++index) {
	    data[index].~T();
	 }
	 operator delete(data);
      }
      Array& operator=(Array other) {
	 swap(*this, other);
	 return *this;
      }
      std::size_t get_size() const {
	 return size;
      }
      T& operator()(std::size_t index) {
	 assert(index < size);
	 return data[index];
      }
      const T& operator()(std::size_t index) const {
	 assert(index < size);
	 return data[index];
      }
      T* begin() {
	 return data;
      }
      const T* begin() const {
	 return data;
      }
      T* end() {
	 return data + size;
      }
      const T* end() const {
	 return data + size;
      }
   private:
      std::size_t size;
      T* data;
};

#endif
#include <iostream>
#include "array.hpp"

int main() {
   Array<int> a = {2, 3, 5, 7, 11, 13};
   for (auto val: a) {
      std::cout << " " << val;
   }
   std::cout << std::endl;
}