Оптимизация памяти при перегрузке операторов и структурах шаблонов

Задание:

В этой задаче вам нужно написать структуру, которая будет работать как обычный массив в C и как список в Python. А именно, напишите структуру

template<typename T>
struct FlexArray;

Те. это шаблонная структура. Это массив элементов типа T фиксированной длины, длина передается в конструктор. По умолчанию следует считать, что длина равна 0. (Это не обязывает вас использовать при реализации только классические массивы)

Доступ к элементам осуществляется через метод at(unsigned int index) — возвращает индексный элемент массива. Напишите этот метод правильно как для неконстантных, так и для константных структур. А именно, вызов at(i) для константных структур не должен допускать изменения содержимого массива и, более того, не может появляться слева от знака =.

Size(), который возвращает текущий размер массива.

Теперь самое интересное.

Напишите operator+ из двух FlexArrays и operator+=, где добавление FlexArray считается конкатенацией их элементов. Те. {1, 3, 5} + {2, 4} = {1, 3, 5, 2, 4}.

Вы можете добавить два разных FlexArrays, только если их аргументы шаблона совпадают.

operator += должен фактически добавить новый массив к текущему FlexArray.

Также напишите operator* и operator*= от FlexArray и числа k > 0, где умножение на число k — это объединение самого себя k раз. Те. {1, 2} * 3 = {1, 2, 1, 2, 1, 2}. Мы можем предположить, что k — это unsigned long

Умножать нужно как слева направо, так и справа налево. Те. Возможны только 3 варианта FlexArray *= k, FlexArray * k, k * FlexArray

int main() {
 FlexArray<int> flex(3);
 flex.at(0) = 1;
 flex.at(1) = 2;
 flex.at(2) = 3;
 std::cout << flex.Size(); // 3

 auto prod = flex * 2;

 for (int i = 0; i < prod.Size(); ++i) {
  std::cout << prod.at(i) << " ";
 }
 std::cout << "\n";
 // Should output 1 2 3 1 2 3

 FlexArray<int> last(2);
 last.at(0) = 4;
 last.at(1) = 5;
 flex += last;

 for (int i = 0; i < flex.Size(); ++i) {
  std::cout << flex.at(i) << " ";
 }
 std::cout << "\n";
 // Should output 1 2 3 4 5

}

Итак, реализация этих методов зависит от вас; типы возвращаемых значений здесь специально не прописаны:

FlexArray<T>::FlexArray();
FlexArray<T>::FlexArray(size_t);
FlexArray<T>::at(size_t);
FlexArray<T>::Size();
FlexArray<T>::operator += (const FlexArray&);
FlexArray<T>::operator *= (size_t);
operator+(const FlexArray<T>&, const FlexArray<T>&);
operator*(const FlexArray<T>&, size_t);
operator*(size_t, const FlexArray<T>&);

Отправьте в систему только код структуры + возможно необходимо # include

Это мое решение:

template<typename T>
struct FlexArray {
    std::vector<T> vec;
    FlexArray();
    FlexArray(size_t n);
    T& at(size_t index);
    const T& at(size_t index) const;
    size_t Size() const;
    FlexArray& operator += (const FlexArray& another);
    FlexArray& operator *= (size_t k);
};

template<typename T>
FlexArray<T>::FlexArray() : vec(0){}

template<typename T>
FlexArray<T>::FlexArray(size_t n) : vec(n) {}

template<typename T>
T& FlexArray<T>::at(size_t index) {
    return vec[index];
}

template<class T>
const T& FlexArray<T>::at(size_t index) const {
    return vec[index];
}

template<typename T>
size_t FlexArray<T>::Size() const{
    return vec.size();
}

template<typename T>
FlexArray<T>& FlexArray<T>::operator += (const FlexArray& another) {
    for (size_t i = 0; i < another.Size(); i++) {
        vec.push_back(another.at(i));
    }
    return *this;
}

template<typename T>
FlexArray<T>& FlexArray<T>::operator *= (size_t k) {
    auto copy = *this;
    for (size_t i = 1; i < k; i++) {
        *this += copy;
    }
    return *this;
}

template<typename T>
FlexArray<T> operator+(const FlexArray<T>& left, const FlexArray<T>& right) {
    auto copy = left;
    copy += right;
    return copy;
}

template<typename T>
FlexArray<T> operator*(const FlexArray<T>& arr, size_t k) {
    auto copy = arr;
    copy *= k;
    return copy;
}

