Класс массива в C++


Я хотел бы научиться создавать полезный и простой в использовании для клиентов Array класс. Класс ниже-это исключительно для учебных целей.

Вот те пункты, которые меня интересуют:

  • Стиль (the code is easy to read and maintain)
  • Логическую правильность (class user will get what he\she expects)
  • Эффективность (no very very big performance problems)

Я рефакторинг класса так хорошо, как я мог, поэтому я обращаюсь за помощью, который научил меня.

//Developed by Trofimov Yaroslav on 30.03.2018

#ifndef _ARRAY_H_TROFIMOV_
#define _ARRAY_H_TROFIMOV_

#include <string>

template<const size_t n, typename T>
class Array {
public:
    typedef const bool (* const BooleanResultDelegate)(const T&);
    class ArrayError {
        const std::string _reason;
        const size_t _index;
        const size_t _maxIndex;
    public:
        ArrayError(const size_t index, const size_t maxIndex,const std::string& reason = "") 
            : _index(index), _maxIndex(maxIndex), _reason(reason) {}
        std::string explanation(void) {
            std::string res;
            res += "Index: " + std::to_string(_index) + "\n"; 
            res += "Max index: " + std::to_string(_maxIndex) + "\n"; 
            res += "Reason: " + _reason + "\n"; 
            return res;
        }
    };

private:
    typedef void (Array<n, T>::* const VoidResultDelegate)(const size_t, const BooleanResultDelegate);

    static unsigned __freeId, __quantity;
    unsigned _id;

    T** _array;
    const size_t _n;

    void iterateAndApply(
        const VoidResultDelegate function, 
        const BooleanResultDelegate shouldApply) {
        for(size_t i(0); i < length(); ++i) {
            (this->*function)(i, shouldApply);
        }
    }
    void removeElement(const size_t i, const BooleanResultDelegate removeCondition) {
        if(removeCondition == 0 || 
            (_array[i] != 0 && removeCondition(*_array[i]))) {
                delete [] _array[i]; _array[i] = 0;
        }
    }
    T _replacer;
    void replaceElement(const size_t i, const BooleanResultDelegate replaceCondition) {
        if(replaceCondition == 0 || 
            (_array[i] != 0 && replaceCondition(*_array[i]))) {
                delete [] _array[i];
                _array[i] = new T(_replacer);
        }
    }

public:
    explicit Array<n, T>(T* arrayFiller = 0) 
        : _n(n), _array(new T*[n]), _id(++__freeId) {
            if(arrayFiller != 0) {
                for(size_t i(0); i < length(); ++i) {
                    _array[i] = new T(*arrayFiller);
                }
            } else {
                for(size_t i(0); i < length(); ++i) {
                    _array[i] = arrayFiller;
                }
            }
            reportIfDebug<n, T>(*this, "created");
            ++__quantity;
    }
    explicit Array<n, T>(const T& arrayFiller) 
        : _n(n), _array(new T*[n]), _id(++__freeId) {
            for(size_t i(0); i < length(); ++i) {
                _array[i] = new T(arrayFiller);
            }
            reportIfDebug<n, T>(*this, "created");
            ++__quantity;
    }
    Array<n, T>(const Array<n, T>& that) 
        : _n(n), _array(new T[n]), _id(++__freeId) {
            for(size_t i(0); i < length(); ++i) {
                (*this)[i] = new T[that[i]];
            }
            reportIfDebug<n, T>(*this, "created");
            ++__quantity;
    }
    ~Array<n, T>(void) {
        removeAll();
        delete [] _array; _array = 0;
        reportIfDebug<n, T>(*this, "deleted", false);
        --__quantity;
    }

    T* operator[](const size_t i) {
        if(i > length()) {
            throw ArrayError(i, _n, "out of bounds exception");
        }
        return _array[i];
    }
    const T* operator[](const size_t i) const {
        if(i > length()) {
            throw ArrayError(i, _n, "out of bounds exception");
        }
        return _array[i];
    }
    const size_t length() const {
        return _n;
    }
    const unsigned getID() const {
        return _id;
    }

    void removeAll(const BooleanResultDelegate removeCondition = 0) {
        iterateAndApply(&Array<n, T>::removeElement, removeCondition);
    }
    void replaceAll(const T& replacer, const BooleanResultDelegate replaceCondition = 0) {
        _replacer = replacer;
        iterateAndApply(&Array<n, T>::replaceElement, replaceCondition);
    }
};

template<const size_t n, typename T>
unsigned Array<n, T>::__freeId = 0;
template<const size_t n, typename T>
unsigned Array<n, T>::__quantity = 0;

template<const size_t n, typename T>
void reportIfDebug(
    const Array<n, T>& instance, 
    const char* const message, 
    const bool showContent = true) {
#ifndef NDEBUG
    std::cout << "========================================" << std::endl;
    std::cout << typeid(instance).name() << ' ' 
        << message << ' '
        << "id: " << instance.getID() << std::endl;
    if(showContent) {
        std::cout << instance;
    }
    std::cout << "========================================" << std::endl;
#endif
}
template<const size_t n, typename T>
std::ostream& operator<<(std::ostream& os, const Array<n, T>& instance) {
    for(size_t i(0); i < instance.length(); ++i) {
        if(instance[i] == 0) {
            os << "[" << i << "]: " << instance[i] << "\n";
        } else {
            os << "[" << i << "]: " << *instance[i] << "\n";
        }
    }
    return os;
}
#endif


179
3
задан 31 марта 2018 в 12:03 Источник Поделиться
Комментарии
1 ответ

Включают Предохранитель

Во-первых, включить гвардии это не то, что я выберу в современном C++ и есть некоторые минусы, поскольку иногда ваши #define может наехать (редкое, но не невозможное). Использовать #pragma once. Это также меньше шаблонного кода.


Тип Антиалиасинга

Предпочитаю преобразования typedef к using BooleanResultDelegate = ... Если вы хотите, чтобы соответствовать "современный C++", но это делает никакой разницы, кроме синтаксиса.


Исключения

Стандартная библиотека уже предусматривает исключение аналогичных ArrayError что std::out_of_range. Вы уже включали показатели в самой строке сообщений, поэтому есть смысл использовать это исключение, так как вы не подвергая скаляры в классе.

Если это не является удовлетворительным для вас, то продолжают строить свой собственный тип, но наследовать от std::logic_error правильно. Чем ближе вы находитесь к стл поведение лучше, потому что большинство пользователей c++ знакомы.

Кроме того, принимая во внимание контекст ваш код будет использоваться, существуют ли возможности добавить noexcept? Помните, - следующее ядро на C++ руководство е. 12, он должен быть использован не только тогда, когда функция не бросать, но если бросать по сути невозвратными или бессмысленным.

Если вы сталкиваетесь в аут ошибка, и вы не можете даже построить за исключением объектов, ArrayError потому что у вас нет места для двух скаляров и строку, есть ли смысл для программы продолжать? Если нет, то возможно выбрать noexcept для конструктора?


Правильность Константный

У вас есть места с кодом, как f(const size_t x) и это не имеет смысла, чтобы сделать его const так как он передается по значению, и таким образом, письмо к копии переменной не влияют на собеседника, и помните, const как договор вы соглашаетесь с абонентом.

1
ответ дан 1 апреля 2018 в 03:04 Источник Поделиться