Russian Qt Forum
Апрель 25, 2024, 13:49 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 2 [3] 4 5   Вниз
  Печать  
Автор Тема: Ненулевые указатели  (Прочитано 29198 раз)
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #30 : Ноябрь 30, 2019, 12:55 »

зачем изобретать указатель, который делает вид,
что он ссылка?

Указателю, после инициализации, можно присваивать другие значения, в отличие от ссылки, которую после инициализации уже нельзя связать с другим объектом. Кроме того, SomeClass&  и, например, not_null<unique_ptr<SomeClass>> выражают совершенно разный тип связи с объектом.
Записан

Пока сам не сделаешь...
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #31 : Декабрь 01, 2019, 16:17 »

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

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

похоже, что ваш not_null - это ещё один способ организации такой связи.

вот только мне не понятно: зачем он нужен то?
всмысле, зачем вообще привлекать ещё какие то дополнительные инструменты,
если желаемое итак можно получить используя обычные ссылки и указатели?


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

тогда:

Код:
struct child
{
    child(parent& object) noexcept
        : m_parent(&object)
    {}

    parent& getParent() noexcept
    {
         return *this->m_parent;
    }

    void setParent(parent& object) noexcept
    {
        this->m_parent = &parent;
    }
    void work()
    {
         // указатель  this->m_parent априори валидный
    }
private:
    parent* m_parent;
};

суть:
указатель m_parent всегда будет валидным,
поскольку получает своё значение через ссылку,
которая гарантированно валидная в корректной программе.


Кроме того, SomeClass&  и, например, not_null<unique_ptr<SomeClass>> выражают совершенно разный тип связи с объектом.

нет, не разный.

SomeClass& - ссылочная семантика
связь с валидным объектом.

not_null<SomeClass>  - тоже ссылочная семантика.
потому что так же подразумевает связь с валидным объектом.


первый вариант простой, понятный и логичный.

второй вариант - замороченный продукт человека,
который сначала создал себе проблему на ровном месте,
а потом героически начал её преодолевать.

однако в обоих случаях семантика связи - одинаковая.
Записан
Авварон
Джедай : наставник для всех
*******
Online Online

Сообщений: 3258


Просмотр профиля
« Ответ #32 : Декабрь 02, 2019, 11:52 »

Ссылка не позволяет реализовать композицию. Композицию позволяет реализовать мембер, но с ним проблемы с присваиванием - надо "писать правильно" и делать мув конструктор. Поди объясни это кутешникам.
Разделяемое владение мембер не позволяет реализовать (потому что нет перегрузки оператора., была бы, сделали бы "шаредную ссылку" и было бы счастье).
Получается что часть объектов по значению и доступ через точку, половина через указатель и доступ через стрелочку.
А когда было уникальное владение через мембер и надо сделать шаредное, начинаются пляски с заменой точки на стрелочку, вот это чем хочется заниматься конечно и ревьювить потом.
Еще у ссылок есть неприятное свойство мимикрировать мод мемберы - я как-то сделал большой патч по передаче объектов черех мув в ктор, а там ВНЕЗАПНО одна мапа объявлена не как мембер, а ссылка (оптимизаторы хреновы). И ни один компилятор мне не сказал мне что я присваиваю временный объект в ссылку и ни один санитайзер это не отловил.
К счастью, clang-tidy подсвечивает как неоптимальное место (типа нафига ты копируешь если в ссылку присваиваешь, передавай ссылку, так быстрее)
Записан
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #33 : Декабрь 02, 2019, 12:13 »

Ссылка не позволяет реализовать композицию. Композицию позволяет реализовать мембер, но с ним проблемы с присваиванием - надо "писать правильно" и делать мув конструктор. Поди объясни это кутешникам.
Разделяемое владение мембер не позволяет реализовать (потому что нет перегрузки оператора., была бы, сделали бы "шаредную ссылку" и было бы счастье).
Получается что часть объектов по значению и доступ через точку, половина через указатель и доступ через стрелочку.
А когда было уникальное владение через мембер и надо сделать шаредное, начинаются пляски с заменой точки на стрелочку, вот это чем хочется заниматься конечно и ревьювить потом.
Еще у ссылок есть неприятное свойство мимикрировать мод мемберы - я как-то сделал большой патч по передаче объектов черех мув в ктор, а там ВНЕЗАПНО одна мапа объявлена не как мембер, а ссылка (оптимизаторы хреновы). И ни один компилятор мне не сказал мне что я присваиваю временный объект в ссылку и ни один санитайзер это не отловил.
К счастью, clang-tidy подсвечивает как неоптимальное место (типа нафига ты копируешь если в ссылку присваиваешь, передавай ссылку, так быстрее)

