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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Константность  (Прочитано 9097 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« : Сентябрь 21, 2012, 11:30 »

Добрый день

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

Код
C++ (Qt)
class MyClass {
 
MyItem * FindItem( ...);
const MyItem * FindItem( ...) const;
..
};
 
Само тело FindItem вызывает только др константные методы, все гуд. А вот с возвращенным указателем (на данные класса) сложнее. В большинстве случаев он const, но иногда необходимо менять данные по этому указателю. Заметим что код метода значителен (не какой-нибудь однострочный оператор), и повторять его 2 раза (с конст и без) никак не хорошо.

Как Вы решаете такую ситуацию?

Спасибо
Записан
QtCoder
Гость
« Ответ #1 : Сентябрь 21, 2012, 11:46 »

Раз иногда надо менять, то значит он никакой не const.
Тогда просто MyItem * FindItem() const;
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #2 : Сентябрь 21, 2012, 12:10 »

Раз иногда надо менять, то значит он никакой не const.
Тогда просто MyItem * FindItem() const;
Так не могу вернуть - MyItem указывает на данные класса
Записан
Akon
Гость
« Ответ #3 : Сентябрь 21, 2012, 12:46 »

Igors:
То, что вы привели - есть const propagation (распространиение константности), когда имея конст. ссылку мы можем получить только конст. на другие объекты. В противоположность этому
Код:
MyItem * FindItem() const;
прерывает константность, т.е. имея конст. ссылку мы можем получить неконст. ссылку на другие объекты. Очевидно, const propagation делает код более строгим.

Кроме как локального и абсолютно контролируемого программистом снятия константности, никакое другое разумное использование const_cast мне в голову не приходит.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Сентябрь 21, 2012, 12:51 »

Кроме как локального и абсолютно контролируемого программистом снятия константности, никакое другое разумное использование const_cast мне в голову не приходит.
Так понял что const_cast здесь неизбежно. Согласен с этим. А с volatille mutable не лучше выходит? (не пробовал)
« Последнее редактирование: Сентябрь 22, 2012, 11:24 от Igors » Записан
LisandreL
Птица говорун
*****
Offline Offline

Сообщений: 984


Надо улыбаться


Просмотр профиля
« Ответ #5 : Сентябрь 21, 2012, 14:54 »

Слышал что const_cast - это плохо, очень плохо, явный дефект в архитектуре и.т.п.
Я писал «почти наверняка», так что не совсем «явный», некоторую долю сомнения я оставил.  Веселый

Заметим что код метода значителен (не какой-нибудь однострочный оператор), и повторять его 2 раза (с конст и без) никак не хорошо.
Соглашусь. Повторять 2 раза плохо прежде всего из-за того, что потом меняя код можно в одном месте можно забыть изменить его в другом.

Как Вы решаете такую ситуацию?
Зависит от ситуации. Где возможно делаю геттеры и сеттеры. Но не при любом MyItem такой подход удобен/применим.

Кроме как локального и абсолютно контролируемого программистом снятия константности, никакое другое разумное использование const_cast мне в голову не приходит.
Ну да, как-то так получается. В своём коде и чётко понимая что делаешь.
Хуже ситуация, когда нечто такое было бы удобно с чужим кодом/библиотекой.
Мы ведь умные, можем посмотреть, как там всё устроено и что снятием константности мы ничего не сломаем.
А потом берём следующую версию библиотеки и программный интерфейс там тот же, а код - другой, и наш «хак» приводит к самым непредвиденным последствиям.
И в принципе тоже возможно и с собственным кодом при изменениях, если забудем, что где-то снимали константность.
В общем я придерживаюсь правил при использовании const_cast: со сторонним кодом не использовать, в полностью своём кодом  обходиться как с сильнодействующим лекарством: применять если «ожидаемая польза превышает потенциальный риск». Не претендую на истинность в последней инстанции.

P.S.
Недавно использовал Speex.
Кодирование фрейма:
Код
C++ (Qt)
int speex_encode_int (void *state, spx_int16_t *in, SpeexBits *bits)
Входной буффер неконстаным указателем?! Вроде бы по логике не должен он изменяться внутри, но вдруг… И что мне локальную копию делать? Хм…

Что интересно в API предлагаемого теперь на замену Opus'а такого огреха нет:
Код
C++ (Qt)
opus_int32 opus_encode(OpusEncoder *st,  const opus_int16 *pcm, int frame_size,  unsigned char *data, opus_int32 max_data_bytes)
Записан
DmitryM
Гость
« Ответ #6 : Сентябрь 21, 2012, 15:51 »

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

Код
C++ (Qt)
class MyClass {
 
MyItem * FindItem( ...);
const MyItem * FindItem( ...) const;
..
};
 
Само тело FindItem вызывает только др константные методы, все гуд. А вот с возвращенным указателем (на данные класса) сложнее. В большинстве случаев он const, но иногда необходимо менять данные по этому указателю. Заметим что код метода значителен (не какой-нибудь однострочный оператор), и повторять его 2 раза (с конст и без) никак не хорошо.

Спасибо
Менять содержимое объекта класса MyItem, не оповещая объект класса MyClass,  не хорошо с точки зрения ООП.
Записан
andrew.k
Гость
« Ответ #7 : Сентябрь 21, 2012, 18:39 »

Igors:
То, что вы привели - есть const propagation (распространиение константности), когда имея конст. ссылку мы можем получить только конст. на другие объекты. В противоположность этому
Код:
MyItem * FindItem() const;
прерывает константность, т.е. имея конст. ссылку мы можем получить неконст. ссылку на другие объекты. Очевидно, const propagation делает код более строгим.
Вообще там такого метода нет.
В примера константный метод возвращает константный указатель, а неконстантный соответственно неконст.
Так что все по-честному.

А по теме. Я думаю, приведенный пример это не очень удачное проектирование.
Сама по себе операция поиск она константная (чисто логически), поэтому метод findItem() должен быть только const.
И видимо должен возвращать что-то типа индекса, для доступа к данным в этом классе.
А уже имея индекс мы должны иметь возможность получить оба варианта указателя.
Второй момент вызывая неконстантный findItem я не могу быть уверен(если не я писал этот класс конечно), что объект сохранится неизменным, даже если я не стану менять полученный элемент.
Может там есть какой-нибудь счетчик доступа или еще что-нибудь, какие-нибудь механизмы включаются.

А с volatille не лучше выходит? (не пробовал)
А с volatile мне кажется не получится либо получится полная ерунда (это если volatile ставить на сами данные).
Что можно попробовать это сохранять внутри класса этот самый индекс, о котором я выше писал.
А уже из самих функций findItem вызывать соответствующий метод getItem или getConstItem получая нужный указатель.
Но чем такие игры лучше чем, const_cast я не вижу.
« Последнее редактирование: Сентябрь 21, 2012, 18:57 от andrew.k » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Сентябрь 21, 2012, 19:00 »

Не то чтобы эта ситуация очень частая, но и какой-то редкой/экзотической ее не назовешь. Возвращение указателя на константу нужно по понятным причинам, напр для пользования классом извне. С др стороны вот я делаю какую-то содержательную работу - возможно методом того же MyClass. Да, изменяю объект - не скрываю, так что, мне FindItem не использовать? Приходится рисовать как-то так
Код
C++ (Qt)
MyItem * MyClass::FindItem( ... )
{
const MyItem * itm = FindItem(..);
return const_cast <MyItem *> (itm);
}
 
Есть лучшие предложения?
Записан
andrew.k
Гость
« Ответ #9 : Сентябрь 21, 2012, 19:03 »

То что я описал про volatile примерно так. Может как-то украсить можно, убрать дублирование. Но я лишь идею с volatile показать хотел.

Код
C++ (Qt)
struct Data
{
   int a;
   int b;
};
 
class Test
{
public:
   Data * findA(int A)
   {
       int index = findAImpl(A);
       return getA(index);
   }
 
   const Data * findA(int A) const
   {
       int index = findAImpl(A);
       return getA(index);
   }
 
   Data *getA(int index)
   {
       if(index > 0 && index < data.length())
           return data[index];
       else
           return 0;
   }
 
   const Data *getA(int index) const
   {
       if(index > 0 && index < data.length())
           return data[index];
       else
           return 0;
   }
 
private:
   QList<Data *>data;
 
   int findAImpl(int A) const
   {
       for (int i = 0; i < data.size(); ++i)
           if(data[i]->a == A)
               return i;
       return -1;
   }
 
   volatile int index_;
};
Записан
DmitryM
Гость
« Ответ #10 : Сентябрь 21, 2012, 19:58 »

Если бы я писал библиотеку и столкнулся бы с такой ситуацией, то возвращал бы не ссылку на объект, а прокси объект.
Никого же не удивляет стандартный begin()
Код
C++ (Qt)
iterator begin ();
const_iterator begin () const;
 
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Сентябрь 21, 2012, 20:15 »

Но я лишь идею с volatile показать хотел.

Код
C++ (Qt)
struct Data
..
   volatile int index_;
};
Цитировать
- Поручик, но зачем же они яйца закапывают?
- Дикари-с
Улыбающийся Чего ж Вы volatile к индексу прилепили? Здесь он никакого отношения к константности не имеет.
Вариант с индексом понятен, но требует ощутимых переделок. Напр я вставляю новые элементы в тот же QList, адрес элемента у меня неизменный, а вот индекс "уплывает". Индекс может быть сложен (напр для дерева) да и не уверен в большом счастье обладания индексом - да, компилятору рот закрыли, но теперь самому решать когда же он валиден.