template<typename T>
FlexArray<T> operator*(size_t k, const FlexArray<T>& arr) {
    return arr * k;
}

Проблема в том, что он не проходит тест памяти (ограничение 64 МБ). Мой код выдает 100мс/86,77Мб.

В системе тестирования используется G++17 7.3.

Как я могу это оптимизировать? (задание взято из онлайн-курса и я уверен, что код можно как-то оптимизировать не очень сложно, или я где-то ошибся)

Возможно, политика изменения размера vector вас подводит. Когда vector необходимо перераспределить для увеличения емкости, размер обычно увеличивается где-то между 1,5 и 2, в зависимости от реализации vector. На несколько мгновений размер хранилища данных изменяется, а в памяти появляется новое хранилище данных, размер которого в 1,5–2 раза превышает размер оригинала.

user4581301 24.07.2024 00:13

Примечание по использованию. Возможно, вам не придется предоставлять инструктору полную программу, но если вы ожидаете, что добровольцы помогут отладить ваш код, в ваших интересах предоставить полную программу. Если людям придется превратить ваши фрагменты кода в работоспособный пример, чтобы они могли с ним поэкспериментировать, многие просто уйдут. Некоторые из них, выходя за дверь, проголосуют против и принудительно, потому что вопрос неполный. Те, кто останется и создаст работоспособный пример, могут случайно исправить ошибку в процессе и уйти, поскольку у вас явно нет проблем, а другие могут вставить новые проблемы и сообщить о них.

user4581301 24.07.2024 00:23

Пожалуйста, предоставьте минимально воспроизводимый пример.

Passer By 24.07.2024 02:58
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
3
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Использование push_back() в цикле не является эффективным способом объединения двух векторов. Вместо этого используйте vector::insert(), например:

template<typename T>
FlexArray<T>& FlexArray<T>::operator += (const FlexArray& another) {
    vec.insert(vec.end(), another.vec.begin(), another.vec.end());
    return *this;
}

Кроме того, перед выполнением конкатенации используйте vector::reserve(), чтобы предварительно выделить память для нового размера, например:

template<typename T>
FlexArray<T>& FlexArray<T>::operator *= (size_t k) {
    if (k > 1) {
        auto copy = *this;
        vec.reserve(vec.size() * k);
        for (size_t i = 1; i < k; i++) {
            *this += copy;
        }
    } else if (k == 0) {
        vec.clear();
    }
    return *this;
}

template<typename T>
FlexArray<T> operator+(const FlexArray<T>& left, const FlexArray<T>& right) {
    FlexArray<T> copy;
    copy.vec.reserve(left.Size() + right.Size());
    copy += left;
    copy += right;
    return copy;
}

Альтернативно, манипулируйте vector напрямую, чтобы избежать ненужных копий, как copy в operator *= выше, например:

template<typename T>
FlexArray<T>& FlexArray<T>::operator += (const FlexArray& another) {
    size_t oldSize = vec.size();
    vec.resize(oldSize + another.Size());
    std::copy(another.vec.begin(), another.vec.end(), vec.begin()+oldSize);
    return *this;
}

template<typename T>
FlexArray<T>& FlexArray<T>::operator *= (size_t k) {
    if (k > 1) {
        size_t oldSize = vec.size();
        vec.resize(oldSize * k);
        for(size_t i = 0; i < oldSize; ++i) {
            for (size_t j = 1; j < k; ++j) {
                vec[i+(oldSize*j)] = vec[i];
            }
        }
    }
    else if (k == 0) {
        vec.clear();
    }
    return *this;
}

template<typename T>
FlexArray<T> operator+(const FlexArray<T>& left, const FlexArray<T>& right) {
    FlexArray<T> copy(left.Size() + right.Size());
    std::copy(left.vec.begin(), left.vec.end(), copy.vec.begin());
    std::copy(right.vec.begin(), right.vec.end(), copy.vec.begin()+left.Size());
    return copy;
}

template<typename T>
FlexArray<T> operator*(const FlexArray<T>& arr, size_t k) {
    FlexArray<T> copy(arr.Size()*k);
    auto iter = copy.vec.begin();
    for(size_t i = 0; i < k; ++i) {
        std::copy(arr.vec.begin(), arr.vec.end(), iter);
        iter += arr.Size();
    }
    return copy;
}

Другие вопросы по теме