AUTOSAR C++14 Rule A18-5-8

Объекты, которые не переживают функцию, должны иметь длительность автоматического хранения

Описание

Управляйте определением

Объекты, которые не переживают функцию, должны иметь длительность автоматического хранения.

Объяснение

Динамически выделенный объект приводит к дополнительным ассигнованиям, и освобождение стоит и делает вашу программу уязвимой для утечек памяти, если, например, программа возвращается из-за броска исключения перед операцией освобождения.

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

Правило позволяет исключение для локальных объектов, которые динамически выделяются, чтобы оптимизировать использование стековой памяти, потому что объекты используют большой объем памяти и могут в противном случае вызвать переполнение стека.

Реализация Polyspace

Polyspace® объекты флагов, которые создаются в функциональном осциллографе и которые не имеют длительности автоматического хранения, когда любое следующее верно:

  • Объект является интеллектуальным указателем (std::shared_ptr или std::unique_ptr) это никогда не копируется, перемещается, повторно присваивается, сбрасывается или передается вызываемому.

    Объект не отмечается, если это - немассив и во время компиляции, его размер больше 4 Кбайт, или его размер неизвестен.

  • Объект динамически выделяется при помощи операторов new или new[] и затем освобожденный через все возможные пути в функции.

    Объект не отмечается, если это - немассив и во время компиляции, его размер больше 4 Кбайт, или его размер неизвестен.

  • Объект является классом обертки, который содержит по крайней мере один элемент данных с фиксированным размером, больше, чем 16 Кбайт.

Поиск и устранение проблем

Если вы ожидаете нарушение правила, но не видите его, относитесь, чтобы Диагностировать, Почему Кодирующие Стандартные Нарушения Не Появляются как ожидалось.

Примеры

развернуть все

#include <iostream>
#include <cstdint>
#include <array>
#include <utility>

constexpr std::size_t size4KB = 4 * 1024;

class MyLargeBoard
{
  public:
    constexpr static size_t size16KB = 16 * 1024;
    MyLargeBoard() {}
    MyLargeBoard(int s);
  private:
    std::array<uint8_t, size16KB> cells;

};

void Reset_ptr(std::shared_ptr<std::pair<int32_t, int32_t>>& ptr)
{
    ptr.reset();
}

void Func(int32_t x_coord, int32_t y_coord,
          std::pair<int32_t, int32_t>** param_ptr)
{
    std::shared_ptr<std::pair<int32_t, int32_t>>reused_ptr(*param_ptr);

    auto toResetPtr = //Compliant, smart pointer is reset
        std::make_shared<std::pair<int32_t, int32_t>>(x_coord, y_coord);
    auto unused_ptr = //Non-compliant
        std::shared_ptr<std::pair<int32_t, int32_t>>(reused_ptr);

    if (toResetPtr->first || toResetPtr->second) {
        Reset_ptr(toResetPtr);
    }

}

void SmartPtrLargeMem()
{
    //Large non-array object
    std::shared_ptr<MyLargeBoard>
        big_non_array(new MyLargeBoard); // Compliant
    //Large array of char
    std::unique_ptr<char []>
        big_array {new char[size4KB]{'1', '2', '3', '4'}}; //Non-compliant

}

В этом примере Polyspace отмечает эти интеллектуальные указатели как несовместимые:

  • Разделяемый интеллектуальный указатель unused_ptr потому что это объявляется локально в Func и это никогда не копируется, перемещается, повторно присваивается, сбрасывается или передается вызываемому.

  • big_array который справляется, динамически выделяют массив char в SmartPtrLargeMem. В этом контексте, использовании стандартного контейнера C++, такого как std::array вместо std::unique_ptr является менее интенсивно использующим память.

Polyspace не отмечает:

  • reused_ptr потому что это используется, чтобы инициализировать unused_ptr и toResetPtr потому что это сбрасывается.

  • big_non_array потому что это - объект немассивов с одним элементом данных размера, меньшего, чем 16 Кбайт. Правило позволяет исключение для таких объектов, потому что динамическое выделение может помочь оптимизировать использование стековой памяти, например, на встроенном устройстве с устройством хранения данных ограниченной памяти.

#include <iostream>
#include <cstdint>
#include <array>
#include <utility>

constexpr std::size_t size4KB = 4 * 1024;

