414просмотров
15 декабря 2025 г.
Score: 455
Давайте посмотрим на такой пример заголовочного файла (.h) c использованием указателя на incomplete тип (forward declaration): #include <memory> class ForwardDClass; class WithSmartPtr
{
public: WithSmartPtr() = default; ~WithSmartPtr() = default;
private: ForwardDClass rawPtr_; //std::shared_ptr<ForwardDClass> sPtr_; //std::unique_ptr<ForwardDClass> uPtr_;
}; int main(int argc, char argv<::>)
{ WithSmartPtr w; return 0;
}
Не будем вдаваться в корректность, посмотрим для начала на компилируемость.
Такой код, конечно же соберется, ведь компилятор знает размер указателя в байтах (размер одинаков как для определенного, так и неопределенного типа). Если мы раскомментируем строку с shared_ptr, то код тоже скомпилируется (по крайней мере у меня собрался). Не будем говорить, про корректность его работы, но напомним, что вцелом удаление неопределенного типа - UB.
А вот если мы раскомментируем строку с unique_ptr, то код перестанет собираться.
Многие используют паттерн pimpl и потому знают, что реализацию деструктора нужно сделать в других исходниках (например .cpp), где forward declaration тип определен и проблема этим решается.
Но почему компилятор не ругается на shared_ptr?
Дело в том, что unique_ptr и shared_ptr требуют полного определения типа в разных местах. Детали сложны (и о них я не знаю), но первому нужен статический deleter, тогда как второму - динамический.
Вывод: в нашей копилке костылей еще одно неправильное решение - заменить unique_ptr на shared_ptr 😊.