Vereinfachung der Konstruktoren mit Hilfe von Faktorisierungen

Content

Bei der letzten Fassung von array.hpp fiel auf, dass mehrere Konstruktoren den gleichen Programmtext hatten, um den benötigten Speicher zu belegen. Sie unterschieden sich im Anschluss nur darin, wie die einzelnen Elemente konstruiert werden.

In so einer Situation lohnt es sich, den gemeinsamen Teil in einen separaten Konstruktor herauszufaktorisieren und diesen dann von den anderen Konstruktoren verwenden zu lassen. Wir setzen dieses Prinzip bereits beim move constructor entsprechendem copy and swap idiom ein:

Array(Array&& other) : Array() {
   swap(*this, other);
}

Hier wird bei der Initialisierung hinter dem Doppelpunkt weder size noch data benannt, stattdessen wird auf den default constructor zurückgegriffen.

Wenn wir einen speziellen Konstruktor für das Belegen von Speicher hinzufügen möchten, dann sollten wir zwei Punkte bedenken:

Aufgabe

Überlegen Sie sich, wie so ein Konstruktor aussehen könnte und fügen Sie diesen hinzu. Die anderen Konstruktoren, die Speicher anlegen, sollten dann entsprechend vereinfacht werden. Testen Sie wiederum Ihre Lösung mit valgrind.

Vorlage

#ifndef ARRAY_HPP
#define ARRAY_HPP

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

template<typename T>
class Array {
   public:
      Array() : size(0), data(nullptr) {
      }
      Array(std::size_t size) :
	 size(size),
	 data(size > 0?
	       static_cast<T*>(operator new(sizeof(T) * size))
	    :
	       nullptr) {
	 for (std::size_t index = 0; index < size; ++index) {
	    new (data + index) T();
	 }
      }
      Array(std::size_t size, const T& t) :
	 size(size),
	 data(size > 0?
	       static_cast<T*>(operator new(sizeof(T) * size))
	    :
	       nullptr) {
	 for (std::size_t index = 0; index < size; ++index) {
	    new (data + index) T(t);
	 }
      }
      Array(const Array& other) :
	    size(other.size),
	    data(size > 0?
	       static_cast<T*>(operator new(sizeof(T) * size))
	    :
	       nullptr) {
	 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];
      }
   private:
      std::size_t size;
      T* data;
};

#endif