LINUX.ORG.RU

Type casting в c++.

 


0

2

Сегодня столкнулся с тем, что для моего проекта на плюсах просто необходимо преобразование типов как вверх, так и вниз (большая часть объектов хранится в одном stl контейнере, хранящем указатели имеющие тип интерфейса, от которого наследуется добрая половина всего, без кастования типов пришлось бы переносить некоторый функционал в сам интерфейс, а это еще больший говнокод)

Допустим, у нас есть такой код.

// Предположим,что класс Foo абстрактный                    
// (иначе downcast попросту невозможен)

class Foo {
   // какая-то переменная
   SomeType something;
   // что-то еще
public:
   // меняем вышеназванную переменную
   virtual void setSomething(SomeType newSomething);
   // что-то еще
};

class Bar : public Foo{
   // что-то
public:
   void setSomething(SomeType newSomething) override;
   // получаем значение 
   SomeType getSomething();
};

int main(){
   // создаем указатель, выделяем память и задаем параметр
   Bar * somepointer_a = new bar;
   somepointer_a->setSomething(SomeType alpha);
   //Создаем новый указатель с типом родительского класса через \
   //кастование типов
   Foo * somepointer_b = dynamic_cast<Foo*>(someponter_a);
   //Используем  функцию, которая была оверрайднута
   somepointer_b->setSomething(SomeType beta);
   //Создаем указатель изначального типа и получаем значение
   Bar * somepointer_c = dynamic_cast<Bar*>(someponter_b);
   std::cout << somepointer_c->getSomething();
}

Есть ли вероятность, что на stdout выведет не то, что было в beta, а то что было в alpha или вообще вылетит с ошибкой? А если еще конктретнее, можно ли это считать неопределенным поведением и способна ли последовательность из upcast и downcast привести к потере данных?



Последнее исправление: le_poisson (всего исправлений: 5)
Ответ на: комментарий от alysnix

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

  1. Изолированные типы, не связаны ни в какие иерархии иине знающие ничего о других
  2. Отсутствие явных кастов к конкретным типам в месте вызова
  3. Можно налагать любые требования на интерфейс объектов в месте вызова.

Замена виртуальному вызову выглядит так

std::visit([](auto&& obj){ obj.foo(); }, obj);

Про то что это тот же самый тег типа внутри класса, это вообще фейспалм.

Что-то в моем варианте нет никаких идиотских тегов внутри классов и идиотских методов as_B() as_C() as_D(), интерфейс каждого класса описывает ровно то что относится к этому классу

CatsCantFly
()
Последнее исправление: CatsCantFly (всего исправлений: 4)
Ответ на: комментарий от CatsCantFly

Изолированные типы, не связаны ни в какие иерархии иине знающие ничего о других

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

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

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

Отсутствие явных кастов к конкретным типам в месте вызова

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

вы собственно написали своим вариантом

struct {
  int tag_;
  union{
    T1 t1_;
    T2 t2_;
    T3 t3_;
  };
};

и три функции визитора, которые будут выбираться свичом, по тегу с реинтерпрет кастом на начало union. замотали это в шаблон и назвали правильным ООП? Это в си так делают, где ООП нет.

Можно налагать любые требования на интерфейс объектов в месте вызова.

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

ваш типа способ еще слаб и тем, что

  1. нельзя написать уже готовое обобщенное хранилище под расширяемую иерархию классов(впрочем вы ее отрицаете похоже). при каждом расширении придется переинстанцировать шаблон.

  2. хранилища такого рода будут друг с другом несовместимы, то есть визиторы для storage <A,B> будут несовместимы с визиторами для storage <A,B,C>. то есть вы порождаете лапшу из классов на все случаи жизни, и число инстанцированных сущностей растет пропорционально числу хранилищ * число хранимых классов.

  3. у вас неправильный визитор для Foo. он там зовет и принт и скан одновременно. так не бывает для принтеров со сканерами.

ps. что что будет если я напложу 100 наследников класса принтер, под разные модели принтеров, и 50 разных сканеров, и попытаюсь их положить в ваше хранилише?

pss. явного реинтерпрет каста там не будет, в силу волшебных свойств union.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 4)
Ответ на: комментарий от alysnix

Касты исчезли из пользовательского кода в код внутренностей стандартной библиотеки и инстанцирования шаблонов == кастов в пользовательском прикладном коде нет. Соответственно, он не ломается от расширения числа обобщаемых классов, в отличие от портянки if-ов с динамик кастом к каждому конкретному типу

  • При каждом расширении придется дописать к Object еще один тип. Вот и все накладные расходы (учитывая, что мы говорим все еще о классах, которые нельзя обобщить общим интерфейсом - вы же полезете добавлять еще одну проверку в простыню динамик кастов)
  • Все совместимо, функция в визиторе принимает любой тип
  • Если бы я хотел, я бы написал там else. Причем тут подход?
CatsCantFly
()
Последнее исправление: CatsCantFly (всего исправлений: 1)
Ответ на: комментарий от CatsCantFly

Соответственно, он не ломается от расширения числа обобщаемых классов, в отличие от портянки if-ов с динамик кастом к каждому конкретному типу

какой еще портянки if?? в данном случае (с принтерами-сканерами) будет три каста - от базового класса, к сканеру, принтеру и принт-сканеру, после чего будут вызываться виртуальные функции принт/скан. и фсе. хранилище не будет расширяться никак, его можно написать раз и забыть навсегда.

любое добавление класса будет локальным - надо будет написать класс наследник от сканера/принтера/принтсканера, и виртуальную функцию…или виртуальные функции тоже отменили??? и создав обьект данного класса пихнуть его в хранилище.

у вас же все 150 сканеров-принтеров надо явно обьявить в одном месте - в инстанцировании шаблона варианта. причем декларации всех 150 типов должны быть видны.

причем вся изюмина такой «шаблонизации» в наличии юниона в си. тут даже с++ не причем.

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

и потом вопрос вообще не в хранении «независимых типов»! вопрос в написании ясночитаемого кода, когда вы получаете обьект по некоей базе, а писать у базы кучу виртуальных методов - не совсем хорошо. и вам надо попользоваться ВСЕМИ свойствами данного обьекта и делать это легко и приятно, без написания каких-то визиторов. и делать это можно только преобразовав обьект к актуальному типу и делая с ним все что хочешь.

у вас же просто шаблон для скана всего хранилища и вызова однотипных функций на обьектах. то есть задача вообще не та.

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

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

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 5)

По условиям задачки объекты находятся в одном контейнере?

FlexBox
()

visitor слишком сложно штоле?

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.