class MyLargeBoard
{
public:
    constexpr static size_t size16KB = 16 * 1024;
    MyLargeBoard() {}
    MyLargeBoard(int s);
private:
    std::array<uint8_t, size16KB> cells;

};


class Mytype
{
public:
    Mytype(int s = 0) : a{s} {}
private:
    int a;
};

void ReadInput(Mytype* input);
bool IsInValidRange(Mytype* input);

void* func(Mytype** output)
{
    auto input1 = new Mytype(); //Non-compliant
    ReadInput(input1);

    auto input2 = new Mytype(); //Compliant
    if (IsInValidRange(input2)) {
        delete input2;
    } else {
        *output = input2;
    }


    Mytype input3; //Compliant
    ReadInput(&input3);

    delete input1;

    return nullptr;

}

void DynamicAllocLargeMem()
{
    //Large non-array object
    MyLargeBoard* big_non_array {new MyLargeBoard}; //Compliant
    //Large array of char
    char* big_array { new char[size4KB]{'1', '2', '3', '4'}}; //Non-compliant
    // ....

    delete big_non_array;
    delete[] big_array;

}


В этом примере, несовместимая локальная переменная input1 динамически выделяется с оператором new и затем освобожденный через все возможные пути в func. Ненужных операций выделения и освобождения можно избежать путем объявления переменной с длительностью автоматического хранения, такой как input3, который автоматически удален когда func возвращается.

Polyspace также отмечает динамически выделенный массив big_array. В этом контексте, использовании стандартного контейнера C++, такого как std::array является менее интенсивно использующим память.

Динамически выделенная переменная input2 совместимо, потому что это не освобождено хотя все возможные пути в func. Переменной оставляют через output в else ветвь.

Точно так же Polyspace не делает флага big_non_array потому что это - объект немассивов с одним элементом данных размера, меньшего, чем 16 Кбайт. Правило позволяет исключение для таких объектов, потому что динамическое выделение может помочь оптимизировать использование стековой памяти, например, на встроенном устройстве с устройством хранения данных ограниченной памяти.

#include <iostream>
#include <cstdint>
#include <array>
#include <vector>

class MyTable
{

public:
    constexpr static size_t size64KB = 65535;
    using arrayType = std::array<uint8_t, size64KB>;
    MyTable() :   tableSize{0} {}
    MyTable(const std::string& dbPath, uint32_t inputSize) : tableSize{inputSize}
    {

        // ...
    }
    uint8_t AverageCellVal() const noexcept;
private:
    arrayType table;
    uint32_t tableSize;
};

void AvgCellVal(const std::string& dbPath, uint32_t inputSize)
{
    std::vector<uint8_t> table1(inputSize);  // Compliant
    MyTable table2(dbPath, inputSize); // Non-compliant
    uint8_t averageCellV = table2.AverageCellVal();
    std::cout << "Average cell value in " << dbPath << ": " << averageCellV << '\n';
}

class DerivedMyTable : public MyTable
{
public:
    constexpr static size_t pathMaxSize = 2 * 1024; // 2 Kb
    DerivedMyTable() : MyTable() {}
    DerivedMyTable(const std::string& dbPath, uint32_t inputSize) : MyTable(dbPath, inputSize)
    {
        std::strncpy(__dbPath, dbPath.data(), pathMaxSize - 1);
        __dbPath[pathMaxSize - 1] = '\0';
    }
private:
    char __dbPath[pathMaxSize];  // OK
};


void func(const std::string& dbPath, uint32_t inputSize)
{
    DerivedMyTable table2_derived(dbPath, inputSize);  // Non-compliant

}

В этом примере, базовый класс MyTable содержит элемент данных table из типа arrayType который соответствует массиву размера 64 Кбайта. Polyspace отмечает объявление переменной table2 потому что это использует большой объем памяти через MyTable и это используется только в AvgCellVal().

Вместо этого можно использовать менее интенсивно использующий память контейнер C++, такой как std::vector объявить объект длительности автоматического хранения, такой как table1.

Polyspace также отмечает table2_derived. Даже если DerivedMyTable не содержит элемент данных с большим размером в памяти, это выведено из базового класса, который переносит объект, который использует большой размер памяти.

Проверяйте информацию

Группа: вспомогательная библиотека Языка
Категория: Необходимый, Частично автоматизированный
Введенный в R2021b