не распарсил этот поток сознания.
ты б хоть примеры привел, поясняющие.
Записан
Авварон
Джедай : наставник для всех
*******
Online Online

Сообщений: 3258


Просмотр профиля
« Ответ #34 : Декабрь 02, 2019, 12:30 »

не распарсил этот поток сознания.
ты б хоть примеры привел, поясняющие.


Меняем юник
Код:
class Foo
{
public:
    using BarPtr = std::unique_ptr<Bar>;
    void setBar(BarPtr bar) { m_bar = std::move(bar); }
    void doBaz() { m_bar->baz(); }

    BarPtr m_bar;
};
на шаред

Код:
class Foo
{
    using BarPtr = std::shared_ptr<Bar>; // поменялось раз
    void setBar(BarPtr bar) { m_bar = std::move(bar); }
    void doBaz() { m_bar->baz(); }

    BarPtr m_bar;
};

Меняем мембер

Код:
class Foo
{
public:
    void setBar(Bar &&bar) { m_bar = std::move(bar); }
    void doBaz() { m_bar.baz(); }
private:
    Bar m_bar;
};

на шаред

Код:
class Foo
{
    using BarPtr = std::shared_ptr<Bar>; // добавилось
    void setBar(BarPtr bar) { m_bar = std::move(bar); } // поменялось раз
    void doBaz() { m_bar->baz(); } // поменялось два (точка на ->)

    BarPtr m_bar; // поменялось три
};

Код приходится перелопачивать полностью тупо чтобы поменять юник на шаред.

Про чудесные ссылки, вот фикс. Можете, глядя на дифф увидеть в чем была проблема? Нет? Я тоже не увидел. А оно вон оказывается как в хедере объявлено
Записан
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #35 : Декабрь 02, 2019, 13:06 »

Код приходится перелопачивать полностью тупо чтобы поменять юник на шаред.

я не понял смысла этих примеров.

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

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

Про чудесные ссылки, вот фикс. Можете, глядя на дифф увидеть в чем была проблема? Нет? Я тоже не увидел. А оно вон оказывается как в хедере объявлено

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







« Последнее редактирование: Декабрь 02, 2019, 13:08 от _Bers » Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #36 : Декабрь 02, 2019, 13:09 »

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

Такой вариант имеет право на существование, только, имхо, выворачивает наизнанку реализацию "связь с априори валидным объектом". Вместо того, чтобы один раз при объявлении поля класса указать, что оно не может быть пустым, и пусть компилятор это отслеживает, разработчики сами должны следить, чтобы в это поле не попала пустота. В конструкторах, сеттерах и прочем. Это вы сейчас знаете, что это поле не может быть пустым, а кто-нибудь другой и потом увидит, что тип поля - это простой указатель, и добавит метод, который принимает указатель. И всё, "алгоритм не работает". Что тогда предлагаете делать? Уповать на то, что другие догадаются, что автор задумал поле непустым, хотя там обычный указатель? Или комментарий для поля писать: "The m_parent must be not_null."? Улыбающийся

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

Давайте рассмотрим немного изменённый пример:
Код
C++ (Qt)
#include <iostream>
 
struct parent
{
   std::string name;
};
 
struct child
{
   child(const parent& mother, const parent& father) noexcept
       : m_mother{&mother}, m_father{&father}
   {}
 
   const parent& getMother() noexcept { return *this->m_mother; }
   const parent& getFather() noexcept { return *this->m_father; }
 
   void setParents(const parent& mother, const parent& father) noexcept
   {
       this->m_mother = &mother;
       this->m_father = &father;
   }
 
   void work()
   {
       std::cout << "mother: " << getMother().name << std::endl;
       std::cout << "father: " << getFather().name << std::endl;
   }
 
private:
   const parent* m_mother;
   const parent* m_father;
};
 
int main()
{
   parent mother{"Marfa"};
   parent father{"Miron"};
 
   child  son{mother, father};
   son.work();
 
   son.setParents(parent{"Stepanida"}, parent{"Stepan"});
   son.work();
 
   return 0;
}

С этим кодом всё хорошо? Никаких проблем нет?

Кроме того, SomeClass&  и, например, not_null<unique_ptr<SomeClass>> выражают совершенно разный тип связи с объектом.

нет, не разный.

SomeClass& - ссылочная семантика
связь с валидным объектом.

not_null<SomeClass>  - тоже ссылочная семантика.
потому что так же подразумевает связь с валидным объектом.

первый вариант простой, понятный и логичный.

