Безопасность использования времени жизни объекта в качестве сеттера - PullRequest
3 голосов
/ 26 сентября 2019

Я сделал «установщик области действия», который автоматически присваивает значение переменной, обычно POD, когда она выходит из области видимости.Я использую его в основном для отслеживания того, находится ли выполнение в данный момент в определенной области.

template<typename T>
struct FScopedSetter
{
    FScopedSetter(T& InObject, T InbOutOfScopeValue)
    {
        Object = &InObject;
        bOutOfScopeValue = InbOutOfScopeValue;
    }
    virtual ~FScopedSetter()
    {
        *Object = bOutOfScopeValue;
    }

    T* Object;
    T bOutOfScopeValue;
};

// Example:
bool bInTaskA = false;
void TaskA()
{
    bInTaskA = true;
    FScopedSetter<bool> Setter(bInTaskA, false);

    // ..
}

Это будет безопаснее, когда позже я решу добавить дополнительный оператор возврата в TaskA, но забуду добавитьbInTaskA = false перед ним.

Мой вопрос: правильно ли это и будет ли (всегда) работать правильно, по крайней мере, при использовании POD, если я назову объект FScopedSetter?Я немного обеспокоен тем, что компилятор может решить, что он может закончить время жизни установщика раньше, потому что он не используется?

Спасибо!

Ответы [ 2 ]

1 голос
/ 26 сентября 2019

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

Однако, есть несколько незначительных проблем с опубликованным кодом.С одной стороны, деструктор FScopedSetter не обязательно должен быть виртуальным, поскольку здесь нет наследования.

И T::operator=(const T&) никогда не должен генерировать (лучше объявить noexcept), в противном случае деструктор вашего класса присваивателя области действияможет бросить.Если ваш код нацелен на C ++ 11, было бы еще лучше переместить bOutOfScopeValue в *Object:

FScopedSetter(T& InObject, T InbOutOfScopeValue)
 : Object(&InObject)
 , bOutOfScopeValue(InbOutOfScopeValue)
{
}
~FScopedSetter()
{
    static_assert(noexcept(*Object = std::move(bOutOfScopeValue)),
        "Move assignment of your data type may throw. Make sure it doesn't.");
    *Object = std::move(bOutOfScopeValue);
}

, а для доступа к *Object может потребоваться синхронизация, в зависимости от того, являются ли «задачи»иметь какое-то отношение к "потокам".

1 голос
/ 26 сентября 2019

Вообще идея кажется хорошей.Однако лучше использовать shared_ptr<>, чтобы быть уверенным, что зависимый объект не выйдет из области видимости слишком рано, что приведет к аварийному завершению вашей программы.

template<typename T>
struct FScopedSetter
{
    FScopedSetter(std::shared_ptr<T> InObject, T InbOutOfScopeValue)
        : Object(InObject), bOutOfScopeValue(InbOutOfScopeValue) {}
    virtual ~FScopedSetter()
    {
        *Object = bOutOfScopeValue;
    }
    std::shared_ptr<T> Object;
    T bOutOfScopeValue;
};

// Example:
auto bInTaskA = make_shared<bool>(false);
void TaskA()
{
    *bInTaskA = true;
    FScopedSetter<bool> Setter(bInTaskA, false);
    // ..
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...