__restrict и взломы shared_ptr

#c #optimization #smart-pointers #restrict-qualifier

#c #оптимизация #интеллектуальные указатели #ограничение-квалификатор

Вопрос:

Безопасно ли следующее?

 struct K { ... }

struct A
{
    A(int psize) : size(psize), foo(nullptr), bar(nullptr)
    {
        auto dataptr = (K*)_aligned_malloc(sizeof(K) * psize * 2, 32);
        data = shared_ptr<K>(dataptr, ptr_fun(_aligned_free));
        foo = data.get();
        bar = data.get()   psize;
    }   
    K* __restrict foo;
    K* __restrict bar;
private:
    shared_ptr<K> data;
};
 

Обратите внимание на __restrict для foo и bar .

Цель состоит в том, чтобы выделенная память самоуничтожилась, когда все псевдонимы объектов исчезли.

 {
    A a(1000);
    { 
        A o = a; 
    }
    //a.foo is still valid
}
//a.foo is invalid
 

Ответ №1:

Здесь вам не нужны __restrict квалификаторы, и на самом деле вы не должны их использовать, потому __restrict что предполагается сообщить компилятору, что у вас нет никаких псевдонимов для одной и той же памяти, но на самом деле у вас есть псевдонимы — foo и data являются псевдонимами для одной и той же памяти.

Я думаю, что семантика вашего кода в порядке, в противном случае. Ваши условия a.foo по-прежнему действительны, а условия a.foo недопустимы, будут истинными.

Комментарии:

1. __restrict это только оптимизация компилятора, она не изменяет поведение вашей программы.

2. Да. Надеялся, что логической гарантии того, что foo не будет изменен в другом месте, будет достаточно для __restrict (т.Е. Память имеет псевдоним, но я обещаю, что не изменю ее, кроме как из foo). Конечно, до тех пор, пока объект не умрет.

3. Дааааа…. Я думаю, вы должны быть очень осторожны при использовании __restrict таким образом. Если у вас есть только один экземпляр A , то, возможно, вам это сойдет с рук, но ваш вариант использования четко указывает на то, что вы хотите иметь возможность копировать-присваивать A , и в этом случае у вас есть память, на которую указывают два shared_ptr указателя s и два __restrict указателя ed… очень плохая ситуация.

4. Ах, я понимаю. Не рассматривал конструкцию копирования. Спасибо!

Ответ №2:

Это небезопасно, потому что malloc не вызывает конструктор, а free не вызывает деструктор. Чтобы это было безопасно, вам нужно вручную добавить эти вызовы:

 A(int psize) : size(psize), foo(nullptr), bar(nullptr)
{
    auto dataptr = (K*)_aligned_malloc(sizeof(K) * psize * 2, 32);
    new(dataptr) K();
    data = shared_ptr<K>(dataptr, [](K *k){ k->~K(); _aligned_free(k) ));
    foo = data.get();
    bar = data.get()   psize;
}
 

Комментарии:

1. У K нет конструктора / деструктора: D Это МОДУЛЬ