второй вариант - замороченный продукт человека,
который сначала создал себе проблему на ровном месте,
а потом героически начал её преодолевать.

однако в обоих случаях семантика связи - одинаковая.

А пошто вы unique_ptr на мороз выставили? Улыбающийся Я же его специально туда добавил. not_null<unique_ptr<SomeClass>> при инициализации и присваивании должен проверять, что unique_ptr не пустой. И помимо выражения самого факта связи с существующим объектом, выражает ещё семантику владения этим объектом.
Записан

Пока сам не сделаешь...
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #37 : Декабрь 02, 2019, 14:17 »

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

в том то и дело, что компилятор не сможет отследить.
это технически невозможно.
ты рантайм с компиляцией не перепутал?

Это вы сейчас знаете, что это поле не может быть пустым, а кто-нибудь другой и потом увидит, что тип поля - это простой указатель, и добавит метод, который принимает указатель.
маловероятно.
на каком основании он может захотеть добавить метод,
который принимает указатель, а не ссылку?

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

в первом случае: пьяный проспится, дурак - никогда.

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

хочешь создать сироту? используй конкструктор вообще без параметров.
хочешь лишить ребенка родителя? используй метод removeParent()

указатели для этого не нужны.

Давайте рассмотрим немного изменённый пример:
С этим кодом всё хорошо? Никаких проблем нет?
замени ссылки на указатели - получишь те же яйца.
какой тогда смысл обсуждать это?

А пошто вы unique_ptr на мороз выставили? Улыбающийся

честно говоря, я не очень то понимаю,
зачем вообще выдавать наружу какой то юник?

если фабрика хочет вернуть объект,
то почему нельзя вернуть просто по значению?

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

Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #38 : Декабрь 02, 2019, 15:22 »

И всё, "алгоритм не работает". Что тогда предлагаете делать? Уповать на то, что другие догадаются, что автор задумал поле непустым, хотя там обычный указатель? Или комментарий для поля писать: "The m_parent must be not_null."? Улыбающийся
"Почему нет, Леня? (АО МММ)". Ну не догадался (уповал), ну и чем это грозит? Ах, боже мой, получу вылет!!! Да я был бы счастлив иметь такие ошибки Улыбающийся Здравый коммент Can (not) be NULL никак не хуже очередной лабуды которую надо запоминать. На работу алгоритма это никак не влияет, там вес таких свистелок-перделок даже не 1%.
Записан
Авварон
Джедай : наставник для всех
*******
Online Online

Сообщений: 3258


Просмотр профиля
« Ответ #39 : Декабрь 02, 2019, 15:27 »

Ах, боже мой, получу вылет!!!

Не получите, разыменование нулевого указателя это UB а не "вылет".
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #40 : Декабрь 02, 2019, 16:22 »

в том то и дело, что компилятор не сможет отследить.
это технически невозможно.
ты рантайм с компиляцией не перепутал?

Нет, не перепутал. В случае с not_null на то, что можно выявить в compile-time, будет выведена ошибка компиляции. Для runtime в not_null будут assert'ы или contract conditions (когда их таки добавят в стандарт), а не разработчики сами их будут везде расставлять. Тип not_null даст дополнительную информацию статическим анализаторам для более детальной диагностики, если компилятор себе такое позволить не сможет.

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

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

в первом случае: пьяный проспится, дурак - никогда.

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

хочешь создать сироту? используй конкструктор вообще без параметров.
хочешь лишить ребенка родителя? используй метод removeParent()

указатели для этого не нужны.

Можно было ответить короче: уповать.

замени ссылки на указатели - получишь те же яйца.
какой тогда смысл обсуждать это?

Так проблем нет, вы про них не знаете, или не хотите озвучивать? Сами сначала замените и посмотрите, получится то же самое или нет. Для начала на обычный указатель, потом на gsl::not_null, хоть и в том состоянии, в котором он находится сейчас.

честно говоря, я не очень то понимаю,
зачем вообще выдавать наружу какой то юник?

если фабрика хочет вернуть объект,
то почему нельзя вернуть просто по значению?

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

Как предлагаете переписать такую абстрактную фабрику, как например QItemEditorFactory, чтобы она возвращала объекты по значению? Тогда может и найдёте одну из причин существования юника.
« Последнее редактирование: Декабрь 02, 2019, 16:36 от ViTech » Записан

Пока сам не сделаешь...
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #41 : Декабрь 02, 2019, 16:34 »

"Почему нет, Леня? (АО МММ)". Ну не догадался (уповал), ну и чем это грозит? Ах, боже мой, получу вылет!!! Да я был бы счастлив иметь такие ошибки Улыбающийся Здравый коммент Can (not) be NULL никак не хуже очередной лабуды которую надо запоминать. На работу алгоритма это никак не влияет, там вес таких свистелок-перделок даже не 1%.