Если бы я писал библиотеку и столкнулся бы с такой ситуацией, то возвращал бы не ссылку на объект, а прокси объект.
Никого же не удивляет стандартный begin()
Код
C++ (Qt)
iterator begin ();
const_iterator begin () const;
 
Называется "сменял шило на мыло", теперь какой итератор вернуть - константный или нет
Записан
andrew.k
Гость
« Ответ #12 : Сентябрь 21, 2012, 23:09 »

Чего ж Вы volatile к индексу прилепили? Здесь он никакого отношения к константности не имеет.
Да. поторопился. Я хотел сохранять найденный индекс в index_, поэтому поставил volatile.
Но это даже не пригодилось.
Записан
andrew.k
Гость
« Ответ #13 : Сентябрь 21, 2012, 23:43 »

Вся соль в том, чтобы отделить мухи от котлет: сам механизм поиска (сложный метод, который должен быть константным) и возврат указывающей сущности (указель, ссылка или итератор) - это пару строк, тут видимо будет дублирование кода (как в моем примере).
И я не понял насчет уплывания индекса. Ну и пусть уплывает.
Этот индекс предназначен для внутреннего использования и актуален только на момент поиска.
Можно методы getA сделать приватными.
Пользователь класса все равно в результате получает искомый указатель "требуемой константности" Улыбающийся

Боже. Вот меня заглючило. Говоря volatile я имел в виду mutable. Улыбающийся
« Последнее редактирование: Сентябрь 22, 2012, 00:01 от andrew.k » Записан
andrew.k
Гость
« Ответ #14 : Сентябрь 22, 2012, 00:08 »

А вот вообще без заморочек:
Код
C++ (Qt)
class Test
{
public:
   Data * findA(int A)
   {
       return findAImpl(A);
   }
 
   const Data * findA(int A) const
   {
       return findAImpl(A);
   }
 
private:
   QList<Data *>data;
 
   Data * findAImpl(int A) const
   {
       for (int i = 0; i < data.size(); ++i)
           if(data[i]->a == A)
               return data[i];
       return 0;
   }
};
Подмигивающий
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


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