Konstruktionen mit Hilfe einer std::initializer_list
Content |
Ein traditionelles Array lässt sich direkt mit einer Werteliste konstruieren:
int values[] = {2, 3, 5, 7, 11, 13};
Es stellt sich daher die Frage, ob dies auch mit selbst-definierten Klassen möglich ist. Und in der Tat wurde dies durch die Einführung der vom Übersetzer unterstützten std::initializer_list<T>-Klasse möglich, die über #include <initializer_list> zur Verfügung steht.
Hier ist ein triviales Beispiel, das die Benutzung zeigt:
#include <initializer_list> #include <iostream> template<typename T> struct Average { Average(std::initializer_list<T> list) : result(0) { T sum{}; for (auto it = list.begin(); it != list.end(); ++it) { sum += *it; } result = sum / list.size(); } T result; }; int main() { Average<double> avg = {1, 2, 3, 4}; std::cout << avg.result << std::endl; // delivers 2.5 }
Wenn der Übersetzer eine in geschweiften Klammern gefasste Initialisierung sieht und es einen passenden Konstruktor mit einer std::initializer_list gibt, wird dieser ausgewählt. Dies hat Vorrang vor den anderen Konstruktoren. Die Nutzung regulärer Konstruktoren ohne eine std::initializer_list kann durch den Einsatz runder Klammern erzwungen werden.
Ferner ist auch die Verwendung von std::initializer_list bei Zuweisungen, Parameterübergaben und bei einer ranged for loop möglich.
Weitere Infos zu dieser Klasse: http://en.cppreference.com/w/cpp/utility/initializer_list
Aufgaben
-
Ergänzen Sie array.hpp um einen entsprechenden Konstruktur und testen Sie diesen mit einem kleinen Testprogramm unter Verwendung von valgrind.
-
Probieren Sie, ob auch eine zwei-dimensionale Matrix mit Hilfe der Array-Klasse deklariert und fertig initialisiert werden kann. Fragen hierzu:
-
Wie ist eine zwei-dimensionale Iteration durch alle Elemente der Matrix möglich mit Hilfe von ranged for loops?
-
Welche Vor- und Nachteile hat so eine Matrix in Vergleich zu den traditionellen Datentypen?
-
Und zum Schluss ein Knacknuss: Wie effizient wird das zwei-dimensionale Array konstruiert? Gibt es eine Abhilfe oder gibt es hierzu keinen einfachen Workaround?
-
Vorlage
#ifndef ARRAY_HPP #define ARRAY_HPP #include <algorithm> #include <cassert> #include <cstdlib> #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(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