Это я в общем про подход, когда один что-то неявно подразумевает, а другие должны об этом догадаться, не обязательно про указатели. У Вас, похоже, случилось то самое "маловероятно", раз Char от бессилия падает на задницу, вместо того, чтобы бодро бегать Улыбающийся.
Записан

Пока сам не сделаешь...
Авварон
Джедай : наставник для всех
*******
Online Online

Сообщений: 3258


Просмотр профиля
« Ответ #42 : Декабрь 02, 2019, 16:54 »

замени ссылки на указатели - получишь те же яйца.
какой тогда смысл обсуждать это?

То есть вы часто пишете код вида?
Код:
son.setParents(&parent{"Stepanida"}, &parent{"Stepan"});

Оно вообще скомпилируется?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #43 : Декабрь 03, 2019, 14:12 »

Не получите, разыменование нулевого указателя это UB а не "вылет".
Т.е. будет "принят"(?) какой-то разумный/существующий адрес, а по нему будет сидеть объект именно нужного типа, и все покатится дальше? Вам это не напоминает "сказки про белого бычка"? Улыбающийся

Это я в общем про подход, когда один что-то неявно подразумевает, а другие должны об этом догадаться, не обязательно про указатели.
Да, и это нормально. См хотя бы std::vector, разве он не предполагает что эл-ты идут подряд, что вставка/удаление может сделать итераторы невалидными и.т.п.? Разве это подтверждается какой-то "семантикой"? Попытки сделать все явным (по объявлению ясно) - в принципе дело хорошее, но в меру, не стоит гнаться за недостижимым. 

У Вас, похоже, случилось то самое "маловероятно", раз Char от бессилия падает на задницу, вместо того, чтобы бодро бегать Улыбающийся.
Поверьте, все эти цацки-пецки к работе не имеют отношения. Ну вот НИКАКОГО. Можно написать все "на голых", можно все "на вумных" - на рез-т это никак не повлияет и ни одного бага не выявит. Будет всего лишь "более или менее удобно" (и то смотря для кого). В расчетных задачах (где тех классов всего лишь десяток-другой) вообще пофиг.

Да, и заниматься такими "общими" (концептуальными) вещами легко м приятно Улыбающийся Это ж не сидеть неделями в отладчике, не отвечать за рез-т, так... "повышать повышаемость"  Улыбающийся
Записан
_Bers
Бывалый
*****
Offline Offline

Сообщений: 486


Просмотр профиля
« Ответ #44 : Декабрь 03, 2019, 14:26 »

Нет, не перепутал. В случае с not_null на то, что можно выявить в compile-time, будет выведена ошибка компиляции. Для runtime в not_null будут assert'ы или contract conditions (когда их таки добавят в стандарт), а не разработчики сами их будут везде расставлять. Тип not_null даст дополнительную информацию статическим анализаторам для более детальной диагностики, если компилятор себе такое позволить не сможет.

ты понимаешь, что твоё "то, что можно выявить в compile-time" - это менее 1% всех реальных кейсов использования?
в то время как на долю рантайма придётся больше 99% ?

в этом свете твоё заявление: "пусть компилятор это отслеживает" - звучит как жульнический маркетинг сивой кобылы.

Можно было ответить короче: уповать.
не распарсил эту фразу.

на что уповать? что новенький не окажется балбесом что ли?
для этого человечество изобрело собеседования и code-review.


Так проблем нет, вы про них не знаете, или не хотите озвучивать? Сами сначала замените и посмотрите, получится то же самое или нет. Для начала на обычный указатель, потом на gsl::not_null, хоть и в том состоянии, в котором он находится сейчас.

немножко неочевидна такая твоя реакция.
ты сам то смотрел?

или может ты просто не догоняешь?

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

указатель:
https://rextester.com/KOASHD96152


ссылка:
https://rextester.com/QIPI41754


просто код нужно писать грамотно:
https://rextester.com/LLDG51891

вот ты в качестве примера привел проблему,
которая с твоим not_null вообще никак не коррелирует.


Как предлагаете переписать такую абстрактную фабрику, как например QItemEditorFactory, чтобы она возвращала объекты по значению? Тогда может и найдёте одну из причин существования юника.

нафига?Непонимающий
это - не твоя фабрика.
зачем её переписывать?

Код:
auto* object = api::create();
assert(object);
work(*object);   // <--- это так сложно?


Записан
Страниц: 1 2 [3] 4 5   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.134 секунд. Запросов: 22.